
    Pmj&                     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     D/home/thesage/.hermes/hermes-agent/tools/environments/singularity.py_find_singularity_executabler      sG    |K   {|M"" }
	:  r   c            	         t                      } 	 t          j        | dgdd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utf-8replace
   capture_outputtextencodingerrors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wW`jl$
 
 
  
 
 
NNNN
 
 	
 $ : : :8s888999: A$$&&tt,^s^^@Q^^V\^^___Js	   '8 ?A7c                  *    t          t                    S N)r   _SNAPSHOT_STOREr   r   r   _load_snapshotsr1   @   s    O,,,r   datac                 0    t          t          |            d S r/   )r	   r0   )r2   s    r   _save_snapshotsr4   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.baser:   existsaccessW_OKloggerinfo)custom_scratchscratch_pathr:   sandboxscratchuser_scratchs         r   _get_scratch_dirrK   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_CACHEDIRTr7   z
.apptainer)r=   r>   r   r?   rK   )	cache_dir
cache_pathrI   s      r   _get_apptainer_cache_dirrP   ]   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           ddlm}  |dd          }t          |          |d<   t          |          |d<   	 t          j        |dt          |          | gdd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tmpTr7   r   )build_subprocess_envF)scrub_secretsinherit_profile_homeAPPTAINER_TMPDIRrM   buildr   r   iX  )r   r   r   r   r   envr    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   rA   
startswithr   rP   str_sif_build_lockrD   rE   r?   tools.environments.localrY   r#   r$   r%   r(   warningr)   r'   unlink	Exception)
rQ   rR   
image_namerN   sif_pathtmp_dirrY   r^   r,   es
             r   _get_or_build_sifrk   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 	BAAAAA""USSS"%g,,$'	NN !	^Wc(mmU;#$\_eh (  F
  A%%PQQQ}fmDSD.ABBB7% % % % % % % %8 KK6777x==;% % % % % % % %< ( 	 	 	NNOPPP   "!!!LLE% % % % % % % %F  	 	 	NNOQRSSSLLLLLK% % % % % % % %F	G% % % % % % % % % %sW   #L	>B#L	"BI)4(I))AL<L	
	LL/L0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defaultrQ   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)rq   r   hermes_   Fzhermes-overlaysTr7   zoverlay-)super__init__r-   rR   rk   rQ   uuiduuid4hexinstance_id_instance_started_persistent_task_id_overlay_dir_cpu_memoryrK   r?   _start_instanceinit_session)selfrQ   rq   r   rr   rs   rt   ru   rv   overlay_base	__class__s             r   r{   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           	      x   | 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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_pathrW   container_pathz:roz8Singularity: could not load credential/skills mounts: %sz--memoryMz--cpusTr   r   x   r   zFailed to start instance: z/Singularity instance %s started (persistent=%s)zInstance start timed out)rR   extendr   r   ra   appendtools.credential_filesr   r   rf   rD   debugr   r   rQ   r   r#   r$   r%   r(   r   r)   r   rE   r'   )r   cmdr   r   mount_entryskills_mountrj   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4RYbkux  AK  AS  T  T  TF A%%"#O#O#OPPP%)D"KKI($*:< < < < <( 	; 	; 	;9:::	;s&   0A4C% %
D/DD$A4H H9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   rR   r   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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   stopTr   r      r   zSingularity instance %s stoppedz*Failed to stop Singularity instance %s: %sNF)r   r#   r$   rR   r   r%   rD   rE   rf   rd   r   r   r1   ra   r   r4   )r   rj   	snapshotss      r   cleanupzSingularityEnvironment.cleanup   s    ! 
	+b_j&$:JK#'dWY`b$,   
 =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)rn   ro   r   r   r   Frp   )__name__
__module____qualname____doc__ra   intfloatboolr{   r   r#   Popenr   r   __classcell__)r   s   @r   rm   rm      s'         &+    	
     $      >!; !; !;F ;@!$+/, , ,C ,4 ,,!Dj,4>4D, , , , ' ' ' ' ' ' 'r   rm   )r   )"r   loggingr=   r   r#   	threadingr|   pathlibr   typingr   hermes_constantsr   r@   r   r   r   r	   	getLoggerr   rD   r0   ra   r   r-   dictr1   r4   rK   rP   Lockrb   rk   rm   r   r   r   <module>r      s     				                       , , , , , ,            
	8	$	$!/##&BB
c 
 
 
 
s    *- - - - -,$ ,4 , , , ,$    *	$ 	 	 	 	 !).""2 2S 2c 2C 2 2 2 2jk' k' k' k' k'_ k' k' k' k' k'r   