§
    ‰eîi/  ã                   ó¾   — d Z ddlZddlZddlZddlZddlZddlZddlZddlm	Z	 ddl
mZmZ ddlmZmZmZmZmZ  ej        e¦  «        Zd
d„Z G d„ d	e¦  «        ZdS )zKSSH remote execution environment with ControlMaster connection persistence.é    N)ÚPath)ÚBaseEnvironmentÚ_popen_bash)ÚFileSyncManagerÚiter_sync_filesÚquoted_mkdir_commandÚquoted_rm_commandÚunique_parent_dirsÚreturnc                  óL   — t          j        d¦  «        st          d¦  «        ‚dS )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ÚRuntimeError© ó    ú8/home/div/.hermes/hermes-agent/tools/environments/ssh.pyÚ_ensure_ssh_availabler      s3   € åŒ<˜ÑÔð 
ÝØeñ
ô 
ð 	
ð
ð 
r   c                   ó$  ‡ — e Zd ZdZ	 	 d&dededed	ed
edefˆ fd„Zd'dedz  defd„Zd„ Z	defd„Z
d(d„Zdededdfd„Zdeeeef                  ddfd„Zdeddfd„Zdee         ddfd„Zd(d„Zdddd œd!ed"ed	ed#edz  dej        f
d$„Zd%„ Zˆ xZS ))ÚSSHEnvironmenta  Run commands on a remote machine over SSH.

    Spawn-per-call: every execute() spawns a fresh ``ssh ... bash -c`` process.
    Session snapshot preserves env vars across calls.
    CWD persists via in-band stdout markers.
    Uses SSH ControlMaster for connection reuse.
    Ú~é<   é   Ú ÚhostÚuserÚcwdÚtimeoutÚportÚkey_pathc                 ó   •‡ — t          ¦   «                              ||¬¦  «         |‰ _        |‰ _        |‰ _        |‰ _        t          t          j        ¦   «         ¦  «        dz  ‰ _	        ‰ j	         
                    dd¬¦  «         t          j        |› d|› d|› �                     ¦   «         ¦  «                             ¦   «         d d…         }‰ j	        |› d�z  ‰ _        t!          ¦   «          ‰                      ¦   «          ‰                      ¦   «         ‰ _        ‰                      ¦   «          t+          ˆ fd	„‰ j        ‰ j        ‰ j        ‰ j        ¬
