
    )j$%                     L   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 ddl	m
Z
 ddlmZ ddlmZmZmZmZ  ej        e          Z e            dz  Zdefd	Zdefd
ZdefdZdeddfdZdefdZdefdZ ej                    ZddededefdZ  G d de          Z!dS )a  Singularity/Apptainer persistent container environment.

Security-hardened with --containall, --no-home, capability dropping.
Supports configurable resource limits and optional filesystem persistence
via writable overlay directories that survive across sessions.
    N)Path)Optional)get_hermes_home)BaseEnvironment_load_json_store_popen_bash_save_json_storezsingularity_snapshots.jsonreturnc                  x    t          j        d          rdS t          j        d          rdS t          d          )z/Locate the apptainer or singularity CLI binary.	apptainersingularityzNeither 'apptainer' nor 'singularity' was found in PATH. Install Apptainer (https://apptainer.org/docs/admin/main/installation.html) or Singularity and ensure the CLI is available.)shutilwhichRuntimeError     C/home/ubuntu/.hermes/hermes-agent/tools/environments/singularity.py_find_singularity_executabler      sG    |K   {|M"" }
	:  r   c                     t                      } 	 t          j        | dgdddt          j                  }nB# t          $ r t          d|  d          t          j        $ r t          d|  d          w xY w|j        d	k    r>|j        	                                d
d         }t          d|  d|j         d|           | S )z?Preflight check: resolve the executable and verify it responds.versionT
   capture_outputtexttimeoutstdinz"Singularity backend selected but 'z' could not be executed.'z version' timed out.r   N   z version' failed (exit code z): )
r   
subprocessrunDEVNULLFileNotFoundErrorr   TimeoutExpired
returncodestderrstrip)exeresultr%   s      r   _ensure_singularity_availabler)   +   s   
&
(
(C
:)Tb$
 
 
  
 
 
NNNN
 
 	
 $ : : :8s888999: A$$&&tt,^s^^@Q^^V\^^___Js	   %6 ?A5c                  *    t          t                    S N)r   _SNAPSHOT_STOREr   r   r   _load_snapshotsr-   @   s    O,,,r   datac                 0    t          t          |            d S r+   )r	   r,   )r.   s    r   _save_snapshotsr0   D   s    _d+++++r   c                     t          j        d          } | r(t          |           }|                    dd           |S ddlm}  |            dz  }t          d          }|                                rnt          j        |t           j                  rO|t          j        dd	          z  d
z  }|                    dd           t          
                    d|           |S |                    dd           |S )NTERMINAL_SCRATCH_DIRTparentsexist_okr   )get_sandbox_dirr   z/scratchUSERhermeszhermes-agentz Using /scratch for sandboxes: %s)osgetenvr   mkdirtools.environments.baser6   existsaccessW_OKloggerinfo)custom_scratchscratch_pathr6   sandboxscratchuser_scratchs         r   _get_scratch_dirrG   H   s   Y566N N++4$777777777o-/G:G~~ BIgrw77 68!<!<<~M4$7776EEEMM$M...Nr   c                      t          j        d          } | r(t          |           }|                    dd           |S t	                      }|dz  }|                    dd           |S )NAPPTAINER_CACHEDIRTr3   z
.apptainer)r9   r:   r   r;   rG   )	cache_dir
cache_pathrE   s      r   _get_apptainer_cache_dirrL   ]   sx    	.//I )__
555  G<'JTD111r   r   image
executablec           	      >   |                      d          r#t          |                                           r| S |                     d          s| S |                     dd                              dd                              dd          }t                      }|| dz  }|                                rt          |          S t          5  |                                rt          |          cd d d            S t          	                    d           t          	                    d|            t          	                    d	|           |d
z  }|
                    dd           t          j                                        }t          |          |d<   t          |          |d<   	 t          j        |dt          |          | gddd|t          j                  }|j        dk    rPt                              d           t                              d|j        d d                    | cd d d            S t          	                    d           t          |          cd d d            S # t          j        $ rS t                              d           |                                r|                                 | cY cd d d            S t,          $ r3}t                              d|           | cY d }~cd d d            S d }~ww xY w# 1 swxY w Y   d S )Nz.sifz	docker:// /-:z&Building SIF image (one-time setup)...z  Source: %sz  Target: %stmpTr3   APPTAINER_TMPDIRrI   buildiX  )r   r   r   envr   r   z/SIF build failed, falling back to docker:// URLz  Error: %si  zSIF image built successfullyz2SIF build timed out, falling back to docker:// URLz2SIF build error: %s, falling back to docker:// URL)endswithr   r=   
