
    i$                     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                  p   t                      } 	 t          j        | dgddd          }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timeoutz"Singularity backend selected but 'z' could not be executed.'z version' timed out.r   N   z version' failed (exit code z): )	r   
subprocessrun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	   + ?A*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_snapshotsr.   C   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.baser4   existsaccessW_OKloggerinfo)custom_scratchscratch_pathr4   sandboxscratchuser_scratchs         r   _get_scratch_dirrE   G   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_CACHEDIRTr1   z
.apptainer)r7   r8   r   r9   rE   )	cache_dir
cache_pathrC   s      r   _get_apptainer_cache_dirrJ   \   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|          }|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tmpTr1   APPTAINER_TMPDIRrG   buildiX  )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   r;   
startswithreplacerJ   str_sif_build_lockr>   r?   r9   r7   environcopyr   r   r"   warningr#   r!   unlink	Exception)	rK   rL   
image_namerH   sif_pathtmp_dirrU   r&   es	            r   _get_or_build_sifrd   k   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/! ! ! ! ! ! ! !0 KK6777x==3! ! ! ! ! ! ! !4 ( 	 	 	NNOPPP   "!!!LL=! ! ! ! ! ! ! !>  	 	 	NNOQRSSSLLLLLC! ! ! ! ! ! ! !>	?! ! ! ! ! ! ! ! ! !sW   #L>B.L-A8I'2(I''AL:L	LK?-L.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defaultrK   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)rj   r   hermes_   Fzhermes-overlaysTr1   zoverlay-)super__init__r'   rL   rd   rK   uuiduuid4hexinstance_id_instance_started_persistent_task_id_overlay_dir_cpu_memoryrE   r9   _start_instanceinit_session)selfrK   rj   r   rk   rl   rm   rn   ro   overlay_base	__class__s             r   rt   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                 ^   | 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          }|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_pathrQ   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)rL   extendrz   r|   rY   appendtools.credential_filesr   r   r_   r>   debugr~   r}   rK   rx   r   r   r"   r   r#   ry   r?   r!   )r   cmdr   r   mount_entryskills_mountrc   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UUUF A%%"#O#O#OPPP%)D"KKI($*:< < < < <( 	; 	; 	;9:::	;s&   0A4C% %
D/DD$A'H H,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)ry   r   rL   rx   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
                              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)ry   r   r   rL   rx   r>   r?   r_   r]   rz   r|   r+   rY   r{   r.   )r   rc   	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)rg   rh   r   r   r   Fri   )__name__
__module____qualname____doc__rY   intfloatboolrt   r   r   Popenr   r   __classcell__)r   s   @r   rf   rf      s'         &+    	
     $      >!; !; !;F ;@!$+/, , ,C ,4 ,,!Dj,4>4D, , , , ' ' ' ' ' ' 'r   rf   )r   )"r   loggingr7   r   r   	threadingru   pathlibr   typingr   hermes_constantsr   r:   r   r   r   r	   	getLoggerr   r>   r*   rY   r   r'   dictr+   r.   rE   rJ   LockrZ   rd   rf   r   r   r   <module>r      s     				                       , , , , , ,            
	8	$	$!/##&BB
c 
 
 
 
s    (- - - - -,$ ,4 , , , ,$    *	$ 	 	 	 	 !)."". .S .c .C . . . .bj' j' j' j' j'_ j' j' j' j' j'r   