¦  «        ‰ _        ‰ j                             d¬¦  «         ‰                      ¦   «          d S )N)r   r   z
hermes-sshT)ÚparentsÚexist_okú@ú:é   z.sockc                  ó2   •— t          ‰ j        › d�¦  «        S )Nú/.hermes)r   Ú_remote_home©Úselfs   €r   ú<lambda>z)SSHEnvironment.__init__.<locals>.<lambda>E   s   ø€ ¥°DÔ4EÐ1OÐ1OÐ1OÑ!PÔ!P€ r   )Úget_files_fnÚ	upload_fnÚ	delete_fnÚbulk_upload_fnÚbulk_download_fn)Úforce)ÚsuperÚ__init__r   r   r   r    r   ÚtempfileÚ
gettempdirÚcontrol_dirÚmkdirÚhashlibÚsha256ÚencodeÚ	hexdigestÚcontrol_socketr   Ú_establish_connectionÚ_detect_remote_homer)   Ú_ensure_remote_dirsr   Ú_scp_uploadÚ_ssh_deleteÚ_ssh_bulk_uploadÚ_ssh_bulk_downloadÚ_sync_managerÚsyncÚinit_session)	r+   r   r   r   r   r   r    Ú
_socket_idÚ	__class__s	   `       €r   r4   zSSHEnvironment.__init__)   s‘  øø€ å‰Œ×Ò˜S¨'ÐÑ2Ô2Ð2ØˆŒ	ØˆŒ	ØˆŒ	Ø ˆŒå¥Ô 3Ñ 5Ô 5Ñ6Ô6¸ÑEˆÔØÔ×Ò t°dÐÑ;Ô;Ð;õ ”^ØÐ#Ð#�dÐ#Ð#˜TÐ#Ð#×*Ò*Ñ,Ô,ñ
ô 
ç
Š)‰+Œ+�c�r�côˆ
ð #Ô.°JÐ1EÐ1EÐ1EÑEˆÔÝÑÔÐØ×"Ò"Ñ$Ô$Ð$Ø ×4Ò4Ñ6Ô6ˆÔà× Ò Ñ"Ô"Ð"Ý,ØPÐPÐPÐPØÔ&ØÔ&ØÔ0Ø!Ô4ð
ñ 
ô 
ˆÔð 	Ô×Ò dÐÑ+Ô+Ð+à×ÒÑÔÐÐÐr   NÚ
extra_argsr   c                 óT  — dg}|                      dd| j        › �g¦  «         |                      ddg¦  «         |                      ddg¦  «         |                      ddg¦  «         |                      ddg¦  «         |                      ddg¦  «         | j        d	k    r)|                      d
t          | j        ¦  «        g¦  «         | j        r|                      d| j        g¦  «         |r|                      |¦  «         |                     | j        › d| j        › �¦  «         |S )Nr   ú-oúControlPath=zControlMaster=autozControlPersist=300zBatchMode=yesz StrictHostKeyChecking=accept-newzConnectTimeout=10r   z-pú-ir$   )Úextendr=   r   Ústrr    Úappendr   r   )r+   rJ   Úcmds      r   Ú_build_ssh_commandz!SSHEnvironment._build_ssh_commandO   s3  € ØˆgˆØ�
Š
�DÐ>¨Ô)<Ð>Ð>Ð?Ñ@Ô@Ð@Ø�
Š
�DÐ.Ð/Ñ0Ô0Ð0Ø�
Š
�DÐ.Ð/Ñ0Ô0Ð0Ø�
Š
�D˜/Ð*Ñ+Ô+Ð+Ø�
Š
�DÐ<Ð=Ñ>Ô>Ð>Ø�
Š
�DÐ-Ð.Ñ/Ô/Ð/ØŒ9˜Š?ˆ?Ø�JŠJ˜�c $¤)™nœnÐ-Ñ.Ô.Ð.ØŒ=ð 	.Ø�JŠJ˜˜dœmÐ,Ñ-Ô-Ð-Øð 	#Ø�JŠJ�zÑ"Ô"Ð"Ø�
Š
�d”iÐ-Ð- $¤)Ð-Ð-Ñ.Ô.Ð.Øˆ
r   c                 óŽ  — |                       ¦   «         }|                     d¦  «         	 t          j        |ddd¬¦  «        }|j        dk    rD|j                             ¦   «         p|j                             ¦   «         }t          d|› �¦  «        ‚d S # t          j	        $ r! t          d| j
        › d| j        › d	�¦  «        ‚w xY w)
Nz!echo 'SSH connection established'Té   ©Úcapture_outputÚtextr   r   zSSH connection failed: zSSH connection to r$   z
 timed out)rS   rQ   Ú
subprocessÚrunÚ
returncodeÚstderrÚstripÚstdoutr   ÚTimeoutExpiredr   r   )r+   rR   ÚresultÚ	error_msgs       r   r>   z$SSHEnvironment._establish_connection`   sâ   € Ø×%Ò%Ñ'Ô'ˆØ�
Š
Ð6Ñ7Ô7Ð7ð	WÝ”^ C¸À4ÐQSÐTÑTÔTˆFØÔ  AÒ%Ð%Ø"œM×/Ò/Ñ1Ô1ÐJ°V´]×5HÒ5HÑ5JÔ5J�	Ý"Ð#H¸YÐ#HÐ#HÑIÔIÐIð &Ð%øõ Ô(ð 	Wð 	Wð 	WÝÐU°D´IÐUÐUÀÄ	ÐUÐUÐUÑVÔVÐVð	Wøøøs   «A'B Â0Cc                 ó\  — 	 |                       ¦   «         }|                     d¦  «         t          j        |ddd¬¦  «        }|j                             ¦   «         }|r(|j        dk    rt                               d|¦  «         |S n# t          $ r Y nw xY w| j
        dk    rdS d	| j
        › �S )
