
    `gjv                     (   d 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 ddlmZ ddlmZmZmZmZmZmZmZ  ej4                  e      Z ee      j<                  j<                  j?                         Z  ee d	z        d
z  dz  dz  dz  Z! e       dz  d
z  dz  dz  dz  Z"dZ#de$e%   fdZ&de'ddfdZ(de'ddfdZ)dZ*dedz  fdZ+defdZ,de$e   fdZ-dede$e.ee%f      fdZ/d#dede'defdZ0d Z1d Z2d  Z3d!e4de'fd"Z5y)$u?  hermes claw — OpenClaw migration commands.

Usage:
    hermes claw migrate              # Preview then migrate (always shows preview first)
    hermes claw migrate --dry-run    # Preview only, no changes
    hermes claw migrate --yes        # Skip confirmation prompt
    hermes claw migrate --preset full --overwrite --migrate-secrets  # Full run w/ secrets
    hermes claw migrate --no-backup  # Skip pre-migration snapshot
    hermes claw cleanup              # Archive leftover OpenClaw directories
    hermes claw cleanup --dry-run    # Preview what would be archived
    N)datetime)Path)Optional)get_hermes_homeget_config_pathload_configsave_config)get_optional_skills_dir)Colorscolorprint_header
print_infoprint_successprint_errorprompt_yes_nozoptional-skills	migrationzopenclaw-migrationscriptszopenclaw_to_hermes.pyskills)	.openclaw	.clawdbot.moltbotreturnc                     g } t         j                  dk7  rJ	 t        j                  g dddd      }|j                  j                         dk(  r| j                  d       t         j                  dk(  r	 dD ]R  }t        j                  d	d
d| gddd      }||j                  j                         v s?| j                  d|        T d}t        j                  ddd|gddd      }|j                  j                         r-| j                  d|j                  j                          d       | S 	 t        j                  g dddd      }|j                  dk(  rL|j                  j                         j                         }| j                  ddj                  |       d       | S # t        t        j                  f$ r Y hw xY w# t        $ r Y | S w xY w# t        t        j                  f$ r Y | S w xY w)zDetect running OpenClaw processes and services.

    Returns a list of human-readable descriptions of what was found.
    An empty list means nothing was detected.
    win32)	systemctlz--userz	is-activezopenclaw-gateway.serviceT   )capture_outputtexttimeoutactivez)systemd service: openclaw-gateway.service)zopenclaw.exez	clawd.exetasklistz/FIzIMAGENAME eq z	process: zGet-CimInstance Win32_Process -Filter "Name = 'node.exe'" | Where-Object { $_.CommandLine -match "openclaw|clawd" } | Select-Object -First 1 ProcessId
powershellz
-NoProfilez-Commandz4node.exe process with openclaw in command line (PID ))pgrepz-fopenclaw   r   zopenclaw process(es) (PIDs: , )sysplatform
subprocessrunstdoutstripappendFileNotFoundErrorTimeoutExpiredlower	Exception
returncodesplitjoin)foundresultexeps_cmdpidss        B/root/.hermes/venv/lib/python3.12/site-packages/hermes_cli/claw.py_detect_openclaw_processesr<   :   s    E ||w	^^P#$F }}""$0HI
 ||w	4 4#-u(=>#'dA &----//LL9SE!2343 
  ^^|Z@#$F }}""$STZTaTaTgTgTiSjjklm L		^^+#$F   A%}}**,224;DIIdO;LANO LS ":#<#<= 		6  	 L	 ":#<#<= 	L	s?   A	F, 4A G 5A<G 4A6G ,G	G		GGG98G9auto_yesc                 h   t               }|syt                t        d       |D ]  }t        d|         t        d       t        d       t                | ryt        j
                  j                         st        d       yt        dd	      s!t        d
       t	        j                  d       yy)zWarn if OpenClaw is still running before migration.

    Telegram, Discord, and Slack only allow one active connection per bot
    token. Migrating while OpenClaw is running causes both to fight for the
    same token.
    NzOpenClaw appears to be running:  * zMessaging platforms (Telegram, Discord, Slack) only allow one active session per bot token. If you continue, both OpenClaw and Hermes may try to use the same token, causing disconnects.z/Recommendation: stop OpenClaw before migrating.u7   Non-interactive session — continuing to preview only.Continue anyway?Fdefaultz1Migration cancelled. Stop OpenClaw and try again.r   )	r<   printr   r   r(   stdinisattyr   exit)r=   runningdetails      r;   _warn_if_openclaw_runningrI   w   s     )*G	G12 $T&?#$	E
 @A	G99LM+U;FG <    c                    ddl m}m}  |       sy |       xs i }|j                  d      xs i }|j	                         D cg c]+  \  }}t        |t              r|j                  d      dk(  r|- }}}|syt                t        ddj                  |      z          t        d	       t        d
       t                | s/t        dd      s!t        d       t        j                  d       yyyc c}}w )a  Check if a Hermes gateway is running with connected platforms.

    Migrating bot tokens while the gateway is polling will cause conflicts
    (e.g. Telegram 409 "terminated by other getUpdates request"). Warn the
    user and let them decide whether to continue.
    r   )get_running_pidread_runtime_statusN	platformsstate	connectedz3Hermes gateway is running with active connections: r'   zMigrating bot tokens while the gateway is active will cause conflicts (Telegram, Discord, and Slack only allow one active session per token).zBRecommendation: stop the gateway first with 'hermes gateway stop'.r@   FrA   z4Migration cancelled. Stop the gateway and try again.)gateway.statusrL   rM   getitems
isinstancedictrC   r   r5   r   r   r(   rF   )r=   rL   rM   datarN   nameinforP   s           r;   _warn_if_gateway_runningrY      s     D &BD%+I(1(9 Q*$tT*txx/@K/O  QI Q	G=
))I
	 	
 ST	GM*<eLIJ M8#Qs   0C3)z*/todo.jsonz*/sessions/*z*/memory/*.jsonz*/logs/*c                  N    t         t        fD ]  } | j                         s| c S  y)z9Find the openclaw_to_hermes.py script in known locations.N)_OPENCLAW_SCRIPT_OPENCLAW_SCRIPT_INSTALLEDexists)	candidates    r;   _find_migration_scriptr_      s.    &(BC 	 rJ   script_pathc                    t         j                  j                  d|       }||j                  yt         j                  j	                  |      }|t
        j                  |j                  <   	 |j                  j                  |       |S # t        $ r, t
        j                  j                  |j                  d        w xY w)z2Dynamically load the migration script as a module.openclaw_to_hermesN)	importlibutilspec_from_file_locationloadermodule_from_specr(   modulesrW   exec_moduler2   pop)r`   specmods      r;   _load_migration_modulerm      s    >>112FTD|t{{*
..
)
)$
/C !CKK		$ J  		4(s   -B
 
5B?c                      g } t         D ];  }t        j                         |z  }|j                         s+| j	                  |       = | S )z&Find all OpenClaw directories on disk.)_OPENCLAW_DIR_NAMESr   homeis_dirr.   )r6   rW   r^   s      r;   _find_openclaw_dirsrr      sE    E# $IIK$&	LL#$ LrJ   
source_dirc           	      P   g }| j                         s|S dD ]E  }| |z  }|j                         s|j                         rdnd}|j                  |d| d| f       G 	 t        | j	                               }|D ]  }|j                         r|j                  j                  d      r/dD ]d  }||z  }|j                         s|j                         rdnd}|j                  |       j                         }	|j                  |d| d|	 f       f  |S # t
        $ r |cY S w xY w)	zmScan an OpenClaw directory for workspace state files.

    Returns a list of (path, description) tuples.
    )	todo.jsonsessionslogs	directoryfilezRoot z: .)ru   rv   rw   memoryz
