
    6j*+                        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m	Z	 ddl
mZ ddlmZ  ej        e          Z ej        d          Zdedefd	Zdefd
ZdedefdZdefdZdedefdZdefdZdedeeef         fdZdededeeef         dz  fdZdededeeef         fdZdededeeef         fdZdS )a  
Hermes Web UI -- Filesystem checkpoint (rollback) API.

Provides endpoints to list, diff, and restore filesystem checkpoints
created by the Hermes agent's CheckpointManager.  Checkpoints live at
``{hermes_home}/checkpoints/<hash>/`` as shadow git repositories.
    N)datetimetimezone)Path)Anyz#^[A-Za-z0-9_-][A-Za-z0-9_.-]{0,63}$
checkpointreturnc                     t          | pd                                          }|r|dv st                              |          st	          d          |S )N ).z..z:checkpoint id must match [A-Za-z0-9_-][A-Za-z0-9_.-]{0,63})strstrip_CHECKPOINT_ID_RE	fullmatch
ValueError)r   cids     "/root/hermes-webui/api/rollback.py_validate_checkpoint_idr   !   sb    
jB


%
%
'
'C 
#$$,=,G,G,L,L$H
 
 	
 J    c                      	 ddl m}  t           |                       S # t          $ rB t          t          j                            dd                                                    cY S w xY w)z(Return the active Hermes home directory.r   get_active_hermes_homeHERMES_HOMEz	~/.hermes)api.profilesr   r   	Exceptionosenvironget
expanduserr   s    r   _hermes_homer   *   s    M777777**,,--- M M MBJNN=+>>??JJLLLLLMs    A	A+*A+	workspacec                     	 t           j                            |           }n# t          t          f$ r | }Y nw xY wt          j        |                                                                          dd         S )zDerive the checkpoint directory name from a workspace path.

    Matches the agent's CheckpointManager._get_checkpoint_dir logic:
    SHA-256 of the canonical workspace path.
    N   )	r   pathrealpathOSErrorr   hashlibsha256encode	hexdigest)r    	canonicals     r   _workspace_hashr+   3   sx    G$$Y//		Z    			>)**,,--7799#2#>>s   " 88c                  $    t                      dz  S )Ncheckpoints)r    r   r   _checkpoint_rootr/   @   s    >>M))r   c                 L   | rt          | t                    st          d          t          j                            |           }t          j                            |          st          d|            	 ddlm} t                      } |            D ]L}|
                    dd          }|r2|                    t          j                            |                     M||vrt          d|            n*# t          $ r t                              d           Y nw xY w|S )	zValidate and return the canonical workspace path.

    Security: workspace must match a known configured workspace
    (from workspaces.json or session-attached workspaces).
    zworkspace is requiredzWorkspace does not exist: r   )load_workspacesr#   r
   z"Workspace not in configured list: z5Could not load workspace list for rollback validation)