z(Detect the remote user's home directory.z
echo $HOMETé
   rV   r   zSSH: remote home = %sÚrootz/rootz/home/)rS   rQ   rY   rZ   r^   r]   r[   ÚloggerÚdebugÚ	Exceptionr   )r+   rR   r`   Úhomes       r   r?   z"SSHEnvironment._detect_remote_homek   sÈ   € ð		Ø×)Ò)Ñ+Ô+ˆCØ�JŠJ�|Ñ$Ô$Ð$Ý”^ C¸À4ÐQSÐTÑTÔTˆFØ”=×&Ò&Ñ(Ô(ˆDØð ˜Ô)¨QÒ.Ð.Ý—’Ð4°dÑ;Ô;Ð;Ø�øøÝð 	ð 	ð 	ØˆDð	øøøàŒ9˜ÒÐØ�7Ø#˜œ	Ð#Ð#Ð#s   ‚BB Â
BÂBc                 óÔ   — | j         › d�}||› d�|› d�|› d�g}|                      ¦   «         }|                     t          |¦  «        ¦  «         t	          j        |ddd¬¦  «         dS )	z?Create base ~/.hermes directory tree on remote in one SSH call.r(   z/skillsz/credentialsz/cacheTrc   rV   N)r)   rS   rQ   r   rY   rZ   )r+   ÚbaseÚdirsrR   s       r   r@   z"SSHEnvironment._ensure_remote_dirs   s„   € àÔ#Ð-Ð-Ð-ˆØ˜Ð&Ð&Ð&¨4Ð(=Ð(=Ð(=À$¸¸¸ÐOˆØ×%Ò%Ñ'Ô'ˆØ�
Š
Õ'¨Ñ-Ô-Ñ.Ô.Ð.ÝŒ�s¨4°dÀBÐGÑGÔGÐGÐGÐGr   Ú	host_pathÚremote_pathc                 ó¦  — t          t          |¦  «        j        ¦  «        }|                      ¦   «         }|                     dt          j        |¦  «        › �¦  «         t          j        |ddd¬¦  «         ddd| j	        › �g}| j
        dk    r)|                     d	t          | j
        ¦  «        g¦  «         | j        r|                     d
| j        g¦  «         |                     || j        › d| j        › d|› �g¦  «         t          j        |ddd¬¦  «        }|j        dk    r)t!          d|j                             ¦   «         › �¦  «        ‚dS )z0Upload a single file via scp over ControlMaster.z	mkdir -p Trc   rV   ÚscprL   rM   r   z-PrN   r$   r%   é   r   zscp failed: N)rP   r   ÚparentrS   rQ   ÚshlexÚquoterY   rZ   r=   r   rO   r    r   r   r[   r   r\   r]   )r+   rl   rm   rq   Ú	mkdir_cmdÚscp_cmdr`   s          r   rA   zSSHEnvironment._scp_upload‰   sU  € å•T˜+Ñ&Ô&Ô-Ñ.Ô.ˆØ×+Ò+Ñ-Ô-ˆ	Ø×ÒÐ:¥U¤[°Ñ%8Ô%8Ð:Ð:Ñ;Ô;Ð;ÝŒ�y°¸DÈ"ÐMÑMÔMÐMà˜$Ð D¨tÔ/BÐ DÐ DÐEˆØŒ9˜Š?ˆ?Ø�NŠN˜D¥# d¤i¡.¤.Ð1Ñ2Ô2Ð2ØŒ=ð 	2Ø�NŠN˜D $¤-Ð0Ñ1Ô1Ð1Ø�Š˜	 d¤iÐ#KÐ#K°$´)Ð#KÐ#K¸kÐ#KÐ#KÐLÑMÔMÐMÝ” ¸À4ÐQSÐTÑTÔTˆØÔ Ò!Ð!ÝÐE¨f¬m×.AÒ.AÑ.CÔ.CÐEÐEÑFÔFÐFð "Ð!r   Úfilesc           	      óÈ  — |sdS t          |¦  «        }|r‚|                      ¦   «         }|                     t          |¦  «        ¦  «         t	          j        |ddd¬¦  «        }|j        dk    r)t          d|j         	                    ¦   «         › �¦  «        ‚t          j        d¬¦  «        5 }|D ]�\  }}t          j                             ||                     d	¦  «        ¦  «        }t          j        t          j                             |¦  «        d¬
¦  «         t          j        t          j                             |¦  «        |¦  «         Œždddd|dg}	|                      ¦   «         }
|
                     d¦  «         t	          j        |	t          j        t          j        ¬¦  «        }	 t	          j        |