Workspace )
r]   rq   r.   sortediterdirOSErrorrW   
startswithrelative_toas_posix)
rs   findingsrW   r^   kindchildrenchild
state_name
state_pathrels
             r;   _scan_workspace_stater      sH   
 (*H 2 A%	"+"2"2"4;&DOOY%vRv(>?@	A*,,./  	J||~!6!6s!;E 	JJ+J  "&0&7&7&9{v ,,Z8AACz$r#-G HI	J		J O  s    D D%$D%dry_runc                    t        j                         j                  d      }| j                   d}| j                  |z  }|j                         r | j                   d| }| j                  |z  }d}|j                         r9| j                   d| d| }| j                  |z  }|dz  }|j                         r9|s| j                  |       |S )zSRename an OpenClaw directory to .pre-migration.

    Returns the archive path.
    z%Y%m%d.pre-migrationz.pre-migration-   -   )r   nowstrftimerW   parentr]   rename)rs   r   	timestamparchive_namearchive_pathcounters         r;   _archive_directoryr     s    
 ''1I oo&n5L$$|3L $//*/)E!((<7 G



$//*/)AgYO!((<71 



 ,'rJ   c                     t        | dd      }|dk(  rt        |        y|dv rt        |        yt        d       t                t        d       t        d       t        d       t                t        d	       y)
