
    `gj$%                     F   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!y)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                  p    t        j                  d      ryt        j                  d      ry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     Q/root/.hermes/venv/lib/python3.12/site-packages/tools/environments/singularity.py_find_singularity_executabler      s2    ||K ||M"
	: r   c                  ~   t               } 	 t        j                  | dgdddt        j                        }|j                  d	k7  r;|j                  j                         d
d }t        d|  d|j                   d|       | S # t        $ r t        d|  d      t        j                  $ r t        d|  d      w xY w)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$$
 A$$&t,Qse#?@Q@Q?RRUV\U]^__J  
05MN
 	
 $$ :Qse#7899:s   *B :B<c                       t        t              S N)r   _SNAPSHOT_STOREr   r   r   _load_snapshotsr-   @   s    O,,r   datac                 $    t        t        |        y r+   )r	   r,   )r.   s    r   _save_snapshotsr0   D   s    _d+r   c                     t        j                  d      } | r t        |       }|j                  dd       |S ddlm}  |       dz  }t        d      }|j                         rkt        j                  |t         j                        rG|t        j                  dd	      z  d
z  }|j                  dd       t        j                  d|       |S |j                  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    YY56NN+4$77-/G:G~~BIIgrww768!<<~M4$76EMM$M.Nr   c                      t        j                  d      } | r t        |       }|j                  dd       |S t	               }|dz  }|j                  dd       |S )NAPPTAINER_CACHEDIRTr3   z
.apptainer)r9   r:   r   r;   rG   )	cache_dir
cache_pathrE   s      r   _get_apptainer_cache_dirrL   ]   sa    		./I)_
5 G<'JTD1r   image
executablec           	      L   | j                  d      rt        |       j                         r| S | j                  d      s| S | j	                  dd      j	                  dd      j	                  dd      }t               }|| dz  }|j                         rt        |      S t        5  |j                         rt        |      cd d d        S t        j                  d       t        j                  d|        t        j                  d	|       |d
z  }|j                  dd       t        j                  j                         }t        |      |d<   t        |      |d<   	 t        j                  |dt        |      | gddd|t        j                         }|j"                  dk7  rCt        j%                  d       t        j%                  d|j&                  d d        | cd d d        S t        j                  d       t        |      cd d d        S # t        j(                  $ rC t        j%                  d       |j                         r|j+                          | cY cd d d        S t,        $ r+}t        j%                  d|       | cY d }~cd d d        S d }~ww xY w# 1 sw Y   y xY w)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K({B/77SAII#sSJ(*Ij\..H8}	 "??x=" " 	<=NE*NH-e#dT2jjoo"%g,$'	N !	^^Wc(mU;#$ ((F
   A%PQ}fmmDS.AB1" "2 KK67x=5" "6 (( 	NNOP !L?" "@  	NNOQRSLE" "@	A" "sQ   %J
BJA>H&HA
JJ$J,JJJJJJ#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.
    FrM   cwdr   cpumemorydiskpersistent_filesystemtask_idc	                    t         
|   ||       t               | _        t	        || j                        | _        dt        j                         j                  d d  | _	        d| _
        || _        || _        d | _        || _        || _        | j                  rJt!               dz  }	|	j#                  dd       |	d| z  | _        | j                  j#                  dd       | j%                          | j'                          y )	N)ri   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   ri   r   rj   rk   rl   rm   rn   overlay_base	__class__s             r   rs   zSingularityEnvironment.__init__   s     	S'279&udoo>
$TZZ\%5%5cr%:$;<!&0,0	+-0AALtd; ,'/C CD##D4#@r   c                 r   | j                   ddg}|j                  ddg       | j                  r3| j                  r'|j                  dt	        | j                        g       n|j                  d       	 ddlm}m}  |       D ]!  }|j                  d	|d
    d|d    dg       #  |       D ]!  }|j                  d	|d
    d|d    dg       # 	 | j                  dkD  r |j                  d| j                   dg       | j                  dkD  r&|j                  dt	        | j                        g       |j                  t	        | j                        | j                  g       	 t!        j"                  |dddt         j$                        }|j&                  dk7  rt)        d|j*                         d| _        t        j/                  d| j                  | j                         y # t        $ r!}t        j                  d|       Y d }~<d }~ww xY w# t         j0                  $ 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   extendry   r{   r[   appendtools.credential_filesr   r   ra   r@   debugr}   r|   rM   rw   r   r    r!   r$   r   r%   rx   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JJS):):%;<=JJ)*	Xe9; i

H[)A(B!KP`DaCbbe&fghi : < k

Hk)B(C1\RbEcDddg&hijk
 <<!JJ
t||nA$67899q=JJ#dii.12

C

OT%5%567	;^^C4QT\f\n\noF  A%"%?#OPP%)D"KKI(($*:*:<  	XLLSUVWW	X" (( 	;9::	;s&   4AG* (BH *	H3HHH6r   N)loginr   
stdin_data
cmd_stringr   r   r
   c                    | j                   st        d      | j                  dd| j                   g}|r|j	                  ddd|g       n|j	                  dd|g       t        ||      S )z5Spawn a bash process inside the Singularity instance.z Singularity instance not startedexeczinstance://bashz-lz-c)rx   r   rN   rw   r   r   )r   r   r   r   r   r   s         r   	_run_bashz SingularityEnvironment._run_bash   sr     %%ABBT--./1JJdJ78JJj123
++r   c                    | j                   rh	 t        j                  | j                  dd| j                  gdddt        j
                         t        j                  d| j                         d	| _         | j                  rE| j                  r8t               }t        | j                        || j                  <   t        |       yyy# t        $ r+}t        j                  d| j                  |       Y d}~d}~ww xY w)
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)rx   r   r    rN   rw   r!   r@   rA   ra   r_   ry   r{   r-   r[   rz   r0   )r   re   	snapshotss      r   cleanupzSingularityEnvironment.cleanup   s    !!b__j&$:J:JK#'dB$,,
 =t?O?OP &+D" 1 1')I'*4+<+<'=Idmm$I& !2	  bKTM]M]_`aabs   A C 	C;!C66C;)~<   r   r   r   Fdefault)__name__
__module____qualname____doc__r[   intfloatboolrs   r~   r   Popenr   r   __classcell__)r   s   @r   rh   rh      s     &+   	
     $ >!;F ;@!$+/,C ,4 ,,!Dj,4>4D4D, 'r   rh   )r   )"r   loggingr9   r   r   	threadingrt   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'r   