|j        t          j        t          j        ¬¦  «        }n7# t.          $ r* |                     ¦   «          |                     ¦   «          ‚ w xY w|j                             ¦   «          	 |                     d¬¦  «        \  }}d}|                     ¦   «         €|                     d¬¦  «        \  }}n"|j        r|j                             ¦   «         nd}nr# t          j        $ r` |                     ¦   «          |                     ¦   «          |                     ¦   «          |                     ¦   «          t          d¦  «        ‚w xY w|j        dk    r@t          d|j        › d|                     d¬¦  «         	                    ¦   «         › �¦  «        ‚|j        dk    r@t          d|j        › d|                     d¬¦  «         	                    ¦   «         › �¦  «        ‚	 ddd¦  «         n# 1 swxY w Y   t@           !                    dtE          |¦  «        ¦  «         dS )aÃ  Upload many files in a single tar-over-SSH stream.

        Pipes ``tar c`` on the local side through an SSH connection to
        ``tar x`` on the remote, transferring all files in one TCP stream
        instead of spawning a subprocess per file.  Directory creation is
        batched into a single ``mkdir -p`` call beforehand.

        Typical improvement: ~580 files goes from O(N) scp round-trips
        to a single streaming transfer.
        NTrp   rV   r   zremote mkdir failed: zhermes-ssh-bulk-)Úprefixú/)r#   Útarz-chfÚ-z-Cú.ztar xf - -C /)r^   r\   )Ústdinr^   r\   éx   )r   r   rc   zSSH bulk upload timed outztar create failed (rc=z): Úreplace©Úerrorsz tar extract over SSH failed (rc=z*SSH: bulk-uploaded %d file(s) via tar pipe)#r
   rS   rQ   r   rY   rZ   r[   r   r\   r]   r5   ÚTemporaryDirectoryÚosÚpathÚjoinÚlstripÚmakedirsÚdirnameÚsymlinkÚabspathÚPopenÚPIPEr^   rg   ÚkillÚwaitÚcloseÚcommunicateÚpollÚreadr_   Údecodere   rf   Úlen)r+   rv   r"   rR   r`   Ústagingrl   rm   ÚstagedÚtar_cmdÚssh_cmdÚtar_procÚssh_procÚ_Ú
ssh_stderrÚtar_stderr_raws                   r   rC   zSSHEnvironment._ssh_bulk_uploadš   s&  € ð ð 	ØˆFå$ UÑ+Ô+ˆØð 	TØ×)Ò)Ñ+Ô+ˆCØ�JŠJÕ+¨GÑ4Ô4Ñ5Ô5Ð5Ý”^ C¸À4ÐQSÐTÑTÔTˆFØÔ  AÒ%Ð%Ý"Ð#R¸6¼=×;NÒ;NÑ;PÔ;PÐ#RÐ#RÑSÔSÐSõ Ô(Ð0BÐCÑCÔCð 3	ÀwØ*/ð ?ð ?Ñ&�	˜;ÝœŸš g¨{×/AÒ/AÀ#Ñ/FÔ/FÑGÔG�Ý”�BœGŸOšO¨FÑ3Ô3¸dÐCÑCÔCÐCÝ”
�2œ7Ÿ?š?¨9Ñ5Ô5°vÑ>Ô>Ð>Ð>à˜f c¨4°¸#Ð>ˆGØ×-Ò-Ñ/Ô/ˆGØ�NŠN˜?Ñ+Ô+Ð+å!Ô'Ø¥
¤½
¼ðñ ô ˆHðÝ%Ô+Ø 8¤?½:¼?Ý%œ?ðñ ô ��øõ ð ð ð Ø—’‘”�Ø—’‘”�Øðøøøð ŒO×!Ò!Ñ#Ô#Ð#ð@Ø (× 4Ò 4¸SÐ 4Ñ AÔ A‘��:ð "%�Ø—=’=‘?”?Ð*Ø(0×(<Ò(<ÀRÐ(<Ñ(HÔ(HÑ%�A�~�~à?G¼Ð%W X¤_×%9Ò%9Ñ%;Ô%;Ð%;ÐTW�NøøÝÔ,ð @ð @ð @Ø—’‘”�Ø—’‘”�Ø—’‘”�Ø—’‘”�Ý"Ð#>Ñ?Ô?Ð?ð@øøøð Ô" aÒ'Ð'Ý"ðI¨XÔ-@ð Ið IØ%×,Ò,°IÐ,Ñ>Ô>×DÒDÑFÔFðIð Iñô ð ð Ô" aÒ'Ð'Ý"ðE°xÔ7Jð Eð EØ!×(Ò(°	Ð(Ñ:Ô:×@Ò@ÑBÔBðEð Eñô ð ð (ð_3	ð 3	ð 3	ñ 3	ô 3	ð 3	ð 3	ð 3	ð 3	ð 3	ð 3	øøøð 3	ð 3	ð 3	ð 3	õj 	�ŠÐAÅ3ÀuÁ:Ä:ÑNÔNÐNÐNÐNsF   Â-C=N/Æ+1GÇN/Ç4HÈN/È.A+JÊN/ÊA/L	Ì	BN/Î/N3Î6N3Údestc                 óà  — | j         › d�                     d¦  «        }|                      ¦   «         }|                     dt	          j        |¦  «        › �¦  «         t          |d¦  «        5 }t          j        ||t          j	        d¬¦  «        }ddd¦  «         n# 1 swxY w Y   |j
        dk    r=t          d	|j                             d