zRoute hermes claw subcommands.claw_actionNmigrate>   cleancleanupz&Usage: hermes claw <command> [options]z	Commands:z;  migrate          Migrate settings from OpenClaw to HermeszH  cleanup          Archive leftover OpenClaw directories after migrationz/Run 'hermes claw <command> --help' for options.)getattr_cmd_migrate_cmd_cleanuprC   )argsactions     r;   claw_commandr   '  sg    T=$/FT	'	'T67kKLXY?@rJ   c                 D   t        | dd      }|rt        |      }nYt        j                         dz  }|j                         s2dD ]-  }t        j                         |z  }|j                         s+|} n t        | dd      }t        | dd      }t        | d	d      }t        | d
d      }t        | dd      }	t        | dd      }
t        | dd      }t	                t	        t        dt        j                               t	        t        dt        j                               t	        t        dt        j                               |j                         s/t	                t        d|        t        d       t        d       yt               }|sPt	                t        d       t        d       t        dt                t        dt                t        d       yt               }t        | dd      }t	                t        d       t        d|        t        d|        t        d|        t        d|rdnd        t        d |rd!nd"        |
dk7  rt        d#|
        |	rt        d$|	        t	                t        |       t!        |       t#               }|j%                         st'        t)                      	 t+        |      }|t        d%       y	 |j3                  dd|*      }|	rt        |	      j5                         nd}	 |j7                  |j5                         |j5                         d|||d|||