isinstancer   r   r   r#   r$   isdirapi.workspacer1   setr   addImportErrorloggerwarning)r    resolvedr1   known_pathswsps         r   _resolve_workspacer>   D   sI     2Jy#66 20111w	**H7=="" CAiAABBB
P111111ee!/## 	5 	5Bvr""A 5 0 0 3 3444;&&M)MMNNN ' P P PNOOOOOPOs   8BC: :$D! D!c                  .    t          j        d          pdS )z"Return the path to the git binary.git)shutilwhichr.   r   r   	_find_gitrC   _   s    <'%'r   c                    t          |           }t          |          }t                      |z  }g }|                                sg |t	          |          dS t                      }t          |                                d d          D ]>}|                                st          ||          }|r|	                    |           ?||t	          |          dS )zList all checkpoints for a workspace.

    Returns a dict with:
        checkpoints: list of checkpoint objects
        workspace: resolved workspace path
        checkpoint_dir: the checkpoint directory path
    )r-   r    checkpoint_dirc                 `    |                                  r|                                 j        ndS )Nr   )is_dirstatst_mtime)r=   s    r   <lambda>z"list_checkpoints.<locals>.<lambda>y   s$    qxxzz:`!&&((:K:K_` r   T)keyreverse)
r>   r+   r/   rG   r   rC   sortediterdir_inspect_checkpointappend)r    r:   ws_hashckpt_dirr-   r@   entry	ckpt_infos           r   list_checkpointsrU   g   s     "),,Hh''G!!G+HK?? [!CPXMMZZZ ++C((**0`0`jnooo * *||~~ 	's33	 	*y))) #h--  r   	ckpt_pathr@   c           	      8   | dz  }|                                 sdS | j        }	 t          j        |dt	          |           dddgddd	          }|j        d
k    s|j                                        sdS |j                                                            d          }t          |          d
k    r|d
         n|}t          |          dk    r|d         nd}t          |          dk    r|d         nd}d}	|rD	 t          j        |          }
|
                    d          }	n# t          t          f$ r |}	Y nw xY wt          j        |dt	          |           dgddd	          }|j                                        r9t          |j                                                            d                    nd
}||dd         |||	|t	          |           dS # t          j        t           f$ r'}t"                              d| |           Y d}~dS d}~ww xY w)z4Extract metadata from a single checkpoint directory.z.gitN-Clogz--format=%H%n%s%n%aIz-1T   capture_outputtexttimeoutr   
   r      r
   z%Y-%m-%d %H:%Mls-filesr"   )idcommitmessagedatedate_displayfilesr#   z#Failed to inspect checkpoint %s: %s)rG   name
subprocessrunr   
returncodestdoutr   splitlenr   fromisoformatstrftimer   	TypeErrorTimeoutExpiredr%   r8   debug)rV   r@   git_dirri   resultlinescommit_hashre   date_strrg   dtfiles_result
file_countes                 r   rO   rO      sY   & G>> t>D($I/EtLdA
 
 
 !!)<)<)>)>!4##%%++D11"%e**q..eAhhd!%jj1nn%((,"5zzA~~5882  	((+H55!{{+;<<	* ( ( ('( "~$I
3dA
 
 
 FREXE^E^E`E`gS,2244::4@@AAAfg
 !#2#&(	NN
 
 	
 %w/   :IqIIItttttsD   AG 5BG =)D' &G 'D=:G <D==BG H2HHc           
         t          |           }t          |          }t          |          }t                      |z  |z  }|                                st          d|           t                      }t          j        |dt          |          dgddd          }|j
        dk    rt          d          d	 |j                                                            d
          D             }g }g }	|D ]}
||
z  }t          |          |
z  }|                                s/	 |                    d          }n# t"          $ r Y Sw xY w|                                r*	 |                    d          }n# t"          $ r d}Y nw xY wd}||                    |
dd           |	                    d|
            |	                    d           |	                    d                    t)          |                                                               |                                D ]}|	                    d|            W||k    rddl}|                    d          }|                    d          }t/          |                    ||d|
 d|
 d                    }|r-|                    |
dd           |	                    |           |||	rd
                    |	          nd|t)          |          dS )zShow the diff between a checkpoint and the current workspace state.

    Returns a dict with:
        diff: unified diff text
        files_changed: list of changed file paths
    Checkpoint not found: rX   rb   T
   r[   r   Failed to list checkpoint filesc                     g | ]}||S r.   r.   .0fs     r   
<listcomp>z'get_checkpoint_diff.<locals>.<listcomp>       GGGQG!GGGr   r_   replace)errorsr
   Ndeleted)filestatusz--- a/z+++ /dev/nullz@@ -1,{lines} +0,0 @@)rw   -)keependsza/zb/)fromfiletofilelinetermmodified)r   r    difffiles_changedtotal_changes)r>   r   r+   r/   rG   r   rC   rj   rk   r   rl   rm   r   rn   r   is_file	read_textr%   rP   formatro   
splitlinesdiffliblistunified_diffextendjoin)r    r   r:   rQ   rR   r@   	ls_result
ckpt_filesr   
diff_linesrel_path	ckpt_filews_fileckpt_content
ws_contentliner   
ckpt_linesws_linesr   s                       r   get_checkpoint_diffr      s    "),,H(44Jh''G!!G+j8H?? @>*>>???
++C 	dCMM:.$  I q  :;;;GGY-3355;;DAAGGGJMJ &( &(x'	x..8+  "" 		$..i.@@LL 	 	 	H	 ?? 	 $..i.@@

      


  J  (i!H!HIII1x11222.///5<<3|G^G^G`G`CaCa<bbccc$//11 . .!!*d**----.Z''NNN%00$0??J!,,d,;;H,,ZOQYOOdsiqdsds  A,  B  B  C  CD ($$h*%M%MNNN!!$''' !)3;		*%%%&]++  s$   %D<<
E	E	!E88FFc                    t          |           }t          |          }t          |          }t                      |z  |z  }|                                st          d|           t                      }t          j        |dt          |          dgddd          }|j
        dk    rt          d          d	 |j                                                            d
          D             }g }g }	|D ]}
||
z  }t          |          |
z  }|                                s.	 |j                            dd           t%          j        t          |          t          |                     |                    |
           # t*          $ rK}|	                    |
t          |          d           t,                              d|
|           Y d}~d}~ww xY wd|||t1          |          |	dS )a$  Restore a checkpoint by copying files back to the workspace.

    Only restores files that exist in the checkpoint.  Does NOT delete
    files that were added after the checkpoint was created.

    Returns a dict with:
        ok: True
        files_restored: list of restored file paths
    r   rX   rb   Tr   r[   r   r   c                     g | ]}||S r.   r.   r   s     r   r   z&restore_checkpoint.<locals>.<listcomp>&  r   r   r_   )parentsexist_ok)r   errorzFailed to restore %s: %sN)okr   r    files_restoredfiles_restored_countr   )r>   r   r+   r/   rG   r   rC   rj   rk   r   rl   rm   r   rn   r   r   parentmkdirrA   copy2rP   r%   r8   r9   ro   )r    r   r:   rQ   rR   r@   r   r   restoredr   r   r   r   r}   s                 r   restore_checkpointr   
  s%    "),,H(44Jh''G!!G+j8H?? @>*>>???
++C 	dCMM:.$  I q  :;;;GGY-3355;;DAAGGGJHF D Dx'	x..8+  "" 		DN   ===LYW666OOH%%%% 	D 	D 	DMM8c!ff==>>>NN5xCCCCCCCC	D
  " #H  s   $A F
GAGG)__doc__r&   jsonloggingr   rerA   rj   r   r   pathlibr   typingr   	getLogger__name__r8   compiler   r   r   r   r+   r/   r>   rC   dictrU   rO   r   r   r.   r   r   <module>r      sH       				 				      ' ' ' ' ' ' ' '            		8	$	$ BJEFF      Md M M M M
?s 
?s 
? 
? 
? 
?*$ * * * *# #    6(3 ( ( ( ( S#X    @/4 /c /d38nt6K / / / /dN3 NC NDcN N N N Nb6# 63 64S> 6 6 6 6 6 6r   