¬¦  «                             ¦   «         › �¦  «        ‚dS )z*Download remote .hermes/ as a tar archive.r(   ry   ztar cf - -C / Úwbr~   )r^   r\   r   Nr   zSSH bulk download failed: r   r€   )r)   r†   rS   rQ   rr   rs   ÚopenrY   rZ   rŒ   r[   r   r\   r“   r]   )r+   rž   Úrel_baser˜   Úfr`   s         r   rD   z!SSHEnvironment._ssh_bulk_downloadè   s3  € ð Ô'Ð1Ð1Ð1×8Ò8¸Ñ=Ô=ˆØ×)Ò)Ñ+Ô+ˆØ�ŠÐ?­¬°HÑ(=Ô(=Ð?Ð?Ñ@Ô@Ð@Ý�$˜ÑÔð 	\ Ý”^ G°A½j¼oÐWZÐ[Ñ[Ô[ˆFð	\ð 	\ð 	\ñ 	\ô 	\ð 	\ð 	\ð 	\ð 	\ð 	\ð 	\øøøð 	\ð 	\ð 	\ð 	\àÔ Ò!Ð!ÝÐl¸F¼M×<PÒ<PÐXaÐ<PÑ<bÔ<b×<hÒ<hÑ<jÔ<jÐlÐlÑmÔmÐmð "Ð!s   Á,#BÂBÂ"BÚremote_pathsc                 ó
  — |                       ¦   «         }|                     t          |¦  «        ¦  «         t          j        |ddd¬¦  «        }|j        dk    r)t          d|j                             ¦   «         › �¦  «        ‚dS )z*Batch-delete remote files in one SSH call.Trc   rV   r   zremote rm failed: N)	rS   rQ   r	   rY   rZ   r[   r   r\   r]   )r+   r¤   rR   r`   s       r   rB   zSSHEnvironment._ssh_deleteô   s�   € à×%Ò%Ñ'Ô'ˆØ�
Š
Õ$ \Ñ2Ô2Ñ3Ô3Ð3Ý” °D¸tÈRÐPÑPÔPˆØÔ Ò!Ð!ÝÐK°F´M×4GÒ4GÑ4IÔ4IÐKÐKÑLÔLÐLð "Ð!r   c                 ó8   — | j                              ¦   «          dS )zCSync files to remote via FileSyncManager (rate-limited internally).N)rE   rF   r*   s    r   Ú_before_executezSSHEnvironment._before_executeü   s   € àÔ×ÒÑ!Ô!Ð!Ð!Ð!r   Fr~   )Úloginr   Ú
stdin_dataÚ
cmd_stringr¨   r©   c                óú   — |                       ¦   «         }|r,|                     dddt          j        |¦  «        g¦  «         n*|                     ddt          j        |¦  «        g¦  «         t	          ||¦  «        S )z7Spawn an SSH process that runs bash on the remote host.Úbashz-lz-c)rS   rO   rr   rs   r   )r+   rª   r¨   r   r©   rR   s         r   Ú	_run_bashzSSHEnvironment._run_bash  sy   € ð ×%Ò%Ñ'Ô'ˆØð 	@Ø�JŠJ˜  d­E¬K¸
Ñ,CÔ,CÐDÑEÔEÐEÐEà�JŠJ˜ ¥e¤k°*Ñ&=Ô&=Ð>Ñ?Ô?Ð?å˜3 
Ñ+Ô+Ð+r   c                 ó®  — | j         r3t                               d¦  «         | j                              ¦   «          | j                             ¦   «         r�	 ddd| j        › �dd| j        › d| j        › �g}t          j	        |dd	¬
¦  «         n# t          t          j        f$ r Y nw xY w	 | j                             ¦   «          d S # t          $ r Y d S w xY wd S )Nz"SSH: syncing files from sandbox...r   rL   rM   z-OÚexitr$   Té   )rW   r   )rE   re   ÚinfoÚ	sync_backr=   Úexistsr   r   rY   rZ   ÚOSErrorÚSubprocessErrorÚunlink)r+   rR   s     r   ÚcleanupzSSHEnvironment.cleanup  s  € ØÔð 	+Ý�KŠKÐ<Ñ=Ô=Ð=ØÔ×(Ò(Ñ*Ô*Ð*àÔ×%Ò%Ñ'Ô'ð 
	ðØ˜dÐ$H°4Ô3FÐ$HÐ$HØ˜V¨¬	Ð%?Ð%?°D´IÐ%?Ð%?ðA�å”˜s°4ÀÐCÑCÔCÐCÐCøÝ�ZÔ7Ð8ð ð ð Ø�ðøøøðØÔ#×*Ò*Ñ,Ô,Ð,Ð,Ð,øÝð ð ð Ø��ðøøøð
	ð 
	s$   Á6B ÂB%Â$B%Â)C Ã
CÃC)r   r   r   r   )N©r   N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__rP   Úintr4   ÚlistrS   r>   r?   r@   rA   ÚtuplerC   r   rD   rB   r§   ÚboolrY   r‹   r­   r·   Ú__classcell__)rI   s   @r   r   r       s,  ø€ € € € € ðð ð 9<ØDFð$ð $˜Sð $¨ð $°#ð $Øð$Ø*-ð$Ø>Að$ð $ð $ð $ð $ð $ðLð ¨T°D©[ð ÀDð ð ð ð ð"	Wð 	Wð 	Wð$ Sð $ð $ð $ð $ð(Hð Hð Hð HðG Sð G°sð G¸tð Gð Gð Gð Gð"LO d¨5°°c°¬?Ô&;ð LOÀð LOð LOð LOð LOð\
n tð 
n°ð 
nð 
nð 
nð 
nðM¨¨S¬	ð M°dð Mð Mð Mð Mð"ð "ð "ð "ð ;@Ø!$Ø+/ð
,ð 
,ð 
, Cð 
,°4ð 
,Øð
,à! D™jð
,à4>Ô4Dð
,ð 
,ð 
,ð 
,ðð ð ð ð ð ð r   r   r¸   )r¼   r9   Úloggingrƒ   rr   r   rY   r5   Úpathlibr   Útools.environments.baser   r   Útools.environments.file_syncr   r   r   r	   r
   Ú	getLoggerr¹   re   r   r   r   r   r   ú<module>rÇ      s#  ðØ QÐ Qà €€€Ø €€€Ø 	€	€	€	Ø €€€Ø €€€Ø Ð Ð Ð Ø €€€Ø Ð Ð Ð Ð Ð à @Ð @Ð @Ð @Ð @Ð @Ð @Ð @ðð ð ð ð ð ð ð ð ð ð ð ð ð ð 
ˆÔ	˜8Ñ	$Ô	$€ð
ð 
ð 
ð 
ðð ð ð ð �_ñ ô ð ð ð r   