+
      }|j9                         }|j;                  d.i       }|j;                  d/d0      }|j;                  d1d0      }|d0k(  r(|d0k(  r#t	                t        d2       t=        |d(3       yt	                |d0kD  rt        d4| d5       nt        d4| d6       t        d7       t=        |d(3       |ry|d0kD  r2|s0t	                t        d8| d9       t        d:       t        d;       yt	                |sNt>        j@                  jC                         st        d<       t        d=       ytE        d>d(?      st        d@       yd}|sh	 d0dAl#m$}m%}  ||B      }|rT ||jM                         jN                        }t	                tQ        dC| dD| dE       t        dF|jR                          	 |j7                  |j5                         |j5                         d(|||d|||
+
      }|j9                         }t=        |d3       y# t,        $ r9}t	                t        d&|        t.        j1                  d'd()       Y d}~yd}~ww xY w# t,        $ r9}t	                t        d,|        t.        j1                  d-d()       Y d}~yd}~ww xY w# t,        $ rD}t	                t        dG|        t        dH       t.        j1                  dId()       Y d}~yd}~ww xY w# t,        $ ra}t	                t        dJ|        t.        j1                  d'd()       |r&t        dK|        t        dF|jR                          Y d}~yd}~ww xY w)Lu&   Run the OpenClaw → Hermes migration.sourceNr   )r   r   r   Fpresetfull	overwritemigrate_secretsworkspace_targetskill_conflictskip	no_backup   ┌─────────────────────────────────────────────────────────┐uB   │          ⚕ Hermes — OpenClaw Migration                 │   └─────────────────────────────────────────────────────────┘zOpenClaw directory not found: z=Make sure your OpenClaw installation is at the expected path.zNYou can specify a custom path: hermes claw migrate --source /path/to/.openclawzMigration script not found.zExpected at one of:  z4Make sure the openclaw-migration skill is installed.yeszMigration SettingszSource:      zTarget:      zPreset:      zOverwrite:   zno (skip conflicts)zSecrets:     zyes (allowlisted only)nozSkill conflicts: zWorkspace:   z Could not load migration script.z!Could not load migration script: zOpenClaw migration errorT)exc_info)r   )
source_roottarget_rootexecuter   r   r   
output_dirselected_optionspreset_nameskill_conflict_modezMigration preview failed: z OpenClaw migration preview errorsummarymigratedr   conflictz!Nothing to migrate from OpenClaw.r   u   Migration Preview — z item(s) would be importedz' conflict(s), nothing would be importedz5No changes have been made yet. Review the list below:z	Plan has z  conflict(s). Refusing to apply.zEach conflict is an item whose target already exists in ~/.hermes/. Re-run with --overwrite to replace conflicting targets (item-level backups are written to the migration report directory).z1Or re-run with --dry-run to review the full plan.u)   Non-interactive session — preview only.z2To execute, re-run with: hermes claw migrate --yeszProceed with migration?rA   zMigration cancelled.)create_pre_migration_backup_format_size)hermes_homezPre-migration backup: z (r#   zRestore with: hermes import z'Could not create pre-migration backup: zMRe-run with --no-backup to skip, or free up disk space under the Hermes home.zPre-migration backup errorzMigration failed: z(A pre-migration backup is available at: )*r   r   rp   rq   rC   r   r   MAGENTAr   r   r_   r[   r\   r   r   rI   rY   r   r]   r	   r   rm   r2   loggerdebugresolve_selected_optionsresolveMigratorr   rR   _print_migration_reportr(   rD   rE   r   hermes_cli.backupr   r   statst_sizer   rW   )r   explicit_sourcers   legacyr^   r   r   r   r   r   r   r   r`   r   r=   config_pathrl   eselected	ws_targetpreviewpreview_reportpreview_summarypreview_countpreview_conflictsbackup_archiver   r   size_strmigratorreports                                  r;   r   r   9  s    dHd3O/*
YY[;.
  "3  IIK&0	##%!*J	
 dIu-GT8V,Fk51Id$5u=Ot%7>T#3V<Nk51I 
G	 @NN	
 
PNN	
 
 @NN	
 4ZLABRScd )*K12()R()*+R2345IJ "#KtUE*H	G%&zl+,{m,-vh'(	u7LMNO?7PTUVW&~&678]#3"456	G h' X& "#KKM"	$[1;:;  ++D$v+FH4D%&..0$I,,"**,#++-&+% .  
 !* %((B7O#''
A6M'++J:
 /1467=	Gq-m_<VWX$%6$77^_	
 FGND9  1Y)**JK	
 	F	

 	FG 
Gyy!BCKL6E-. &*N	S8[QN'(;(;(=(E(EF 6~6Fb
RSTU9.:M:M9NOP<<"**,#++-&+% .   
 !!# FE2Q  7s;</$?	0  0457$G	^  	GA!EF_ LL5LE	.  (,-/$?A.AQRS5n6I6I5JKLsd   	U AV  A'W% AX5 	V$/VV 	W")/WW"%	X2.:X--X25	Z>AZZc           	      	  
 t        | dd      }t        | dd      }t        | dd      }t                t        t        dt        j                               t        t        dt        j                               t        t        dt        j                               |rt        |      g}n
t               }|st                t        d	       yt               }|rt                t        d
       |D ]  }t        d|         t        d       t        d       t                |sCt        j                  j                         st        d       yt        dd      st        d       yd}|D ]  }t                t        d|        t!        |      }		 |j#                         D 

cg c]C  

j%                         r1
j&                  j)                  d      st+        
fddD              r
E }}
|rt        dt/        |              |dd D ]  }g }|dz  j1                         r|j3                  d       |dz  j%                         r|j3                  d       |dz  j1                         r|j3                  d       |dz  j1                         r|j3                  d       |rdj5                  |      nd}t        d |j&                   d!| d"        t/        |      dkD  rt        d#t/        |      dz
   d$       |	r~t                t        t        d%t/        |	       d&t        j6                               |	dd' D ]  \  }}t        d |         t/        |	      d'kD  rt        d#t/        |	      d'z
   d$       t                |r t9        |d()      }t        d*| d+|        A|s9t        j                  j                         st        d,|        t        d-       ||st        d.| d/d(      r$	 t9        |      }t        d0| d+|        |d1z  }t        d6        t                |r0t/        |      }t        d7| d4|d1k(  rd8nd9 d:       t        d;       y|r%t        d<| d=|d1k(  rd8nd9 d       t        d>       yt        d?       yc c}
w # t,        $ r g }Y w xY w# t,        $ r+}t        d2|        t        d3| d4| d5       Y d}~vd}~ww xY w)@zArchive leftover OpenClaw directories after migration.

    Scans for OpenClaw directories that still exist after migration and offers
    to rename them to .pre-migration to free disk space.
    r   Fr   r   Nr   uB   │          ⚕ Hermes — OpenClaw Cleanup                   │r   z3No OpenClaw directories found. Nothing to clean up.z%OpenClaw appears to be still running:r?   zArchiving .openclaw/ while the service is active may cause it to immediately recreate an empty skeleton directory, destroying your config.zCStop OpenClaw first: systemctl --user stop openclaw-gateway.serviceu?   Non-interactive session — aborting. Stop OpenClaw and re-run.zProceed anyway?rA   z>Aborted. Stop OpenClaw first, then re-run: hermes claw cleanupr   zFound: rz   c              3   D   K   | ]  }|z  j                           y w)N)r]   ).0rW   ds     r;   	<genexpr>z_cmd_cleanup.<locals>.<genexpr>{  s     iT))+is    )ru   SOUL.md	MEMORY.mdzUSER.mdzWorkspace directories: r   ru   rv   z	sessions/r   r   r'   empty      z/  (r#   z      ... and z morer   z state file(s) found:   Tr   zWould archive:     → u+   Non-interactive session — would archive: z2To execute, re-run with: hermes claw cleanup --yeszArchive ?z
Archived: r   zCould not archive: zTry manually: mv  r   zSkipped.zDry run complete. rx   directoriesz would be archived.z&Run without --dry-run to archive them.zCleaned up z
 OpenClaw zJDirectories were renamed, not deleted. You can undo by renaming them back.zNo directories were archived.)r   rC   r   r   r   r   rr   r   r<   r   r   r(   rD   rE   r   r   r   r}   rq   rW   r   anyr~   lenr]   r.   r5   YELLOWr   )r   r   r=   r   dirs_to_checkrG   rH   total_archivedrs   state_filesr   workspace_dirswsrS   pathdescr   r   _n_dirss             `        r;   r   r   .  s    dIu-GtUE*HdHd3O	G	 @NN	
 
PNN	
 
 @NN	
 o./+-KL )*G;< 	(FfX'	(X	
 	XY99##%\] !2EB[\N# ;#
wzl+, ,J7	 %--/88:aff&7&7&<i8hii N  0^1D0EFG$Ra( 7$,,.LL-O++-LL-N**,LL+$,,.LL--25)rwwitF81567 >"Q&s>':Q'>&?uEFG%"S-..CDfmmTU)"1o '
dtfo&';!#s;'7!';&<EBC-j$GLE,HI#))"2"2"4DZLQRKL*Q'?NX1*=
:,eL>JK!#
 z"w;#| 