startswithreplacerL   str_sif_build_lockr@   rA   r;   r9   environcopyr   r    r!   r$   warningr%   r#   unlink	Exception)	rM   rN   
image_namerJ   sif_pathtmp_dirrW   r(   es	            r   _get_or_build_sifrf   l   s   ~~f $u++"4"4"6"6 K(( {B//77SAAII#sSSJ(**Ij....H 8}}	 " "?? 	!x==" " " " " " " " 	<===NE***NH---e#dT222joo"%g,,$'	NN !	^Wc(mmU;#$ (  F
  A%%PQQQ}fmDSD.ABBB1" " " " " " " "2 KK6777x==5" " " " " " " "6 ( 	 	 	NNOPPP   "!!!LL?" " " " " " " "@  	 	 	NNOQRSSSLLLLLE" " " " " " " "@	A" " " " " " " " " "sW   #L>B.L-BI2=(I22ALL	LL
8L9L
LLLLc                        e Zd ZdZ	 	 	 	 	 	 	 ddeded	ed
ededededef fdZ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 )SingularityEnvironmenta  Hardened Singularity/Apptainer container with resource limits and persistence.

    Spawn-per-call: every execute() spawns a fresh ``apptainer exec ... bash -c`` process.
    Session snapshot preserves env vars across calls.
    CWD persists via in-band stdout markers.
    ~<   r   FdefaultrM   cwdr   cpumemorydiskpersistent_filesystemtask_idc	                 L   t                                          ||           t                      | _        t	          || j                  | _        dt          j                    j        d d          | _	        d| _
        || _        || _        d | _        || _        || _        | j        rQt!                      dz  }	|	                    dd           |	d| z  | _        | j                            dd           |                                  |                                  d S )	N)rl   r   hermes_   Fzhermes-overlaysTr3   zoverlay-)super__init__r)   rN   rf   rM   uuiduuid4hexinstance_id_instance_started_persistent_task_id_overlay_dir_cpu_memoryrG   r;   _start_instanceinit_session)selfrM   rl   r   rm   rn   ro   rp   rq   overlay_base	__class__s             r   rv   zSingularityEnvironment.__init__   s#    	S'222799&udo>>
<TZ\\%5crc%:<<!&0,0	 	A+--0AALtd;;; ,/C'/C/C CD##D4#@@@r   c                 t   | j         ddg}|                    ddg           | j        r1| j        r*|                    dt	          | j                  g           n|                    d           	 ddlm}m}  |            D ]+}|                    d	|d
          d|d          dg           , |            D ]+}|                    d	|d
          d|d          dg           ,n2# t          $ r%}t                              d|           Y d }~nd }~ww xY w| j        dk    r|                    d| j         dg           | j        dk    r)|                    dt	          | j                  g           |                    t	          | j                  | j        g           	 t!          j        |dddt           j                  }|j        dk    rt)          d|j                   d| _        t                              d| j        | j                   d S # t           j        $ r t)          d          w xY w)Ninstancestartz--containallz	--no-homez	--overlayz--writable-tmpfsr   )get_credential_file_mountsget_skills_directory_mountz--bind	host_pathrS   container_pathz:roz8Singularity: could not load credential/skills mounts: %sz--memoryMz--cpusTx   r   zFailed to start instance: z/Singularity instance %s started (persistent=%s)zInstance start timed out)rN   extendr|   r~   r[   appendtools.credential_filesr   r   ra   r@   debugr   r   rM   rz   r   r    r!   r$   r   r%   r{   rA   r#   )r   cmdr   r   mount_entryskills_mountre   r(   s           r   r   z&SingularityEnvironment._start_instance   s   
G4

NK0111 	+ 1 	+JJS):%;%;<====JJ)***	Xeeeeeeee99;; i i

H[)A&f&fKP`Da&f&f&fghhhh : : < < k k

Hk)B&h&h\RbEc&h&h&hijjjjk 	X 	X 	XLLSUVWWWWWWWW	X <!JJ
t|$6$6$678889q==JJ#di..1222

C
OOT%56777	;^C4QT\f\noooF A%%"#O#O#OPPP%)D"KKI($*:< < < < <( 	; 	; 	;9:::	;s&   0A4C% %
D/DD$A2H H7r   N)loginr   
stdin_data
cmd_stringr   r   r
   c                    | j         st          d          | j        dd| j         g}|r|                    ddd|g           n|                    dd|g           t          ||          S )z5Spawn a bash process inside the Singularity instance.z Singularity instance not startedexeczinstance://bashz-lz-c)r{   r   rN   rz   r   r   )r   r   r   r   r   r   s         r   	_run_bashz SingularityEnvironment._run_bash   s     % 	CABBB/T-//1 	3JJdJ78888JJj12223
+++r   c                    | j         r	 t          j        | j        dd| j        gdddt          j                   t                              d| j                   n8# t          $ r+}t          	                    d| j        |           Y d}~nd}~ww xY wd	| _         | j
        rB| j        r=t                      }t          | j                  || j        <   t          |           dS dS dS )
z;Stop the instance. If persistent, the overlay dir survives.r   stopT   r   zSingularity instance %s stoppedz*Failed to stop Singularity instance %s: %sNF)r{   r   r    rN   rz   r!   r@   rA   ra   r_   r|   r~   r-   r[   r}   r0   )r   re   	snapshotss      r   cleanupzSingularityEnvironment.cleanup   s   ! 
	+b_j&$:JK#'dB$,   
 =t?OPPPP b b bKTM]_`aaaaaaaab%*D" 	' 1 	''))I'*4+<'='=Idm$I&&&&&	' 	' 	' 	's   AA 
B%!BB)ri   rj   r   r   r   Frk   )__name__
__module____qualname____doc__r[   intfloatboolrv   r   r   Popenr   r   __classcell__)r   s   @r   rh   rh      s'         &+    	
     $      >!; !; !;F ;@!$+/, , ,C ,4 ,,!Dj,4>4D, , , , ' ' ' ' ' ' 'r   rh   )r   )"r   loggingr9   r   r   	threadingrw   pathlibr   typingr   hermes_constantsr   r<   r   r   r   r	   	getLoggerr   r@   r,   r[   r   r)   dictr-   r0   rG   rL   Lockr\   rf   rh   r   r   r   <module>r      s     				                       , , , , , ,            
	8	$	$!/##&BB
c 
 
 
 
s    *- - - - -,$ ,4 , , , ,$    *	$ 	 	 	 	 !).""/ /S /c /C / / / /dk' k' k' k' k'_ k' k' k' k' k'r   