Gm$ 	%l{>>QS	
 	;<	.),1{}EQH	
 	_`23I
  	 N	 R  X1!56.zl!J<~VWWXs=   R!'AR/R!<!R3R!!R0/R03	S'< S""S'r   c                 R
   | j                  di       }|j                  dd      }|j                  dd      }|j                  dd      }|j                  dd      }t                |rt        d       t        d       nt        d	       t                | j                  d
g       }|r|D cg c]  }|j                  d      dk(  s| }	}|D cg c]  }|j                  d      dk(  s| }
}|D cg c]  }|j                  d      dk(  s| }}|D cg c]  }|j                  d      dk(  s| }}|	r|rdnd}t        t	        d| dt
        j                               |	D ]  }|j                  dd      }|j                  dd      }|rIt        |      j                  t        t        j                               d      }t        d|dd|        rt        d|         t                |rjt        t	        dt
        j                               |D ]8  }|j                  dd      }|j                  dd      }t        d|dd|        : t                |
rjt        t	        dt
        j                               |
D ]8  }|j                  dd      }|j                  dd      }t        d|dd|        : t                |rjt        t	        dt
        j                               |D ]8  }|j                  dd      }|j                  dd      }t        d|dd|        : t                g }|r|rdnd}|j                  | d |        |r|j                  | d!       |r|j                  | d"       |r|j                  | d#       |rt        d$d%j                  |              nt        d&       | j                  d'      }|rt        d(|        |r4t                t        d)       t        d*| j                  d+d,              y5|rt                t!        d-       | j                  d
g       D cg c],  }|j                  d      d.k(  r|j                  d      dk(  r|. }}|rt                t        t	        d/t
        j                               t        t	        d0t
        j                               t                t        d1       t        d2       t                t        d3       t        d4       y5y5y5c c}w c c}w c c}w c c}w c c}w )6z#Print a formatted migration report.r   r   r   skippedr   errorzDry Run Resultsz?No files were modified. This is a preview of what would happen.zMigration ResultsrS   statuszWould migrateMigratedu     ✓ :r   unknowndestination ~r   z<22sr   u7     ⚠ Conflicts (skipped — use --overwrite to force):reasonzalready existsr   u     ─ Skipped:u     ✗ Errors:zunknown errorzwould migrater   z conflict(s)z skippedz	 error(s)z	Summary: r'   zNothing to migrate.r   zFull report saved to: z0To execute the migration, run without --dry-run:z  hermes claw migrate --preset r   r   zMigration complete!zprovider-keysuL     ⚠ API keys were NOT migrated (secrets migration is disabled by default).zI  Your OPENROUTER_API_KEY and other provider keys must be added manually.z!To migrate API keys, re-run with:z'  hermes claw migrate --migrate-secretszOr add your key manually:z3  hermes config set OPENROUTER_API_KEY sk-or-v1-...N)rR   rC   r   r   r   r   GREENstrreplacer   rp   r   DIMREDr.   r5   r   )r   r   r   r   r   	conflictserrorsrS   imigrated_itemsskipped_itemsconflict_itemserror_itemslabelitemr   dest
dest_shortr   partsr   r   skipped_keyss                          r;   r   r     s   jjB'G{{:q)Hkk)Q'GJ*I[[!$F	G&'TU()	G JJw#E%*LaeeHo.K!LL$)JqQUU8_	-IJJ%*LaeeHo.K!LL"'FQ155?g+EqFF'.OJE%&q)6<<89& +xx	2xxr2!$T!2!23tyy{3CS!IJF4+U:,?@F4&/*+ G%QSYS`S`ab& 6xx	2(,<=tDkF8456 G%(&**56% 6xx	2(B/tDkF8456 G%45# 6xx	2(O<tDkF8456 G E$+z6(+,	{,/0y)*xy)*Ytyy/012() L)J+J<89EF4VZZ&5Q4RST	+, zz'2.
uuV}/AEE(Oy4P 
 
 G%fhnhuhuvw%cekererstG:;@AG23LM  
I MJLFJ
s6   ,TTT*T4TTT2T1T$)F)6__doc__importlib.utilrc   loggingr*   r(   r   pathlibr   typingr   hermes_cli.configr   r   r   r	   hermes_constantsr
   hermes_cli.setupr   r   r   r   r   r   r   	getLogger__name__r   __file__r   r   PROJECT_ROOTr[   r\   ro   listr   r<   boolrI   rY   _WORKSPACE_STATE_GLOBSr_   rm   rr   tupler   r   r   r   r   rU   r    rJ   r;   <module>r"     s  
    
    X X 4   
		8	$H~$$++335 L+<<=  	    	
   = :DI :z  @!t ! !J t  "T$Z "d "tE$)4D/E "J4 $ 4 6A$n3jN4bjND jN4 jNrJ   