
    Jji                       U d Z 	 ddlZn# e$ r Y nw xY wddlZddlZddZdaded<   defdZ	ddd	de
fd
ZddZ e             de
fdZdee         de
fdZdedz  fdZddZde
fdZ e            r ed          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 ddlmZ ddlm Z! ddl"m#Z# ddl$m%Z% ddl&m'Z' ddl(m)Z) ddl*m+Z+ ddl,m-Z- ddl.m/Z/ ddl0m1Z1 ddl2m3Z3 ddl4m5Z5 ddl6m7Z7 ddl8m9Z9 dd l:m;Z; dd!l<m=Z= dd"l>m?Z? dd#l@mAZA dd$lBmCZC dd%lDmEZE dd&lFmGZG dd'lHmIZI dd(lJmKZK dd)lLmMZM dd*lNmOZO dd+lPmQZQ dd,lRmSZS dd-lTmUZU dd.lVmWZW dd/lXmYZY dd0lZm[Z[ dd1l\m]Z] dd2l^m_Z_ dd3l`maZa dd4lbmcZc dd5ldmeZe dd6lfmgZg dd7lhmiZi dd8ljmkZk dd9llmmZm d:eddfd;Zn eeo          jp        jp        q                                Zrejs        t                    d eer                     dd<Zu eu             dd=lvmwZw dd>lxmyZy  eyerd?z  @           dAZz	 ddl{Z| ew            dBz  Z}e}~                                r ee}dCD          5 Z e|j        e ee|dEd          pe|j        F          pi Zddd           n# 1 swxY w Y   	 ddGlmZ  ej        e          Zn# e$ r Y nw xY wdHej        vr^e                    dIi           Z eee          r<e                    dJ          Ze% ee                                          ej        dH<   e                    dKi           Z eee          re                    dL          rdMZz[[}n# e$ r Y nw xY w	 ddNlmZ  e edO ej        dPd         D             dQ          dRv rdSndTU           n# e$ r Y nw xY wezr	 ddVlmZ  edMW           n# e$ r Y nw xY wddlZddlZddlZddXlmZ ddYlmZmZ ddZlmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZ  ej        e          Zdd[eeef         dz  de
fd\Zd]ed^ededz  fd_Zd`ededz  fdaZdefdbZdefdcZde
fddZddeZde
fdfZde
fdgZdefdhZde
fdiZdjedee         fdkZÐddledee         fdmZĐddnedoedpe
fdqZdredsefdtZduedee         fdvZdwee         dee         fdxZ	 ddyee         dzee         ddfd{Z ed|d}h          Z	 d~edefdZdAdd~ede
deeeedf         f         fdZdede
fdZdZdZ eh d          ZdefdZdede
fdZԐddZՐddedz  dedz  fdZdede
fdZdeddfdZdede
deee         ef         fdZdedee         fdZdee         fdZݐddedefdZ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddee         de
dee         dee         dededee
         de
dee         dee         de
de
de
dee         de
fdZߐddZddZde
fdZd Zd Zd Zd Zd Zd Zd Zd Zdede
fdZddZd Zg dZeeeeef                  ed<   deeeeef                  fdZdedefdZdQdQdQddedededededdfdZdefdZddZdeddfdZ	 ddededededdf
dƄZdededdfdǄZdddɜdʄZddgZddededee         fd΄ZdedefdτZddЄZddфZ	 dd҄Zdӄ ZdZdՄ Z defdքZdeddfd؄ZddلZddededefdۄZdedefd܄ZdedefdބZd߄ Zd Zd Z	d Z
d Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd ZdMdde
ddfdZd Zd ZdedefdZdZdedz  fdZdee
edz  edz  f         fdZddedede defdZ!dede
fd Z"dddddnee         dedeܐded[eeef         dz  dej#        fdZ$deeef         dz  fdZ%d	dMdd
dededeedf         de
d[eeef         dz  dej#        fdZ&dAddede
de
fdZ'dede
fdZ(dedefdZ)defdZ*dedede
de
fdZ+dede
ddfdZ,dedee         fdZ-dee         fdZ.dedee         fdZ/dZ0dedefdZ1dedefdZ2dede
fd Z3dede
fd!Z4dd"ded[ed#ee         de
fd$Z5ded[ede
fd%Z6dedee         fd&Z7deddfd'Z8d[ede
de
fd(Z9de
fd)Z:d*ede
fd+Z;d*ede
fd,Z<deee         ef         fd-Z=d.ej>        fd/Z?dd0d1e@e         dz  dee         fd2ZAdՐd3ZBdՐd4ZCd5edefd6ZD	 dސd8eddfd9ZEeEZFd:ed;eddfd<ZGd= ZHd>ee         dedee         fd?ZId>ee         ded@edee         fdAZJ	 d֐d@edBee         ddfdCZK	 	 dߐd>ee         ded@edDe
de
f
dEZLd>ee         ded@ede
fdFZMh dGZNdHZOdIZPd>ee         dedee         fdJZQdKee         de
fdLZRd>ee         dede
fdMZSd>ee         dede
fdNZTd>ee         dedOedPedef
dQZUde
fdRZVdS ZWd>ee         dede
fdTZXd>ee         deddfdUZYdV ZZddXedee         fdYZ[defdZZ\dՐd[Z]dՐd\Z^dՐd]Z_dd^d_dnee         d[eeef         dz  d`eddfdaZ`de
fdbZadedz  fdcZbddedee         fdeZcddfddedgedz  deeeef                  fdhZddieeeef                  ddedefdjZedkdlddedmedeeeef                  fdnZfdoedpede
fdqZgdreeeef                  ddfdsZhdddtdnee         d[eeef         dz  ddedz  ddfduZid֐ddedz  ddfdvZjdՐdwZkddWdxdyee         d[eeef         dz  dXeddfdzZldee         fd{Zmdd|dyee         d[eeef         dz  ddfd}ZnddWdxdyee         d[eeef         dz  dXeddfd~Zodyee         d[eeef         dz  dedz  fdZpdd[eeef         dz  de
fdZqde
fdZrdd|dyee         d[eeef         dz  ddfdZsdee         dedz  fdZtdՐdZu G d d          Zvdؐde
fdZwdeddfdZxddddZyd ZzdefdZ{ddAddede
fdZ|dՐdZ}dՐdZ~dedede
fdZdee         de de@e         fdZdee
ef         fdZdd0d1e@e         dz  deeeeef                  fdZdieeeeef                  defdZdedz  fdZdՐdZdedz  ddfdZd Zd Zd Zde
fdZdedefdZd ZdՐdZdefdZdedede
fdZdՐdZd Zd Zd Zd֐dZd Zd Zd Zdee         fdZ eh d          Z eh d          Zdedz  fdZde
fdZh dZdddhfddhfddhfdZde
fdÄZde
fdĄZde
fdńZdՐdƄZdՐdǄZde
fdȄZde
fdɄZdʄ Zd˄ Zd̄ Zd̈́ Zd΄ Zdτ ZdЄ Zdф Zd҄ Zdӄ Zedk    r e             dS dS (  u	  
Hermes CLI - Main entry point.

Usage:
    hermes                     # Interactive chat (default)
    hermes chat                # Interactive chat
    hermes gateway             # Run gateway in foreground
    hermes gateway start       # Start gateway as service
    hermes gateway stop        # Stop gateway service
    hermes gateway status      # Show gateway status
    hermes gateway install     # Install gateway service
    hermes gateway uninstall   # Uninstall gateway service
    hermes setup               # Interactive setup wizard
    hermes logout              # Clear stored authentication
    hermes status              # Show status of all components
    hermes cron                # Manage cron jobs
    hermes cron list           # List cron jobs
    hermes cron status         # Check if cron scheduler is running
    hermes doctor              # Check configuration and dependencies
    hermes honcho setup                    # Configure Honcho AI memory integration
    hermes honcho status                   # Show Honcho config and connection status
    hermes honcho sessions                 # List directory → session name mappings
    hermes honcho map <name>               # Map current directory to a session name
    hermes honcho peer                     # Show peer names and dialectic settings
    hermes honcho peer --user NAME         # Set user peer name
    hermes honcho peer --ai NAME           # Set AI peer name
    hermes honcho peer --reasoning LEVEL   # Set dialectic reasoning level
    hermes honcho mode                     # Show current memory mode
    hermes honcho mode [hybrid|honcho|local]  # Set memory mode
    hermes honcho tokens                   # Show token budget settings
    hermes honcho tokens --context N       # Set session.context() token cap
    hermes honcho tokens --dialectic N     # Set dialectic result char cap
    hermes honcho identity                 # Show AI peer identity representation
    hermes honcho identity <file>          # Seed AI peer identity from a file (SOUL.md etc.)
    hermes honcho migrate                  # Step-by-step migration guide: OpenClaw native → Hermes + Honcho
    hermes version             Show version
    hermes update              Update to latest version
    hermes uninstall           Uninstall Hermes Agent
    hermes acp                 Run as an ACP server for editor integration
    hermes sessions browse     Interactive session picker with search

    hermes claw migrate --dry-run  # Preview migration without changes
    Nreturnc                     	 ddl } |                      d           dS # t          $ r Y nw xY wddl}ddl}	 |                                }|dk    r2|                    dd          }|                    dd	ddd           dS |d
k    r.|                    dd          }|                    d	           dS dS # t          $ r Y dS w xY w)ud  Set the process title to 'hermes' so tools like 'ps', 'top', and
    'htop' show the app name instead of 'python3.xx'.

    Purely cosmetic — non-fatal on any platform.

    Strategy (try in order):
      1. ``setproctitle`` (opt-in dep — installed via ``hermes tools`` or
         ``pip install setproctitle``, or bundled in a future release).
      2. ctypes ``prctl(PR_SET_NAME)`` (Linux only, 15-char limit).
      3. ctypes ``pthread_setname_np`` (macOS only, kernel thread name —
         changes lldb/top but not ``ps aux``).
      4. No-op on Windows (the .exe name is already ``hermes.exe``).
    r   NhermesLinuxz	libc.so.6T)	use_errno   s   hermesDarwinz
libc.dylib)	setproctitleImportErrorctypesplatformsystemCDLLprctlpthread_setname_np	Exception)r
   r   r   r   libcs        5/home/rurouni/.hermes/hermes-agent/hermes_cli/main.py_set_process_titler   D   s   !!(+++    MMMOOO
""W;;{d;;;DJJr9aA.....x;;|t;<<D##I.....      s$    
**A
B8 2B8 8
CCzlist | None_EARLY_INTERFACE_CACHEc            	      X   t           t           d         S d} 	 t          j                            d          }|r!t          j                            |d          }n>t          j                            t          j                            d          dd          }t          j                            |          rddl}t          |d	          5 }|
                    |t          |d
d          p|j                  pi }ddd           n# 1 swxY w Y   |                    di           }t          |t                    rV|                    d          }t          |t                    r,|                                                                dk    rd} n# t$          $ r d} Y nw xY w| ga | S )zReturn the configured default interface ("cli"/"tui") via a minimal
    YAML read. Best-effort: any error falls back to "cli" (legacy behavior).Nr   cliHERMES_HOMEconfig.yaml~.hermesutf-8encodingCSafeLoaderLoaderdisplay	interfacetui)r   osenvirongetpathjoin
expanduserexistsyamlopenloadgetattr
SafeLoader
isinstancedictstrstriplowerr   )valuehomecfg_path_yaml_iface_frawdispifaces           r   _config_default_interface_earlyr?   t   s    )%a((Ez~~m,, 	Ww||D-88HHw||BG$6$6s$;$;YVVH7>>(## 	"&&&&h111 R!&&w{M4HHbKLb '                   779b))D$%% "--eS)) "ekkmm.A.A.C.Cu.L.L!E   #WLs7   B4F 0D<F DF DBF F$#F$argvzlist[str] | Nonec                     | t           j        dd         } d| v rdS t          j                            d          dk    sd| v rdS t                      d	k    S )
zEarliest TUI decision, usable before argparse/config imports.

    Precedence: explicit ``--cli`` wins (forces classic REPL), then
    ``--tui``/``HERMES_TUI=1``, then ``display.interface`` in config.
    N   z--cliF
HERMES_TUI1z--tuiTr%   )sysr@   r&   r'   r(   r?   r@   s    r   _wants_tui_earlyrG      s]     |x|$u	z~~l##s**goot*,,55    c                      t           j                            d          dk    rd S t                      sd S 	 t          j        d          sd S t          j        dd           d S # t          $ r Y d S w xY w)NHERMES_TUI_NO_EARLY_DISABLErD   rB   sM   [?1003l[?1002l[?1001l[?1000l[?9l[?1006l[?1005l[?1015l[?1016l[?2029l)r&   r'   r(   rG   isattywriteOSError rH   r   _suppress_mouse_residue_earlyrO      s    	z~~344;;  y|| 	F 	G	
 	
 	
 	
 	

    s   A$ A$ $
A21A2c                      t           j                            dd          } t          t           j                            d          pd| v p|                     d                    S )z3Tiny Termux check for pre-import startup shortcuts.PREFIX TERMUX_VERSIONcom.termux/files/usr/data/data/com.termux/)r&   r'   r(   bool
startswithprefixs    r   #_is_termux_startup_environment_fastrZ      s_    Z^^Hb))F

'(( 	7!V+	7566  rH   c                     | dgdgdgfv S )N	--versionz-VversionrN   rF   s    r   _is_termux_fast_version_argvr^      s    [MD6I;777rH   c                  D   t           j        D ]} | st          j                    } t          j                            | dd          }	 t          |d          5 }|D ]}|                                }|                    d          s,|                    d          \  }}}|	                    dd          d	                                                             d
          }|pdc cddd           c S 	 ddd           n# 1 swxY w Y   # t          $ r Y w xY wdS )zARead OpenAI SDK version without importing ``importlib.metadata``.openaiz_version.pyr   r   __version__=#rB   r   z"'N)rE   r)   r&   getcwdr*   r.   r5   rW   	partitionsplitrM   )baseversion_filehandlelinestripped_key_sepr7   s           r   _read_openai_version_fastrn      s      	9;;Dw||D(MBB
	lW555 )" ) )D#zz||H#..}== ! (0(:(:3(?(?%D$!KKQ//288::@@GGE =D(() ) ) ) ) ) ) ) ) ))) ) ) ) ) ) ) ) ) ) ) ) ) ) )  	 	 	H	4s=   DBD&D6DD	D	D	
D
DDc                     ddl m} m} t          j                            t          j                            t          j                            t                    t          j	                            }t          d| d|  d           t          d|            t          dt          j                                        d                     t                      }t          |rd| nd	           d S )
Nr   )__release_date__ra   zHermes Agent v ()	Project: Python: OpenAI SDK: OpenAI SDK: Not installed)
hermes_clirp   ra   r&   r)   abspathr*   dirname__file__pardirprintrE   r]   rf   rn   )rp   ra   project_rootopenai_versions       r   _print_fast_version_infor      s    888888887??27<<0I0I29#U#UVVL	
=;
=
=*:
=
=
=>>>	
$l
$
$%%%	
-S[&&((+
-
-....00N	^
\
)
)
)
)A\]]]]]rH   c                      t           j                            d          dk    rdS t                      sdS t	          t
          j        dd                   sdS t                       dS )zDHandle ``hermes --version`` before config/logging imports on Termux.HERMES_TERMUX_DISABLE_FAST_CLIrD   FrB   NT)r&   r'   r(   rZ   r^   rE   r@   r   rN   rH   r   _try_termux_ultrafast_versionr      sd    	z~~6773>>u.00 u'55 u4rH   )Path)Optional)add_accept_hooks_flag)build_cron_parser)build_gateway_parser)build_profile_parser)build_model_parser)build_setup_parser)build_postinstall_parser)build_whatsapp_parser)build_slack_parser)build_login_parser)build_logout_parser)build_auth_parser)build_status_parser)build_webhook_parser)build_hooks_parser)build_doctor_parser)build_security_parser)build_dump_parser)build_debug_parser)build_backup_parser)build_import_cmd_parser)build_config_parser)build_console_parser)build_version_parser)build_update_parser)build_uninstall_parser)build_dashboard_parser)build_gui_parser)build_logs_parser)build_prompt_size_parser)build_memory_parser)build_acp_parser)build_tools_parser)build_insights_parser)build_skills_parser)build_pairing_parser)build_plugins_parser)build_mcp_parser)build_claw_parsercommand_namec                     t           j                                        s5t          d|  dt           j                   t          j        d           dS dS )a  Exit with a clear error if stdin is not a terminal.

    Interactive TUI commands (hermes tools, hermes setup, hermes model) use
    curses or input() prompts that spin at 100% CPU when stdin is a pipe.
    This guard prevents accidental non-interactive invocation.
    zError: 'hermes z' requires an interactive terminal.
It cannot be run through a pipe or non-interactive subprocess.
Run it directly in your terminal instead.filerB   N)rE   stdinrK   r|   stderrexit)r   s    r   _require_ttyr   5  sk     9 9l 9 9 9 		
 	
 	
 	
 	 rH   c                  6   t           j        dd         d} d}d}dt          dt          ffd}dt          dt          dz  fd}h d	}d
dh}d}|t                    k     r|         }|dk    rn|dk    r ||          rn|dv r&|dz   t                    k     r|dz            } d}|}n|                    d          r!|                    dd          d         } d}|}n~d|vr ||v r|dz   t                    k     r|dz  }nGd|vr>||v r:|dz   t                    k     r$|dz                                d          s|dz  }n|dz  }|t                    k     | &|dk    r ddl}	|		                    d|           sd} d}d}t          j                            dd          }
| !|
rt          |
          j        j        dk    rdS | t          j                            d          sr	 ddlm}  |            dz  }|                                r2|                                                                }|r
|dk    r|} d}n# t*          t,          f$ r Y nw xY w| 3	 ddlm}  ||           }n# t2          $ rI} ||           }|s2t5          d| t           j                   t          j        d           Y d}~n~d}~wt:          $ r<}t5          d| t           j                   t          j        d           Y d}~n:d}~wt<          $ r*}t5          d| dt           j                   Y d}~dS d}~ww xY w|t          j        d<   |dk    rA|A|dz   }t           j        d|         t           j        ||z   d         z   t           _        dS dS dS dS ) z:Pre-parse --profile/-p and set HERMES_HOME before imports.rB   Nr   indexr   c                     	                      dd|           }                     d|dz   |            n# t          $ r Y dS w xY wdS )a"  True once argv reaches `hermes mcp add ... --args <command argv>`.

        ``mcp add --args`` is command-argv passthrough. Flags after that point
        belong to the child MCP command (for example Docker MCP Toolkit's
        ``--profile``), not to Hermes' own profile selector.
        mcpr   addrB   FT)r   
ValueError)r   	mcp_indexr@   s     r   _inside_mcp_add_argsz5_apply_profile_override.<locals>._inside_mcp_add_args[  sa    	

5!U33IJJui!mU3333 	 	 	55	ts   15 
AAnamec                    | dk    rdS t          t          d          rt          j                    dk    rdS t          j                            dd                                          }|r|dk    rdS 	 ddl}t          |                    |          j	                  }n# t          $ r Y dS w xY w|dz  d	z  | z  }	 |                                rt          |          S n# t          $ r Y dS w xY wdS )
a  Resolve `sudo hermes -p <name>` against the invoking user's home.

        `_apply_profile_override()` runs before argparse, so `--run-as-user`
        is not available yet. For sudo invocations, the best available signal
        is SUDO_USER: root is only doing the privileged install/start action,
        while the profile store normally belongs to the user who invoked sudo.
        defaultNgeteuidr   	SUDO_USERrR   rootr   profiles)hasattrr&   r   r'   r(   r5   pwdr   getpwnampw_dirr   is_dirr4   rM   )r   	sudo_userr   r8   	candidates        r   _resolve_sudo_user_profile_envz?_apply_profile_override.<locals>._resolve_sudo_user_profile_envi  s(    94r9%% 	):):4JNN;3399;;	 	I//4	JJJY//677DD 	 	 	44	 9$z1D8		!! &9~~%& 	 	 	44	ts$   4+B   
B.-B.="C! !
C/.C/>   -m-r-s-t-z--model--resume--skills	--oneshot
--provider
--toolsets-c
--continue--z--args>   	--profile-p   z
--profile=rb   -z^[a-z0-9][a-z0-9_-]{0,63}$r   rR   r   HERMES_S6_SUPERVISED_CHILDget_default_hermes_rootactive_profiler   )resolve_profile_envError: r   z"Warning: profile override failed (z), using default)rE   r@   intrV   r4   lenrW   rf   rematchr&   r'   r(   r   parentr   hermes_constantsr   r,   	read_textr5   UnicodeDecodeErrorrM   hermes_cli.profilesr   FileNotFoundErrorr|   r   r   r   r   )profile_nameconsumeprofile_indexr   r   value_flagsoptional_value_flagsiarg_rehermes_home_envr   active_pathr   r   hermes_homeexcstartr@   s                     @r   _apply_profile_overrider   T  s   8ABB<DLGMC D      S S4Z    D  K !,/	A
c$ii--1g$;;(??33A66?%%%!a%#d))*;*;A;LGM>>,'' 	99S!,,Q/LGMc>>c[00QUSYY5F5FFAAsNN+++AD		!!QK**3// " FAAFA5 c$ii--@ GqLLyy6EE 	!LG M jnn]B77O  ',
::F BJNN3O$P$P
	@@@@@@11336FFK!!##  ",,..4466  DI--#'LG"G, 	 	 	D	 	??????--l;;KK  	 	 	88FFK oooCJ7777 	 	 	/C//
3333HQKKKKKKKK 	 	 	JSJJJZ    FFFFF	 %0
=!Q;;=4!A%Ex'#(57?3D3D*EECHHH1  , ;44sC   .AI II#I5 5
L>??KL>2LL>L99L>get_hermes_home)load_hermes_dotenv.env)project_envFr   r   r   r    r!   )managed_scopeHERMES_REDACT_SECRETSsecurityredact_secretsnetwork
force_ipv4Tsetup_loggingc              #   D   K   | ]}|                     d           |V  dS r   NrW   .0r   s     r   	<genexpr>r  >  s3      LLSs8K8KLSLLLLLLrH   rB   rR   >   guiservedesktop	dashboardr  r   mode)apply_ipv4_preferenceforce)datetime)ra   rp   )_prompt_auth_credentials_choice_model_flow_openrouter_model_flow_nous_model_flow_openai_codex_model_flow_xai_oauth_model_flow_qwen_oauth_model_flow_minimax_oauth_model_flow_custom_model_flow_azure_foundry_model_flow_named_custom_model_flow_copilot_model_flow_copilot_acp_model_flow_kimi_model_flow_stepfun_model_flow_bedrock_api_key_model_flow_bedrock_model_flow_vertex_model_flow_api_key_provider_model_flow_anthropic_model_flow_moaenvc                     | pt           j        }t          |                    dd                    }t	          |                    d          pd|v p|                    d                    S )z<Import-safe Termux check for cold-start-sensitive CLI paths.rQ   rR   rS   rT   rU   )r&   r'   r4   r(   rV   rW   )r,  checkrY   s      r   _is_termux_startup_environmentr/  t  so    2:E8R(())F		"## 	7!V+	7566  rH   
common_dirrefc                    	 | dz                       dd          }n# t          $ r Y dS w xY w|                                D ]}|r*|                    d          s|                    d          r/|                    dd	          }t          |          d
k    r:|d	                                         |k    r|d                                         c S dS )zLook up a ref in .git/packed-refs without spawning git.

    packed-refs lines look like ``<sha> <ref>`` with optional ``^<sha>``
    peel lines and ``#``-prefixed comments / ``# pack-refs with:`` header.
    zpacked-refsr   replacer   errorsNrc   ^ rB   r   r   )r   rM   
splitlinesrW   rf   r   r5   )r0  r1  textrj   partss        r   _read_packed_refr;    s    ]*55wy5YY   tt!! $ $ 	ts++ 	ts/C/C 	

3""u::??uQx~~//3668>>#####4s    
++	repo_rootc                 <   | dz  }	 |                                 r|                    dd                                          D ]r}|                    d          \  }}}|                                dk    r?|                                r+| |                                z                                  } ns|}|dz  }|                                rT	 |                    dd                                          }|r||z                                  }n# t          $ r Y nw xY w|dz  }	|	                    dd                                          }
|
                    d	          r|
	                    dd
          d
                                         }||fD ]L}||z  }|                                r1d| d|                    dd                                           c S Mt          ||          }|rd| d| S d| dS d|
 S # t          $ r Y dS w xY w)z9Return a cheap checkout fingerprint without spawning git..gitr   r3  r4  :gitdir	commondirHEADzref:rB   zgit:z:unresolvedz	git:HEAD:N)is_filer   r8  re   r5   resolver,   rM   rW   rf   r;  )r<  git_dirrj   key_r7   r0  commondir_filerel	head_fileheadr1  r   ref_file
packed_shas                  r   _read_git_revision_fingerprintrN    s   & G'?? 	))79)MMXXZZ   $s 3 3Q99;;(**u{{}}*(5;;==8AACCGE 
 ;.  "" 	$..	.RRXXZZ ;")C-!8!8!:!:J   f$	""GI"FFLLNN??6"" 	+**S!$$Q'--//C &z2 i i	$s???$$ ih#hh(:(:GT](:(^(^(d(d(f(fhhhhhi)*c::J 10c00J000 +#****!4!!!   ttsJ   CH AD H 
D# H "D##CH 'H H H 
HHc                      t          t                    } | r| S t          dz  }	 |                                }dt           dt           d|j         d|j         S # t          $ r dt           dt           dcY S w xY w)z=Cheap invalidation key for Termux bundled-skill startup sync.skillszskills:r?  z:missing)rN  PROJECT_ROOTstatra   rp   st_mtime_nsst_sizerM   )git_fp
skills_dirrR  s      r   "_termux_bundled_skills_fingerprintrW    s    +L99F (JB  [[['7[[$:J[[T\[[[ B B BAAA'7AAAAAABs   5A A:9A:c                  *    t                      dz  dz  S )NrP  z.termux_bundled_sync_stampr   rN   rH   r   !_termux_bundled_skills_stamp_pathrY    s    x'*FFFrH   c                     t                      sdS t          j                            d          dk    rdS 	 t	                      } |                     d                                          t                      k    S # t          $ r Y dS w xY w)NTHERMES_TERMUX_FORCE_SKILLS_SYNCrD   r   r   )	r/  r&   r'   r(   rY  r   r5   rW  rM   stamps    r   "_termux_bundled_skills_sync_neededr^    s    )++ t	z~~788C??t133006688<^<`<```   tts   AA= =
B
Bc                      t                      sd S 	 t                      } | j                            dd           |                     t                      dz   d           d S # t          $ r Y d S w xY w)NTparentsexist_ok
r   r   )r/  rY  r   mkdir
write_textrW  rM   r\  s    r   "_mark_termux_bundled_skills_syncedrf    s    )++ 1334$777;==DwWWWWW   s   AA$ $
A21A2c                      t                      rt                      sdS ddlm}   | d           t	                       dS )a  Sync bundled skills, but skip unchanged Termux checkouts cheaply.

    Hashing every bundled skill is safe but expensive on older Android
    storage. The git/ref stamp keeps post-update correctness: a changed
    checkout revision forces one real sync, then later starts skip it.
    Fr   sync_skillsTquiet)r/  r^  tools.skills_syncri  rf  rh  s    r    _sync_bundled_skills_for_startuprm    s[     &'' 0R0T0T u------Kd&(((4rH   c                  h    t                      sdS t          j                            d          dk    S )NTHERMES_TERMUX_PREFETCH_UPDATESrD   )r/  r&   r'   r(   rN   rH   r   $_termux_should_prefetch_update_checkrp    s/    )++ t:>>:;;sBBrH   c                 F   | sdS t          j                    | z
  }|dk     rdS |dk     rt          |dz             dS |dk     rt          |dz             dS |dk     rd	S |d
k     rt          |dz             dS t          j        |                               d          S )zBFormat a timestamp as relative time (e.g., '2h ago', 'yesterday').?<   just now  m agoQ h agoi  	yesterdayi:	 d agoz%Y-%m-%d)_timetimer   r  fromtimestampstrftime)tsdeltas     r   _relative_timer    s     sJLL2Erzzzt||ebj//((((u}}edl##****v~~{v~~eem$$++++!"%%..z:::rH   c                     ddl m} m}m} ddlm} ddl m} |                    dd          } |            }|                    d          }t          |t                    r*|                    d          pd
                                }n,t          |t                    r|
                                }nd}|o||k    }	ddlm}
 h d	}|
                                D ]'}|j        d
k    r|                    |j                   (t#          d |D                       rdS  |             }|                                r	 |                    d                                          D ]}|
                                }|                    d          sd|vr0|                    d          r
|dd         }|                    d          \  }}}|
                                
                    d          }|
                                |v r|r dS n# t.          $ r Y nw xY w	 |
                                D ]4\  }}|j        d
k    r ||          }|                    d          r dS 5n# t.          $ r Y nw xY w |            dz  }|                                rq	 ddl} |j        |                                          }|                    d          }|r" ||          }|                    d          rdS n# t.          $ r Y nw xY wt          |t                    r|                    d          pd
                                }|                    d          pd
                                }|                    d
          pd
                                }|s|s|rdS |	rH	 ddlm}m}  |            }|r" ||          s|                    d          rdS n# t.          $ r Y nw xY wdS )z3Check if at least one inference provider is usable.r   )get_env_pathr   load_config)get_auth_status)DEFAULT_CONFIGmodelrR   r   )PROVIDER_REGISTRY>   OPENAI_API_KEYANTHROPIC_TOKENOPENAI_BASE_URLANTHROPIC_API_KEYOPENROUTER_API_KEYapi_keyc              3   >   K   | ]}t          j        |          V  d S N)r&   getenv)r  vs     r   r  z/_has_any_provider_configured.<locals>.<genexpr>6  s*      
3
3A29Q<<
3
3
3
3
3
3rH   Tr   r   rc   rb   zexport    Nz'"	logged_inz	auth.jsonactive_providerproviderbase_url)read_claude_code_credentialsis_claude_code_token_validrefreshTokenF)hermes_cli.configr  r   r  hermes_cli.authr  r  r(   r2   r3   r5   r4   r  values	auth_typeupdateapi_key_env_varsanyr,   r   r8  rW   re   r   itemsjsonloadsagent.anthropic_adapterr  r  )r  r   r  r  r  _DEFAULT_MODELcfg	model_cfg_model_name_has_hermes_configr  provider_env_varspconfigenv_filerj   rF  rG  valprovider_idstatus	auth_filer  authactivecfg_providercfg_base_urlcfg_api_keyr  r  credss                                 r   _has_any_provider_configuredr    s   LLLLLLLLLL////// 100000#''44N
+--C  I)T""  }}Y//52<<>>	Is	#	# oo''$F)F
 211111   %++-- ? ?	))$$W%=>>>

3
3!2
3
3
333 t |~~H 	 **G*<<GGII 	  	 zz||??3'' 3d????9-- $8D"nnS11Qiikk''..99;;"333344	   	 	 	D	$5$;$;$=$= 	 	 K I--$_[11Fzz+&& tt		      !!K/I 
	KKK4:i113344DXX/00F  (00::k**  4 	 	 	D	 )T"" !j117R>>@@!j117R>>@@ }}Y//52<<>> 	< 	; 	4
  	       
 1022E **5115:YY~5N5N t 	 	 	D	 5sW   ?CH H 
H H $AI. ,I. .
I;:I; AL   
LL+4O" "
O/.O/sessionsc                      st          d           dS 	 ddldgd d  fd}                    |           d         S # t          $ r Y nw xY wt          d           t	                     D ]\  }}|                    d          pd	                                }|                    d
          pd	                                }|p	|p|d         }t          |          dk    r|dd         dz   }t          |                    d                    }|                    dd	          dd         }t          d|dz   dd|dd|dd|            	 	 t          dt                      d                                          }	|	r|	
                                dv rdS t          |	          dz
  }
d|
cxk    rt                     k     rn n |
         d         S t          dt                      d           n@# t          $ r t          d           Y n%t          t          f$ r t                       Y dS w xY w)a  Interactive curses-based session browser with live search filtering.

    Returns the selected session ID, or None if cancelled.
    Uses curses (not simple_term_menu) to avoid the ghost-duplication rendering
    bug in tmux/iTerm when arrow keys are used.
    No sessions found.Nr   c                    |                      d          pd                                }|                      d          pd                                }|                      dd          dd         }t          |                      d                    }| d         dd	         }d
}t          d||z
            }|r|d|         }	n|r|d|         }	n|}	|	d| d|dd|dd| S )z!Format a session row for display.titlerR   previewsourceN   last_activeid   -      <  <10<5r7  )r(   r5   r  max)
smax_xr  r  r  r  sid
fixed_cols
name_widthr   s
             r   _format_rowz+_session_browse_picker.<locals>._format_row  s   UU7^^)r0022EuuY''-24466GUU8R((!,F(})=)=>>KD'#2#,C -JR!344J [j[) {
{+PZPPPPKPPPvPPP3PPPrH   c                    |                                 }||                     d          pd                                 v p||                     d          pd                                 v pT||                     dd                                           v p*||                     d          pd                                 v S )z?Check if a session matches the search query (case-insensitive).r  rR   r  r  r  )r6   r(   )r  queryqs      r   _matchz&_session_browse_picker.<locals>._match  s    AaeeGnn*11333 8y))/R668888dB--///8 x.B55777	rH   c           
      *                        d                                           r                                                                                      dj        d                               dj        d                               dj        d                               dj        dk    rdnj	        d           d}d}dt                    }	 |                                  |                                 \  }}|d
k     s|dk     rS	 |                     ddd           n# j        $ r Y nw xY w|                                  |                                  d S r:d d}j        }                                r|                    d          z  }n5d}j        }                                r|                    d          z  }	 |                     dd||dz
  |           n# j        $ r Y nw xY wd}t)          d||z
            }	ddd|	 dddddddd }
	                                 r                    d          nj        }|                     dd|
|dz
  |           n# j        $ r Y nw xY w|dz
  }t)          |d          }|s7	 d}|                     dd||dz
  j                   nB# j        $ r Y n5w xY w|t-          |          k    rt-          |          dz
  }t)          |d          }||k     r|}n|||z   k    r||z
  dz   }t/          t1          |t3          t-          |          ||z                                 D ]\  }}|dz   }||dz
  k    r n||         }||k    rdnd}| ||dz
            z   }j        }||k    r3j        }                                r|                    d          z  }	 |                     |d||dz
  |           # j        $ r Y w xY w|dz
  }|rPd|dz    dt-          |           d}t-          |          t-                    k     r|d t-                     d!z  }nd"t-                     d}	 |                     |d||dz
                                  r                    d          nj                   n# j        $ r Y nw xY w|                                  |                                 }|j        hv r|r|dz
  t-          |          z  }n|j        hv r|r|dz   t-          |          z  }n|j        d#d$hv r|r||         d%         d<   d S |d&k    rrdt                    }d}d}nd S |j        d'dhv r2r/d d         rfd(D             }nt                    }d}d}nL|t?          d)          k    rsd S d*|cxk    rd+k    r(n n%tA          |          z  fd,D             }d}d}.)-Nr   rB   r            rR   T   (   zTerminal too smallu     Browse sessions — filter: u   █uM     Browse sessions — ↑↓ navigate  Enter select  Type to filter  Esc quitr  r  z   zTitle / Previewr  r  Activer  Srcr  r7  IDz  No sessions match the filter.    → /	 sessionsz (filtered from rr   z  0/
      r        c                 ,    g | ]} |          |S rN   rN   r  r  r  search_texts     r   
<listcomp>zB_session_browse_picker.<locals>._curses_browse.<locals>.<listcomp>E  s*    'V'V'Vavva?U?U'V'V'V'VrH   r      ~   c                 ,    g | ]} |          |S rN   rN   r  s     r   r  zB_session_browse_picker.<locals>._curses_browse.<locals>.<listcomp>O  s*    NNNavva7M7MNNNNrH   )!curs_set
has_colorsstart_coloruse_default_colors	init_pairCOLOR_GREENCOLOR_YELLOW
COLOR_CYANCOLORSCOLOR_WHITElistcleargetmaxyxaddstrerrorrefreshgetchA_BOLD
color_pairaddnstrr  A_DIMr   	enumeraterangeminA_NORMALKEY_UPKEY_DOWN	KEY_ENTERKEY_BACKSPACEordchr)stdscrcursorscroll_offsetfilteredmax_yr  headerheader_attrr  r  
col_headerdim_attrvisible_rowsmsgdraw_ir   yr  arrowrowattrfooter_yfooterrF  r  r  r  cursesresult_holderr  s                           @r   _curses_browsez._session_browse_picker.<locals>._curses_browse  s   OOA  "" X""$$$))+++  F$6;;;  F$7<<<  F$5r:::  ):):AA@RTVWWWFMKH~~HM&%00u199

a,@AAAA!<   NN$$$LLNNNF  	<NkNNNF"(-K((** <#v'8'8';';;lF"(-K((** <#v'8'8';';;NN1aKHHHH|   D 1
 UZ%788
g#4gzgggghgggV[gggaegg
060A0A0C0CU))!,,,  NN1aUQYIIII|   D  %qy"<33   #!?q!S%!)V\JJJJ!<    X..!$X!2 ^^F--(.=<#???(.(=(A%.)H}|/KLL & & ! !	 #QJ	>>!E$QK+,;;E#kk!UQY&?&??%;;#)=D%0022 = $(9(9!(<(< <!"NN1aeaiFFFF%| ! ! ! D! !19 =G&1*GGs8}}GGGF8}}s8}}44"ES]]"E"E"EE<CMM<<<F	NN 	060A0A0C0CU))!,,,    |   D    llnn6=*** >"(1*H!=V_... >"(1*H!=V-r2666 B+3F+;D+Aa(FBYY" &(#'>>!"() V13:::" *&1#2#&6& 6'V'V'V'V'V8'V'V'VHH'+H~~H!"()CHH__[_F3%%%%#%%%%%3s88+KNNNNN8NNNHF$%M[M&sn   D7 7
EE#H   
HH:AJ 
JJ/#K 
K"!K")P
PPA
S 
SSz:
  Browse sessions  (enter number to resume, q to cancel)
r  rR   r  r  2   /   ...r  r  r  r  rB   z>3. <50r  Tz
  Select [1-]: >   r  r   quitz  Invalid selection. Enter 1-z or q to cancel.z/  Invalid input. Enter a number or q to cancel.)r|   r  wrapperr   r  r(   r5   r   r  inputr6   r   r   KeyboardInterruptEOFError)r  r   r   r  r  r  labelr  srcr  idxr  r  r  r  s   `          @@@@r   _session_browse_pickerr/    s     "###tG	Q 	Q 	Q,	 	 	\	& \	& \	& \	& \	& \	& \	& \	& \	&| 	~&&&Q    

HIII(## F F1w%2,,..55##)r0022++AdGu::??#2#J&E$QUU=%9%9::eeHb!!"1"%D1q5DDDuDDDKDDDsDDEEEE	;X;;;<<BBDDC #))++)>>>tc((Q,CC''''#h--'''''}T**Q#h--QQQRRRR 	E 	E 	ECDDDDD!8, 	 	 	GGG44	s6   2A 
AA"A
H .<H + H I	'I	I	r  c                    d}	 ddl m}  |            }|                    | d          }|r|d         d         nd	 |&	 |                                 S # t          $ r Y S w xY wS # t          $ r Y nw xY w	 |&	 |                                 n># t          $ r Y n2w xY wn-# |&	 |                                 w # t          $ r Y w w xY ww xY wdS )z7Look up the most recently-used session ID for a source.Nr   	SessionDBrB   )r  limitr  )hermes_stater2  search_sessionscloser   )r  dbr2  r  s       r   _resolve_last_sessionr8  u  sE   	B******Y[[%%V1%==$,6x{4  $6 >



        >



    2>



    
 4sk   8A&  A
A"!A"&
A30B  2A33B  :B 
BB C
$B98C
9
CC
CC
cmdbackendvia_sudoc                     	 t          j        | ddd          S # t           j        $ rC |rd| n|}t          d| d| dt          j                   t	          j        d	           Y d
S w xY w)zRun a container inspect probe, returning the CompletedProcess.

    Catches TimeoutExpired specifically for a human-readable message;
    all other exceptions propagate naturally.
    Tr   capture_outputr9  timeoutsudo zError: timed out waiting for z to respond.
The z+ daemon may be unresponsive or starting up.r   rB   N)
subprocessrunTimeoutExpiredr|   rE   r   r   )r9  r:  r;  r,  s       r   _probe_containerrD    s    	~c$T2NNNN$   %-:!!!!7HE H HH H H	
 	
 	
 	

 	s    AA,+A,container_infocli_argsc                 L   | d         }| d         }| d         }| d         }t          j        |          }|s3t          d| dt          j                   t          j        d           d	}t          |d
dd|g|          }|j        dk    rt          j        d          }|rt          |d|d
dd|g|d          }	|	j        dk    rjt          d| d| d| d| dt          j	        dd           d| dd
                    |           t          j                   t          j        d           nKt          d| d| dd
                    |           t          j                   t          j        d           t          j                                        }
|
rdgndg}g }dD ]?}t          j                            |          }|r|                    d | d!| g           @|r|d|gn|g}|d"gz   |z   d#|gz   |z   ||gz   |z   }t          j        |d         |           d	S )$a  Replace the current process with a command inside the managed container.

    Probes whether sudo is needed (rootful containers), then os.execvp
    into the container. On success the Python process is replaced entirely
    and the container's exit code becomes the process exit code (OS semantics).
    On failure, OSError propagates naturally.

    Args:
        container_info: dict with backend, container_name, exec_user, hermes_bin
        cli_args: the original CLI arguments (everything after 'hermes')
    r:  container_name	exec_user
hermes_binr   z. not found on PATH. Cannot route to container.r   rB   Ninspectz--formatokr   sudo-nT)r;  zError: container 'z' not found via zL.

The container is likely running as root. Your user cannot see it
because z7 uses per-user namespaces. Grant passwordless
sudo for u    — the -n (non-interactive) flag is required
because a password prompt would hang or break piped commands.

On NixOS:

  security.sudo.extraRules = [{
    users = [ "USERz	your-userz"" ];
    commands = [{ command = "z<"; options = [ "NOPASSWD" ]; }];
  }];

Or run: sudo hermes r7  z<.
The container may be running under root. Try: sudo hermes z-it-i)TERM	COLORTERMLANGLC_ALL-erb   execz-u)shutilwhichr|   rE   r   r   rD  
returncoder&   r  r*   r   rK   r'   r(   extendexecvp)rE  rF  r:  rH  rI  rJ  runtime	sudo_pathprobeprobe2is_tty	tty_flags	env_flagsvarr  
cmd_prefixexec_cmds                    r   _exec_in_containerrf    s'    Y'G#$45N{+I-Jl7##G MgMMM	
 	
 	
 	
 	
 I	)Z~> E 1L((	  	%D'9j$W  F
  A%%@ @ @ @ @  '@ @ !(	@ @ ')i&D&D@ @ 6=@ @ ,/88H+=+=@ @ !   $ b^ b bW b bMPXXV^M_M_b bZ   
 HQKKKYF!-vII6 5 5jnnS!! 	5dsNNSNN3444/8G)T7++wiJ(	
	 
	 		
 :
&	' 	  Ihqk8$$$$$rH   
name_or_idc                 8   	 ddl m}  |            }|                    |           }d}|r	|d         }n|                    |           }|r)	 |                    |          p|}n# t
          $ r Y nw xY w|                                 |S # t
          $ r Y nw xY wdS )aX  Resolve a session name (title) or ID to a session ID.

    - If it looks like a session ID (contains underscore + hex), try direct lookup first.
    - Otherwise, treat it as a title and use resolve_session_by_title (auto-latest).
    - Falls back to the other method if the first doesn't match.
    - If the resolved session is a compression root, follow the chain forward
      to the latest continuation. Users who remember the old root ID (e.g.
      from an exit summary printed before the bug fix, or from notes) get
      resumed at the live tip instead of a stale parent with no messages.
    r   r1  Nr  )r4  r2  get_sessionresolve_session_by_titleget_compression_tipr   r6  )rg  r2  r7  sessionresolved_ids        r   _resolve_session_by_name_or_idrn    s    ******Y[[ ..,,%) 	B!$-KK 55jAAK 	 44[AAP[    	


   4s6   A	B
 A$ #B
 $
A1.B
 0A11B
 

BBr)   c                    | sd S 	 t          j        t          |                               d                    }t	          |                    d          pd                                          }|pd S # t          $ r Y d S w xY w)Nr   r   
session_idrR   )r  r  r   r   r4   r(   r5   r   )r)   datar  s      r   _read_tui_active_session_filerr     s     tz$t**...@@AA$((<((.B//5577{d   tts   A.A5 5
BBrp  active_session_filec                 &   t          |          p| pt          d          }|sdS d}	 ddlm}  |            }|                    |          }|s	 ||                                 dS dS |                    |          }t          |                    d          pd          }|dk    r	 ||                                 dS dS t          |                    d          pd          }t          |                    d          pd          }	t          |                    d	          pd          }
t          |                    d
          pd          }t          |                    d          pd          }||	z   |
z   |z   |z   }n)# t          $ r Y ||                                 dS dS w xY w	 ||                                 n# ||                                 w w xY wt                       t          d           t          d|            |rt          d| d           t                       t          d|            |rt          d|            t          d|            t          d| d| d|	 d|
|z    d| d           dS )z/Print a shell-visible epilogue after TUI exits.r%   r  Nr   r1  message_countinput_tokensoutput_tokenscache_read_tokenscache_write_tokensreasoning_tokenszResume this session with:z  hermes --tui --resume z  hermes --tui -c ""zSession:        zTitle:          zMessages:       zTokens:         z (in z, out z, cache z, reasoning rr   )rr  r8  r4  r2  ri  r6  get_session_titler   r(   r   r|   )rp  rs  targetr7  r2  rl  r  rv  rw  rx  ry  rz  r{  total_tokenss                 r   _print_tui_exit_summaryr  +  se   
 	&&9:: 	/	/ ... 
  	B******Y[[..(( 	* >HHJJJJJ >' $$V,,GKK88=A>>A  >HHJJJJJ > 7;;~66;!<<GKK88=A>>,? @ @ EAFF -A!B!B!GaHHw{{+=>>C!DD  !! 	 	    >HHJJJJJ > 	 >HHJJJ >HHJJJJ  
GGG	
%&&&	
-V
-
-... .,E,,,---	GGG	
%V
%
%&&& *((()))	
,]
,
,---		Y	Y 	Y*	Y 	Y2?	Y 	Y"%77	Y 	YEU	Y 	Y 	Y    s6   'F +?F CF G	 
F.G	 -F..G	 	G"ideallyInertpeerdirc                     | dz                                   r:| dz                                   s#| j        dz                                   r| j        S | S )u6  Return the npm workspace root for *dir*.

    In a workspace checkout the single ``package-lock.json`` and hoisted
    ``node_modules/`` live at the workspace root (the parent of the
    sub-package directory).  Heuristic: if *dir* has a ``package.json``
    but **no** ``package-lock.json``, and its **parent** has a
    ``package-lock.json``, the parent is the workspace root.
    Otherwise *dir* itself is the root (standalone project or
    prebuilt-bundle layout).

    Used by ``_tui_need_npm_install``, ``_make_tui_argv``, and
    ``_build_web_ui`` so that lockfile/node_modules resolution and
    ``npm install`` cwd stay consistent — a single helper prevents
    the checks from diverging if someone accidentally creates a
    sub-package lockfile (e.g. running ``npm install`` in the wrong
    directory).
    package.jsonpackage-lock.json)rC  r   )r  s    r   _workspace_rootr  t  sd    & 
~	&&((**3355 Z--6688
 zJrH   include_child_workspacesr  .c                N   t          |           }|| k    r| dfS 	 |                     |                                          }n# t          $ r |dfcY S w xY wd|g}|r| dz  }|                                rt          |                                          D ]i}|                                rS|dz                                  r<|                    d|                    |                                          g           j|	                    d           |t          |          fS )zJReturn Termux-only ``(cwd, npm_args)`` for installing deps for *dir* only.rN   --workspacepackagesr  z--include-workspace-root=false)r  relative_toas_posixr   r   sortediterdirrC  rZ  appendtuple)r  r  ws_root	workspaceworkspace_argspackages_dirchilds          r   !_termux_workspace_install_contextr    sT    c""G#~~BwOOG,,5577		   { "/	 :N Z'   	 4 4 6 677  <<>> u~'=&F&F&H&H "))&(9(9'(B(B(K(K(M(MN   :;;;E.))))s   'A AAr   c                 L   | dz  dz  }t          |           }|dz  }|                                r|                                sdS |dz  dz  dz  dz  }|                                sd	S |                                sdS |dz  d
z  }|                                sd	S 	 t          j        |                    d                                        d          pi }t          j        |                    d                                        d          pi }nV# t          t          t          j        f$ r7 |	                                j
        |	                                j
        k    cY S w xY wdt          dt          fd}|                                D ]\  }	}
|	st          |
t                    s|	|vr.|
                    d          s|
                    d          rM d	S t          ||	         t                    r! ||
           |||	                   k    r d	S dS )uQ  True when @hermes/ink is missing or node_modules is behind package-lock.json.

    Prebuilt bundle mode: when ``dist/entry.js`` exists and there is no
    ``package-lock.json`` (nix install layout only ships ``dist/`` +
    ``package.json``), skip reinstall entirely — the bundle is self-contained
    and there is nothing to install.

    With npm workspaces the single ``package-lock.json`` and the hoisted
    ``node_modules/`` live at the workspace root (the parent of the
    ``ui-tui/`` directory).  The lockfile / ink / marker checks use that
    workspace root; only the prebuilt-bundle sentinel stays relative to
    *root* (``ui-tui/dist/entry.js``).

    Compares ``package-lock.json`` against ``node_modules/.package-lock.json``
    (npm's hidden lockfile) by **content**, not mtime: git checkouts and npm
    rewrites can bump the root lockfile's timestamp even when installed deps
    already match, which used to trigger a spurious "Installing TUI
    dependencies" on every launch.

    For each entry in the root lock's ``packages`` map:
      - missing from hidden lock → reinstall (unless the entry is marked
        ``optional`` or ``peer``, which npm may intentionally skip per platform)
      - present but with differing fields (excluding npm-written runtime
        annotations like ``ideallyInert``) → reinstall

    Extra entries that exist only in the hidden lock are ignored — stale
    transitives left over from a removed dependency don't break runtime and
    we'd rather not force a reinstall for them. Falls back to mtime
    comparison if either lockfile is unparseable.
    distentry.jsr  Fnode_modulesz@hermesinkr  Tz.package-lock.jsonr   r   r  pkgr   c                 >    d |                                  D             S )Nc                 ,    i | ]\  }}|t           v||S rN   )_NPM_LOCK_RUNTIME_KEYSr  kr  s      r   
<dictcomp>z=_tui_need_npm_install.<locals>.comparable.<locals>.<dictcomp>  s)    PPPA9O0O0O10O0O0OrH   )r  )r  s    r   
comparablez)_tui_need_npm_install.<locals>.comparable  s    PPPPPPrH   optionalr  )r  rC  r  r  r   r(   rM   r   JSONDecodeErrorrR  st_mtimer3   r  r2   )r   entryr  lockr  markerwanted	installedr  r   r  s              r   _tui_need_npm_installr    sa   B 6MJ&Ed##G((D}} t||~~ u
N
"Y
.
6
GC;;== t<<>> u~%(<<F>> t
=DNNGN<<==AA*MMSQSJv///AABBFFzRRXVX		')=> = = =yy{{#fkkmm&<<<<<=Q Q Q Q Q Q \\^^  	c 	#t$$ 	y  wwz"" cggfoo 44iot,, 	CJJdOE
 E
 2
 2
 445s    A:D AE.-E.)r-  zpackages/hermes-ink/src)	r  r  ztsconfig.jsonztsconfig.build.jsonzbabel.compiler.config.cjszscripts/build.mjsz packages/hermes-ink/package.jsonzpackages/hermes-ink/index.jsz!packages/hermes-ink/text-input.js>   .cjs.mjs.js.ts.jsx.tsx.jsonc              #     K   t           D ]}| |z  }|                                r|V   t          D ]Z}| |z  }|                                s|                    d          D ](}|                                r|j        t          v r|V  )[dS )z?Yield source/config files that affect ``ui-tui/dist/entry.js``.*N)_TUI_BUILD_INPUT_FILESrC  _TUI_BUILD_INPUT_DIRSr   rglobsuffix_TUI_BUILD_INPUT_SUFFIXES)r   rI  r)   rg   s       r   _iter_tui_build_inputsr    s      %  cz<<>> 	JJJ$  cz{{}} 	JJsOO 	 	D||~~ $+1J"J"J


		 rH   c                    t           j                            d          pd                                                                }|dv rdS | dz  dz  }	 |                                j        }n# t          $ r Y dS w xY wt          |           D ]5}	 |                                j        |k    r dS $# t          $ r Y  dS w xY wdS )a  True when ``dist/entry.js`` is missing or older than TUI inputs.

    The TUI bundle is self-contained. Rebuilding it on every launch adds a
    visible cold-start tax on slow Termux CPUs, while a simple mtime freshness
    check still rebuilds immediately after source updates, dependency updates,
    or local edits. Set ``HERMES_TUI_FORCE_BUILD=1`` to force the old behaviour.
    HERMES_TUI_FORCE_BUILDrR   >   rD   onyestrueTr  r  F)	r&   r'   r(   r5   r6   rR  r  rM   r  )r   r  r  output_mtimer)   s        r   _tui_need_rebuildr     s     Z^^455;BBDDJJLLE***t6MJ&Ezz||,   tt 't,,  	yy{{#l22tt 3 	 	 	444	5s$   A/ /
A=<A=B33
CCc            	         t          j        d          rt          j        d          rdS t          j                            d          rdS t
          dz  dz  dz  } |                                 sdS t          j                            d          p"t          t          j	                    d	z            }	 t          j        d
dd|  dgi t          j        d|iddddd          }n# t          t          j        f$ r Y dS w xY wt          j                            dd                              t          j                  }g }|j        pd                                }|r9|                    t          |                                          j                   |                    t          |          dz  dz  t          j	                    dz  dz  g           |D ]?}t          |          }|                                r||vr|                    d|           @t          j                            |          t          j        d<   dS )uz  Make sure `node` + `npm` are on PATH for the TUI.

    If either is missing and scripts/lib/node-bootstrap.sh is available, source
    it and call `ensure_node` (fnm/nvm/proto/brew/bundled cascade). After
    install, capture the resolved node binary path from the bash subprocess
    and prepend its directory to os.environ["PATH"] so shutil.which finds the
    new binaries in this Python process — regardless of which version manager
    was used (nvm, fnm, proto, brew, or the bundled fallback).

    Idempotent no-op when node+npm are already discoverable. Set
    ``HERMES_SKIP_NODE_BOOTSTRAP=1`` to disable auto-install.
    nodenpmNHERMES_SKIP_NODE_BOOTSTRAPscriptslibznode-bootstrap.shr   r   bashr   zsource "z+" >&2 && ensure_node >&2 && command -v nodeTr   r3  F)r,  r>  r9  r   r5  r.  PATHrR   binz.localr   )rW  rX  r&   r'   r(   rQ  rC  r4   r   r8   rA  rB  rM   SubprocessErrorrf   pathsepstdoutr5   r  rD  r   rZ  r   insertr*   )helperr   resultr:  extrasresolvedextrar  s           r   _ensure_tui_noder  ;  s<    |F U 3 3 	z~~233 I%-0CCF>> *..//O3ty{{Y7N3O3OK N6NNN
 ;2::}k::
 
 
 Z/0    JNN62&&,,RZ88EF#**,,H 7d8nn,,..5666
MM4$$v-5ty{{X7MPU7UVWWW  JJ<<>> 	aunnLLA//BJvs   52C( (DDhermes_cli_dirc                 x    | t          t                    j        } | dz  dz  }|                                r|ndS )z3Find a pre-built TUI entry.js bundled in the wheel.Ntui_distr  )r   rz   r   rC  )r  bundleds     r   _find_bundled_tuir  v  s>    h.z)J6Goo''177T1rH   tui_dirc                     t          j        d          }|r| j        dz                                  sdS 	 t	          j        |dd| j        gt          | j                  ddd           n# t          $ r Y dS w xY w| 	                                S )uJ  Try to restore a missing ``ui-tui/`` from git, returning True on success.

    On Windows an antivirus / NTFS filter driver can leave tracked ``ui-tui/``
    files deleted in the working tree after ``hermes update`` (HEAD stays
    intact; the files just vanish — see issue #49145). Those files are tracked,
    so ``git restore`` puts them back deterministically. Best-effort: returns
    False (rather than raising) when git is unavailable, this isn't a checkout,
    or the restore leaves the directory still missing — the caller then prints
    the manual-recovery message.
    gitr>  Frestorer   Tcwdr>  r9  r.  )
rW  rX  r   r,   rA  rB  r   r4   rM   r   )r  r  s     r   _restore_tui_workspacer  ~  s     ,u

C w~.6688 u	)T7<0GN##	
 	
 	
 	
 	
    uu>>s   4A+ +
A98A9c                    |                                  rdS t          |           r3t          j                            d          st          d|             dS t          d|  dt          j                   t          j        d           dS )a  Ensure ``ui-tui/`` exists before any npm/node subprocess uses it as cwd.

    Without this, a missing workspace falls through to ``subprocess.run(...,
    cwd=<missing ui-tui>)``, which crashes with ``NotADirectoryError``
    (``WinError 267`` on Windows) instead of a usable message (#49145). We
    first try to self-heal via ``git restore``; only if that can't recover the
    directory do we abort with concrete manual-recovery steps.
    NHERMES_QUIETz Restored missing TUI workspace: zSError: the TUI workspace is missing from this Hermes checkout.
Expected directory: a/  
This usually means `hermes update` left tracked ui-tui files deleted.
Recovery:
  1. From the Hermes checkout, run `git restore -- ui-tui`
  2. Run `npm install --silent --no-fund --no-audit --progress=false`
  3. Retry `hermes --tui`
If the checkout is still inconsistent, run `hermes update --force`.r   rB   )	r   r  r&   r'   r(   r|   rE   r   r   )r  s    r   _ensure_tui_workspacer    s     ~~ g&& z~~n-- 	@>W>>???		N&	N 	N 	N Z
 
 
 
 HQKKKKKrH   tui_devc                 T	   t                       dt          dt          fd}t          j                            d          }|r5|r3t          d| dt          j                   t          j        d           |st          |            |s|rNt          |          }|d	z  d
z                                  r% |d          }|dt          |d	z  d
z            g|fS t                      }|$ |d          }|dt          |          g|j        fS d}t                      }d}	|r|st          |           }	|o| o|	 }
|
sht!          |           rX |d          }t          j                            d          st          d           t#          |           }|| k    rdnd}|rt%          | d          \  }}t'          j        |dg|ddddt          |          t&          j        t&          j        dddi t          j        ddi          }|j        d k    r|j        pd! d"|j        pd!                                 }d"                    |                                d#d                   }t          d$           |rt          |           t          j        d           d}|r	 |d          }| d%z  d&z  }t'          j        |d'd(gt          |          dddd)          }|j        d k    r|j        pd! |j        pd!                                 }d"                    |                                d#d                   }t          d*           |rt          |           t          j        d           | d+z  d,z  d-z  }|                                rt          |          d.g| fS |d/g| fS d}|r|p|	}|r |d          }t'          j        |d'd(gt          |           dddd)          }|j        d k    r|j        pd! |j        pd!                                 }d"                    |                                d#d                   }t          d0           |rt          |           t          j        d            |d          }|dt          | d	z  d
z            g| fS )1uL   TUI: --dev → tsx src; else node dist (HERMES_TUI_DIR prebuilt or esbuild).r  r   c                    | dk    rat           j                            d          }|r@t           j                            |          r!t          j        |t           j                  r|S t          j        |           }|s=| dk    r7	 ddl	m
}  |d          rt          j        d          }n# t          $ r Y nw xY w|s&t          |  d           t          j        d           |S )Nr  HERMES_NODEr   ensure_dependencyu.    not found — install Node.js to use the TUI.rB   )r&   r'   r(   r)   isfileaccessX_OKrW  rX  hermes_cli.dep_ensurer  r   r|   rE   r   )r  env_noder)   r  s       r   	_node_binz!_make_tui_argv.<locals>._node_bin  s    &==z~~m44H  BGNN844  8RW9U9U  |C   	vCCCCCC$$V,, 0!<//D    	SHHHIIIHQKKKs   %B+ +
B87B8HERMES_TUI_DIRz1Error: --dev is incompatible with HERMES_TUI_DIR=z
The prebuilt TUI has no source code to hot-reload.
Unset HERMES_TUI_DIR (e.g. `unset HERMES_TUI_DIR`) to use --dev from a checkout.r   rB   r  r  r  z--expose-gcNFr  r  u   Installing TUI dependencies…rN   )r  ui-tuiTr  install--silent	--no-fund
--no-audit--progress=falser   r3  CIrD   r  r  r   r9  r   r5  r,  r   rR   rc  znpm install failed.r  z
hermes-inkrB  build)r  r>  r9  r   r5  zTUI dev prebuild failed.r  z.bintsxzsrc/entry.tsxr   zTUI build failed.)r  r4   r&   r'   r(   r|   rE   r   r   r  r   rC  r  r   r/  r  r  r  r  rA  rB  PIPErY  r  r5   r*   r8  r,   )r  r  r  ext_dirpr  r  did_installtermux_startuptermux_need_rebuild$skip_install_for_fresh_termux_bundler  npm_cwdnpm_workspace_argsr  combinedr  ink_dirr  should_builds                       r   _make_tui_argvr    s   s s    & jnn-..G 7 ` ` ` ` 		
 	
 	
 	
 	 'g&&&  G 	NWAF
Z'0022 N y((mSVj1H-I-IJAMM $%%9V$$D-W6FF K355N 9g 9/88 	Bw;B/B+B ) 1-!'**- iz~~n-- 	42333!'** 5<w4F4FbbLe 	*K)-+ + +'G'  $ 	
   # G??)2:)tS))!
 
 
$ !! --2FF1D"FFLLNNHii 3 3 5 5cdd ;<<G'((( gHQKKK ' iJ&5%!G
 
 
 !! --2Dv}/BDDJJLLHii 3 3 5 5cdd ;<<G,--- gHQKKK&/%7::<< 	8HHo.77W~w&&
 L :"9&9 i%!G
 
 
 !! --2Dv}/BDDJJLLHii 3 3 5 5cdd ;<<G%&&& gHQKKK9VD-Wv%5
%B!C!CDgMMrH   toolsetsc                    	 ddl m}  ||           pg S # t          t          f$ r | sg cY S t	          | t
                    r| gn| }t	          |t          t          f          s|g}g }|D ]~}t	          |t
                    r3|                    d |	                    d          D                        J|
                    t          |                                                     d |D             cY S w xY w)zCNormalize argparse/Fire-style toolset input for the TUI subprocess.r   )_normalize_toolsetsc              3   >   K   | ]}|                                 V  d S r  r5   r  parts     r   r  z*_normalize_tui_toolsets.<locals>.<genexpr>r  s*      !K!K4$**,,!K!K!K!K!K!KrH   ,c                     g | ]}||S rN   rN   )r  items     r   r  z+_normalize_tui_toolsets.<locals>.<listcomp>v  s    444t4444rH   )hermes_cli.oneshotr  AttributeErrorr   r2   r4   r  r  rZ  rf   r  r5   )r  r  	raw_items
normalizedr  s        r   _normalize_tui_toolsetsr  a  s.   5::::::""8,,22K( 5 5 5 	III",Xs";";IXJJ	)dE]33 	$"I "
 	5 	5D$$$ 5!!!K!K4::c??!K!K!KKKKK!!#d))//"3"34444444444445s    C7CC76C7c                  f   d} | D ]}	 t          |dd          5 }|                                                                }ddd           n# 1 swxY w Y   n# t          t          f$ r Y gw xY w|dk    r dS |sw	 t          |          }n# t          $ r Y w xY w|dk    r|dk    r dS |c S dS )	u_  Return the container memory limit in bytes, or None if unconstrained.

    Node's V8 heap is NOT cgroup-aware: with a flat ``--max-old-space-size=8192``
    it happily grows the heap toward 8GB regardless of the container's real
    memory limit.  In a Docker/k8s container capped below ~9-10GB, the cgroup
    OOM-killer SIGKILLs Node before V8's own heap monitor ever fires — which
    runs no JS handler, writes no ``[tui-parent]`` breadcrumb, and the user
    sees only a bare gateway ``stdin EOF``.  Reading the real cgroup limit lets
    us size the heap cap below it so V8 GCs/exits gracefully instead of being
    reaped silently.

    Checks cgroup v2 (``/sys/fs/cgroup/memory.max``) then v1
    (``/sys/fs/cgroup/memory/memory.limit_in_bytes``).  A literal ``max`` (v2)
    or the v1 "unlimited" sentinel (a huge near-INT64 value) means no limit.
    )z/sys/fs/cgroup/memory.maxz+/sys/fs/cgroup/memory/memory.limit_in_bytesrr   r   Nr  r   l           )r.   readr5   rM   r   r   )
candidatesr)   fr<   r3  s        r   _read_cgroup_memory_limitr   y  s@    J   	dC'222 'affhhnn&&' ' ' ' ' ' ' ' ' ' ' ' ' ' '$ 	 	 	H	%<<44 	 	HHEE 	 	 	H	A:: W444sF   A'AAA	AA	AA-,A-=B
BB    
default_mbc                     t                      }|s| S |dz  }t          |dz            }|| k    r| S |dk    rt          d|          n|S )u  Pick a V8 ``--max-old-space-size`` (MB) that fits the container.

    Returns ``default_mb`` (8192) when unconstrained or when the box is large
    enough that 8GB fits.  In a memory-limited container, returns ~75% of the
    cgroup limit so the heap + non-heap RSS stays under the cgroup ceiling,
    clamped to a sane floor (1536MB — below this V8 GC-thrashes and the TUI
    is barely usable).  Never exceeds ``default_mb``.
       g      ?i   i   )r   r   r  )r"  r3  limit_mbsizeds       r   _resolve_tui_heap_mbr'    sj     &''E %H 4  E
  ($3tUE9rH   resume_session_idr  r  rP  verboserk  r  imageworktreecheckpointspass_session_id	max_turnsaccept_hooksc                 	   t           dz  }ddl}t          j                                        }	 ddlm}  ||           n,# t          $ r t          	                    dd           Y nw xY w|
                    d	d
          \  }}t          j        |           ||d<   t          j                            dt          t                               |d<   |                    dt          j                   |                    dt          j                               |                    d|rdnd           d}|
r	 ddlm}m}m}m}  |            }|r ||            |            }n7# t          $ r*}t/          d| t          j                   d}Y d}~nd}~ww xY w|st          j        d           |d         |d<   |d         |d<   |r
||d<   ||d<   |r
||d<   ||d<   t5          |          }|rd                    |          |d<   |rt9          |t:          t<          f          rag }|D ]A}|                    d t          |                               d          D                        B|rd                    |          |d <   n(t          |          !                                }|r||d <   |r||d!<   |	r|	|d"<   |rd#|d$<   |rd#|d%<   |t          |          |d&<   |rd'|d(<   n|rd)|d(<   |rd#|d*<   |                    d+d,                                           } tE          d- | D                       s$| #                    d.tI                                  d/                    |           |d+<   |%                    d0d           | r| |d0<   tM          ||          \  }!}"d}#	 	 tO          j(        |!t          |"          |1          }#n# tR          $ r d2}#Y nw xY w|#d3v rtU          | |           	 t          j+        |           n# tX          $ r Y nw xY w|r	  ||           n[# t          $ r Y nOw xY wnJ# 	 t          j+        |           n# tX          $ r Y nw xY w|r	  ||           w # t          $ r Y w w xY ww xY w|#d4k    r?dd5l-m.}$ t/                       t/          d6           t/                        |$d7gd89           t          j        |#           dS ):z%Replace current process with the TUI.r  r   N)apply_terminal_config_to_envr,  z5Failed to apply terminal config bridge for TUI launchTexc_infozhermes-tui-active-session-r  )rY   r  HERMES_TUI_ACTIVE_SESSION_FILEHERMES_PYTHON_SRC_ROOTHERMES_PYTHON
HERMES_CWDNODE_ENVdevelopment
production)_cleanup_worktree_git_repo_root_prune_stale_worktrees_setup_worktreeu#   ✗ Failed to create TUI worktree: r   rB   r)   TERMINAL_CWDHERMES_MODELHERMES_INFERENCE_MODELHERMES_TUI_PROVIDERHERMES_INFERENCE_PROVIDERr  HERMES_TUI_TOOLSETSc              3   f   K   | ],}|                                 |                                 V  -d S r  r  r  s     r   r  z_launch_tui.<locals>.<genexpr>  sK       ! !%)TZZ\\!JJLL! ! ! ! ! !rH   HERMES_TUI_SKILLSHERMES_TUI_QUERYHERMES_TUI_IMAGErD   HERMES_TUI_CHECKPOINTSHERMES_TUI_PASS_SESSION_IDHERMES_TUI_MAX_TURNSr)  HERMES_TUI_TOOL_PROGRESSoffHERMES_ACCEPT_HOOKSNODE_OPTIONSrR   c              3   @   K   | ]}|                     d           V  dS )--max-old-space-size=Nr
  )r  ts     r   r  z_launch_tui.<locals>.<genexpr>3  s/      FFq||344FFFFFFrH   rR  r7  HERMES_TUI_RESUMEr  r,     >   r   rV  *   relaunchu   ⚕ Launching update...r  F)preserve_inherited)/rQ  tempfiler&   r'   copyr  r1  r   loggerdebugmkstempr6  r(   r4   
setdefaultrE   
executablerd   r   r<  r=  r>  r?  r|   r   r   r  r*   r2   r  r  rZ  rf   r5   r  r  r'  popr  rA  callr*  r  unlinkrM   hermes_cli.relaunchrY  )%r(  r  r  r  r  rP  r)  rk  r  r*  r+  r,  r-  r.  r/  r  r[  r,  r1  active_session_fdrs  wt_infor<  r=  r>  r?  repor   tui_toolsets	flattenedr  r7   _tokensr@   r  coderY  s%                                        r   _launch_tuirm    s   $ X%GOOO
*//

C]BBBBBB$$----- ] ] ]LW[\\\\\]-5-=-=+G .> . .** H,?C()$&JNN #l"3"3% %C ! NN?CN333NN<---NN:I}}\JJJG .	            ">##D -&&t,,,%o''GG 	 	 	===CJOOOOGGGGGG	  	HQKKK#FOL%foN .#N(-$% 4%-!"+3'(*844L <%(XXl%;%;!" 1ftUm,, 	1I     ! !-0YY__S-A-A! ! !      ?+.88I+>+>'(KK%%''E 1+0'( ("' ("' ,(+$% 0,/()&))nn"# 0*3&''	 0*/&' )%(!" ggnb))//11GFFgFFFFF IG/C/E/EGGHHH((7++C GG&&& 5#4 w00ID#D	?4SXX3???DD  	 	 	DDD	 8#$57JKKK	I)**** 	 	 	D	 	!!'****   			I)**** 	 	 	D	 	!!'****   	 rzz000000'((((6666HTNNNNNs   A &A*)A*;-E) )
F3 FF+$O P< OP< OP< 7P 
PPP+ +
P87P8<R>QR
Q RQ  R&Q21R2
Q?<R>Q??Rc                      t           j                            d          rdS 	 ddlm}   |             t           j        d<   dS # t
          $ r Y dS w xY w)uM  Pin the active kanban board into ``HERMES_KANBAN_BOARD`` for the chat session.

    Without this, in-process tools (``kanban_*``) and shelled-out CLI calls
    (``hermes kanban …``) resolve the board on different paths: the env-pin if
    set, otherwise the global ``<root>/kanban/current`` file. A concurrent
    ``hermes kanban boards switch`` from another session can flip the file
    mid-turn, so the same chat sees its tool calls hit board A while its shell
    calls hit board B (#20074). Pinning at chat boot mirrors what the
    dispatcher already does for spawned workers.
    HERMES_KANBAN_BOARDNr   get_current_board)r&   r'   r(   hermes_cli.kanban_dbrq  r   rp  s    r   _pin_kanban_board_envrs  e  su     
z~~+,, ::::::,=,=,?,?
()))   s   A 
AAc                  N    	 ddl m}   | d           dS # t          $ r Y dS w xY w)u+  Seed ``~/.hermes/skills/`` with the bundled skill library on first launch.

    Called from any CLI entrypoint that the user might use as their first
    interaction with Hermes — chat, dashboard (the desktop GUI's backend),
    and gateway. The skills_sync module is manifest-based and idempotent:
    skipped skills cost ~milliseconds, so calling this repeatedly is fine.

    Failures are swallowed because skills are an enhancement, not a hard
    dependency. Hermes still functions without them; the user just sees an
    empty skills library.
    r   rh  Trj  N)rl  ri  r   rh  s    r   _sync_bundled_skills_quietlyru  z  sT    111111$   s    
$$c                    t          | dd          rdS t          | dd          s#t          j                            d          dk    rdS 	 ddlm}  |                                d	i           pi                     d
d          }t          |t                    o)|                                	                                dk    S # t          $ r Y dS w xY w)u  Decide whether to launch the TUI for a chat/bare invocation.

    Precedence (highest first):
      1. ``--cli`` flag         → always classic REPL
      2. ``--tui`` flag / ``HERMES_TUI=1`` → always TUI
      3. ``display.interface`` config value ("cli" | "tui")
      4. default → classic REPL

    Explicit flags always win over config so muscle memory and scripts keep
    working regardless of the configured default.
    r   Fr%   rC   rD   Tr   r  r#   r$   )r0   r&   r'   r(   r  r  r2   r4   r5   r6   r   )argsr  r>   s      r   _resolve_use_tuiry    s     tUE"" utUE"" bjnn\&B&Bc&I&It111111""9b117R<<[%PP%%%H%++--*=*=*?*?5*HH   uus   A8C 
CCc                    t          |           }t          | dd          }|rt          | dd          st          |t                    rPt	          |          }|r|| _        nt          d| d           t          d           t          j        d           ne|rdnd	}t          |
          }|s|dk    rt          d	
          }|r|| _        n-|rdnd}t          d| d           t          j        d           t          | dd          }|rt	          |          }|r|| _        	 ddl
m}m}	m}
m} ddlm}  |
 |                      }|rt          j                            dt'          |           d|	 d           |D ].}t          j                            d ||           d           /t          j                            d| d           t          j                            d           n# t(          $ r Y nw xY wt+                      st                       t          d           t                       t          d           t                       ddlm}m}  |            s |d           t          j        d           	 t3          d                                                                          }n# t8          t:          f$ r d}Y nw xY w|d v rt=          |            dS t                       t          d!           t          j        d           t?                      r"	 dd"l m!}  |             n# t(          $ r Y nw xY w	 tE                       n# t(          $ r Y nw xY wt          | d#d$          rd%tF          j$        d&<   t          | d'd$          r-d%tF          j$        d(<   d%tF          j$        d)<   d%tF          j$        d*<   t          | d+d$          rd%tF          j$        d)<   t          | d,d$          rd%tF          j$        d*<   t          | d-d          r| j%        tF          j$        d.<   tM                       |rtO          t          | dd          t          | d/d$          t          | d0d          t          | d1d          t          | d2d          t          | d3d          t          | d4d          t          | d5d$          t          | d6d          t          | d7d          t          | d8d$          t          | d9d$          t          | d:d$          t          | d;d          t          | d<d$          =           dd>l(m)} i d0| j*        d1t          | d1d          d2| j+        d3t          | d3d          d4t          | d4d          d5t          | d5d$          d6| j,        d7t          | d7d          dt          | dd          d8t          | d8d$          d9t          | d9d$          d:t          | d:d$          d;t          | d;d          d,t          | d,d$          pt          | d'd$          d+t          | d+d$          pt          | d'd$          d?t          | d?d$          }d@ |-                                D             }	  |dBi | dS # t\          $ r1}t          dA|            t          j        d           Y d}~dS d}~ww xY w)CzRun interactive chat CLI.continue_lastNresumezNo session found matching ''.z5Use 'hermes sessions list' to see available sessions.rB   r%   r   ru  TUICLIzNo previous z session found to continue.r   )MIGRATION_GUIDE_URLRETIREMENT_DATEfind_retired_xai_refsformat_issuerw  u   [33m⚠ xAI retires z model(s) in your config on z:[0m
u     [33m⚠[0m rc  z  [2mMigration guide: z[0m
z,  [2mRun 'hermes doctor' for details.[0m

zLIt looks like Hermes isn't configured yet -- no API keys or providers found.z  Run:  hermes setup)is_interactive_stdin#print_noninteractive_setup_guidancez;No interactive TTY detected for the first-run setup prompt.zRun setup now? [Y/n] n>   rR   r  r  z4You can run 'hermes setup' at any time to configure.)prefetch_update_checkyoloFrD   HERMES_YOLO_MODE	safe_modeHERMES_SAFE_MODEHERMES_IGNORE_USER_CONFIGHERMES_IGNORE_RULESignore_user_configignore_rulesr  HERMES_SESSION_SOURCEr  r  r  r  rP  r)  rk  r  r*  r+  r,  r-  r.  r/  )r  r  r  r  rP  r)  rk  r  r*  r+  r,  r-  r.  r/  maincompactc                     i | ]
\  }}|||S r  rN   r  s      r   r  zcmd_chat.<locals>.<dictcomp>c	  s    ???tq!arH   r   rN   )/ry  r0   r2   r4   rn  r|  r|   rE   r   r8  hermes_cli.xai_retirementr  r  r  r  r  r  r   rL   r   r   r  hermes_cli.setupr  r  r)  r5   r6   r+  r*  	cmd_setuprp  hermes_cli.bannerr  rm  r&   r'   r  rs  rm  r   r  r  r  r  r  r   )rx  use_tuicontinue_valr  r  last_idkind
resume_valr  r  r  r  _load_config_for_xai_check_retired_xai_refs_refr  r  replyr  cli_mainkwargses                         r   cmd_chatr    s   t$$G 4$77L GD(D99 lC(( 	5lCCH &DLDDDEEEMNNN &0UU5F+6:::G >v/u=== % '2uuUFTFFFGGG x..J #1*== 	#"DK
	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	POOOOO112L2L2N2NOO 	UJA3/@+A+A A A%4A A A   * Q Q
  !Od9K9K!O!O!OPPPPJX:MXXXYYYJSTTT    ()) Z	
 	
 	
 	$%%%	
 	
 	
 	
 	
 	
 	
 	

 $#%% 	//M   HQKKK	12288::@@BBEE+, 	 	 	EEE	$$$dOOOFDEEE
 ,-- 	??????!!#### 	 	 	D	(****    tVU## -),
%& t[%(( 0),
%&25
./,/
() t)511 625
./
 t^U++ 0,/
() tXt$$ :.2k
*+ 
D(D))D)U33$..T:t44T:t444400D)T22$//$..$..T:u55mU;;#D*;UCCdK66 ~u==	
 	
 	
 	
& %$$$$$GD*d33 	DM 	'$$//	
 	74D11 	w.. 	 	w-- 	'$$// 	GD*e44 	wt]E:: 	74):EBB 	WT;55 	ne<<ak[`@a@a 	gd,@%HHmGTXZeglLmLm  	74E22!F& @?v||~~???F6   mmmsa   *CG8 8
HH3K KK3M 
MMM$ $
M10M1Z 
[%&[[c                 D    t                       ddlm}  ||            dS )zGateway management commands.r   )gateway_commandN)ru  hermes_cli.gatewayr  )rx  r  s     r   cmd_gatewayr  l	  s6     """222222ODrH   c                     ddl m}  ||           }t          |t                    r|dk    rt	          |          dS dS )z1Local OpenAI-compatible proxy to OAuth providers.r   )	cmd_proxyN)hermes_cli.proxy.clir  r2   r   
SystemExit)rx  
_cmd_proxyrcs      r   r  r  u	  sY     =<<<<<	D		B"c rQwwnn wwrH   c                 f   t          d           ddlm}m} ddlm}m} t                       t          d           t          d            |d          pd}|st                       t          d	           t                       t          d
           t          d           t          d           t                       t          d           t          d           t          d           t                       	 t          d          	                                }n'# t          t          f$ r t          d           Y dS w xY w|dk    r |dd           d}t          d           t                       t          d           t          d           t          d           t          d           t          d           t          d           t          d           t          d           t          d           t          d           t          d           t          d            n< |dd!           d!}t          d"           n|}|dk    rd#nd$}t          d%|            t                        |d&          pd                                d'k    rt          d(            |d)          pd}	|	rt          d*|	            	 t          d+          	                                }
n# t          t          f$ r d,}
Y nw xY w|
                                d-v r}|dk    r"t          d.          	                                }n!t          d/          	                                }|r2 |d)|                    d0d                     t          d1|            nt                       |dk    r1t          d2           t          d3          	                                }n!t          d/          	                                }|r3 |d)|                    d0d                     t          d4|            nt          d5           dd6lm}  |            }|d7z  }|                                st          d8|            dS |d9z                                  s"t          d:            |d;          }|st          d<           dS 	 t#          j        |d=d>d?d@gt'          |          t"          j        t"          j        dAdBdC |            D          }n # t          $ r t          dE           Y dS w xY w|j        dk    rn|j        pd	                                }|r/dF                    |                                dGd                   ndH}t          dI           t          |           dS t          dJ           nt          dK           t5                      dz  dLz  }|                    dAdAM           |dNz                                  rt          dO           	 t          dP          	                                }
n# t          t          f$ r d,}
Y nw xY w|
                                d-v r=t9          j        |dAQ           |                    dAdAM           t          dR           nO |d&          pd                                d'k    r |d&d'           t          dS           t          dT           dS t                       t          dU           |dk    rt          dV           t          dW           nt          dX           t                       t          dY           t          dU           t                       	 t#          j         |dZ          pdZt'          |          d[d\t'          |          gt'          |           |            ]           n# t          $ r Y nw xY wt                       |dNz                                  r |d&d'           t          d^           t                       |dk    rZt          d_           t          d`           t          da           t          db           t                       t          dc           nht          d_           t          d`           t          dd           t          de           t                       t          dc           t          df           t                       t          dg           dS t          dh           dS )izESet up WhatsApp: choose mode, configure, install bridge, pair via QR.whatsappr   )get_env_valuesave_env_valuefind_node_executablewith_hermes_node_pathu   ⚕ WhatsApp Setupz2==================================================WHATSAPP_MODErR   z&How will you use WhatsApp with Hermes?z&  1. Separate bot number (recommended)uF        People message the bot's number directly — cleanest experience.zH     Requires a second phone number with WhatsApp installed on a device.z   2. Personal number (self-chat)z/     You message yourself to talk to the agent.z3     Quick to set up, but the UX is less intuitive.z  Choose [1/2]: z
Setup cancelled.NrD   botu     ✓ Mode: separate bot numberu     ┌─────────────────────────────────────────────────┐u9     │  Getting a second number for the bot:           │u9     │                                                 │u9     │  Easiest: Install WhatsApp Business (free app)  │u9     │  on your phone with a second number:            │u;     │    • Dual-SIM: use your 2nd SIM slot            │u<     │    • Google Voice: free US number (voice.google) │u;     │    • Prepaid SIM: $3-10, verify once            │u9     │  WhatsApp Business runs alongside your personal │u;     │  WhatsApp — no second phone needed.             │u     └─────────────────────────────────────────────────┘z	self-chatu'     ✓ Mode: personal number (self-chat)zseparate bot numberzpersonal number (self-chat)u   
✓ Mode: WHATSAPP_ENABLEDr  u   ✓ WhatsApp is already enabledWHATSAPP_ALLOWED_USERSu   ✓ Allowed users: z
  Update allowed users? [y/N] r  >   r  r  z<  Phone numbers that can message the bot (comma-separated): z(  Your phone number (e.g. 15551234567): r7  u     ✓ Updated to: z+  Who should be allowed to message the bot?z4  Phone numbers (comma-separated, or * for anyone): u     ✓ Allowed users set: uF     ⚠ No allowlist — the agent will respond to ALL incoming messages)resolve_whatsapp_bridge_dirz	bridge.jsu    
✗ Bridge script not found at r  uM   
→ Installing WhatsApp bridge dependencies (this can take a few minutes)...r  u5     ✗ npm not found on PATH — install Node.js firstr  r  r  r  Tr   r3  r  u   
  ✗ Install cancelledrc  r  z(no output)u     ✗ npm install failed:u     ✓ Dependencies installedu)   ✓ Bridge dependencies already installedrl  r`  z
creds.jsonu#   ✓ Existing WhatsApp session foundz8
  Re-pair? This will clear the existing session. [y/N] ignore_errorsu     ✓ Session clearedu'   
✓ WhatsApp is configured and paired!z(  Start the gateway with: hermes gatewayu   ──────────────────────────────────────────────────u0   📱 Open WhatsApp (or WhatsApp Business) on thez*   phone with the BOT's number, then scan:u,   📱 Open WhatsApp on your phone, then scan:u0      Settings → Linked Devices → Link a Devicer  z--pair-onlyz	--sessionrU  u!   ✓ WhatsApp paired successfully!z  Next steps:z)    1. Start the gateway:  hermes gatewayz2    2. Send a message to the bot's WhatsApp numberz)    3. The agent will reply automaticallyu;     Tip: Agent responses are prefixed with '⚕ Hermes Agent'u)       2. Open WhatsApp → Message Yourselfu.       3. Type a message — the agent will replyz4  so you can tell them apart from your own messages.z1  Or install as a service: hermes gateway installuG   ⚠ Pairing may not have completed. Run 'hermes whatsapp' to try again.)r   r  r  r  r   r  r  r|   r)  r5   r+  r*  r6   r3  !gateway.platforms.whatsapp_commonr  r,   rA  rB  r4   DEVNULLr  rY  r   r*   r8  r   rd  rW  rmtree)rx  r  r  r  r  current_modechoicewa_mode
mode_labelcurrent_usersresponsephoner  
bridge_dirbridge_scriptr  r  errr  session_dirs                       r   cmd_whatsappr  	  s{
   ????????LLLLLLLL	GGG	
	(OOO !=117RL .+67776777VWWWV	
 	
 	
 	0111?@@@CDDD	-..4466FF+, 	 	 	&'''FF	 S==N?E222G3444GGG  p  q  q  qMNNNMNNNMNNNMNNNOPPPPQQQOPPPMNNNMNNNOPPP  p  q  q  q  qN?K888!G;<<<<%,%5%5!!;X 	 	)Z))*** 
GGG())/R6688FBB/000 "M":;;ArM \3M33444	?@@FFHHHH+, 	 	 	HHH	>>|++%R %''  HIIOOQQ 47sB9O9OPPP2522333e?@@@F egg E DEEKKMME 	\N3U]]35K5KLLL5e556666Z[[[ NMMMMM,,..J,M!! A-AABBB'//11 ;\	
 	
 	
 #"5)) 	IJJJF	^il<NO
OO!)!  ))++	 	 	FF ! 	 	 	-...FF	 !!=&B--//C;>Qdii 0 0 6777MG-...'NNNF,----9::: "##j09<KdT222l"**,, 3444	K egg H +, 	 	 	HHH	>>|++M+T::::dT:::)**** 0117R>>@@FJJ16:::<===<===F 
GGG	*%@AAA:;;;;<===	GGG	
<===	*	GGG$$V,,6M""K   J%%''
	
 
	
 
	
 
	
 
	
     
GGGl"**,, Y
 	)62221222e/"""=>>>FGGG=>>>GGGOPPPP/"""=>>>=>>>BCCCGGGOPPPHIIIABBBBBWXXXXXs\   >!D    EE!K- -LLA
T T/.T/!X> >YYA_ 
_)(_)c                 @    t          d           ddlm}  |            S )u"  Set up WhatsApp Business Cloud API (official Meta integration).

    Walks the user through the Meta-side credentials (Phone Number ID,
    Access Token, App Secret, optional App/WABA IDs) plus webhook
    configuration. Includes field-shape validators that catch the most
    common setup mistakes (e.g. pasting a phone number into the Phone
    Number ID field).

    Distinct from ``hermes whatsapp`` (the Baileys bridge wizard) — the
    two adapters are complementary, not alternatives. See
    ``hermes_cli/setup_whatsapp_cloud.py``.
    whatsapp-cloudr   )run_whatsapp_cloud_setup)r   hermes_cli.setup_whatsapp_cloudr  )rx  r  s     r   cmd_whatsapp_cloudr  i
  s4     !"""HHHHHH##%%%rH   c                 (    ddl m}  ||            dS )zInteractive setup wizard.r   )run_setup_wizardN)r  r  )rx  r  s     r   r  r  |
  s+    111111TrH   c                 "   ddl m} ddlm}  |d           t	          d           t	                       dD ]} ||           t                      st	                       t          |            dS t	                       t	          d           dS )	zFOne-shot bootstrap for pip users: install non-Python deps + run setup.r   )stamp_install_methodr  pipu!   ⚕ Hermes post-install bootstrap)r  browserripgrepffmpegu   ✓ Post-install complete.N)r  r  r  r  r|   r  r  )rx  r  r  deps       r   cmd_postinstallr  
  s    666666777777	
-...	GGG7  #')) ,$*+++++rH   c                     t          d           t          | dd          r1	 ddlm}  |             t	          d           n# t
          $ r Y nw xY wt          |            dS )	uK   Select default model — starts with provider selection, then model picker.r  r  Fr   )clear_provider_models_cachez  Cleared model picker cache.rx  N)r   r0   hermes_cli.modelsr  r|   r   select_provider_and_model)rx  r  s     r   	cmd_modelr  
  s    tY&& 	EEEEEE'')))12222 	 	 	D	4((((((s   A 
AAr  c                 f    	 ddl m}  ||           }|duo
|j        dk    S # t          $ r Y dS w xY w)a6  Return True when provider_id maps to a profile with auth_type='api_key'.

    Used as a catch-all in select_provider_and_model() so that new providers
    declared in plugins/model-providers/<name>/ automatically dispatch to _model_flow_api_key_provider
    without requiring an explicit elif branch here.
    r   )get_provider_profileNr  F)	providersr  r  r   )r  r  _ps      r   _is_profile_api_key_providerr  
  sa    222222!!+..~;",)";;   uus   " 
00c                   01234567 ddl m7m0m} ddlm4m}m} ddlm	}  |            }|
                    d          }t          |t                    r|
                    dd          }|pd}d	}|
                    d          5t          5t                    r5
                    d
          }|pt          j        d          pd3 4|          }dt          t          t          t          t          f         f         f047fd}	dt          dt          fd2 |	|          1dt          f1235fd}
 |
            }|d}|sI3dk    rC |3|
                    d          |          }||j        }nd3 d}t#          d| d           |sE	  7d          }n8# 0$ r0}3dk    r ||          }t#          d| d           d	}Y d	}~nd	}~ww xY w|dk    r |d          rd}ddlm}m}m}m} t          |          6|r|1v r1|         d         }n|r6
                    ||          nd}t#                       t#          d|            t#          d|            t#                       d |D             } |d  |D                       }|r ||          nd}g }d}|D ]}|d!         d"k    r\|d#         }|
                    d$d          }|r|d%          d&| d'n
|d%          d(}d)| }t/          |          o||k    }|d*         }nK|d+         } |
                    | 6
                    | |                     }| }t/          |          o| |k    }g }|r.|                    || d,|f           t3          |          d-z
  }|                    |||f           1                                D ]\  }}!|!d         }"|!d.         }#|#                    d/d                              d0d                              d1          }$|!
                    dd          }%|%rd2|% nd}&|" d3|$ d'|& }|r4||k    r.|                    || d,g f           t3          |          d-z
  }|                    ||g f           |                    dd4g f           t          |
                    d5          t:                    o!t/          |
                    d5                    }'|'r|                    d6d7g f           |                    d8d9g f           |                    d:d;g f           t=          d< |D             |=          }(|(||(         d         d:k    rt#          d>           d	S ||(         d         })||(         d?         }*|*rd}+||*v r|*                    |          }+6fd@|*D             },||(         d-                              d(d-          d         }-t=          |,|+dA|- dBC          }.|.t#          d>           d	S |*|.         }/n|)}/|/d8k    rtC                       d	S |/dk    rtE          ||           n|/dDk    rtG          ||           n|/dEk    rtI          ||| F           n|/dGk    rtK          ||           n|/dHk    rtM          ||| F           n|/dIk    rtO          ||           n|/dJk    rtQ          ||| F           nn|/dKk    rtS          ||           nV|/dLk    rtU          ||           n>|/dk    rtW          |           n'|/,                    dM          s|/1v rJ |	 |                      
                    |/          }!|!t#          dN           d	S t[          ||!           n|/d6k    rt]          |           n|/dOk    rt_          ||           n|/dPk    rta          ||           n|/dQk    rtc          ||           ni|/dRk    rte          ||           nR|/dSk    rtg          ||           n;|/dTk    rti          ||           n$|/dUv stk          |/          rtm          ||/|           |/dVvr%|/,                    dM          sto                       d	S d	S d	S )Wa  Core provider selection + model picking logic.

    Shared by ``cmd_model`` (``hermes model``) and the setup wizard
    (``setup_model_provider`` in setup.py).  Handles the full flow:
    provider picker, credential prompting, model selection, and config
    persistence.
    r   )resolve_provider	AuthErrorformat_auth_error)get_compatible_custom_providersr  r  )resolve_provider_fullr  r   rR   z	(not set)Nr  rD  autor   c                 4   ddl m} i i  |            }dt          dt          dt          dt          dt          dd ffd	}|                    d
          }t	          |t
                    r|D ]}t	          |t                    s ||                    dd          d|                    dd          p|                    dd          |                    dd          |                    dd          p+|                    dd          p|                    dd                     |                    d          }t	          |t                    r|                                D ]\  }}t	          |t                    s ||                    dd          p|||                    dd          p|                    dd          |                    dd          |                    dd          p+|                    dd          p|                    dd                     dt          t          t          f         dt          dt          dt          dt          f
d}i }	 |           D ]}
t	          |
t                    s|
                    d          pd	                                }|
                    d          pd	                                }|r|spd|
                                                    dd          z   }|
                    d          pd	                                }|r	  |           n# $ r |}Y nw xY w|||
                    dd          |
                    dd          |
                    dd          |
                    di           |
                    dd          |
                    dd          | ||||
                    dd                     ||||
                    dd                    d|	|<   |	S )Nr   )read_raw_configr   provider_keyr  r  r  r   c                    t          |pd                                          }t          |pd                                          }t          | pd                                          } t          |pd                                          }t          |pd                                          }g }| r>|                    |                                 f|                                 |ff           |r>|                    |                                f|                                |ff           d|v r|D ]}	                    ||           d|v r|D ]}
                    ||           d S d S )NrR   ${)r4   r5   rZ  r6   r`  )r   r  r  r  r  templatebase_template
identitiesidentityraw_api_key_refsraw_base_url_refss            r   _record_rawzRselect_provider_and_model.<locals>._named_custom_provider_map.<locals>._record_raw
  s    7=b))//11HB//5577Mtzr??((**D|1r2288::L$$**,,E
 J L!!DJJLL?TZZ\\54I"JKKK !!"((**,|/A/A/C/CU.KL   x * D DH$//(CCCC}$$ * J JH%00=IIII %$J JrH   custom_providersrR   default_modelurlapir  refsc                 j   t          |pd                                                                          }t          |pd                                                                          }t          |pd                                          }||f|f||f|ffD ]}|d         r|| v r
| |         c S dS )NrR   r   r4   r5   r6   )r  r   r  r  name_lcpkey_lcr  s          r   _lookup_refzRselect_provider_and_model.<locals>._named_custom_provider_map.<locals>._lookup_ref#  s     $*"oo++--3355G,,"--3355;;==G$$**,,E% 
% 
	 * * A; *8t#3#3>)))2rH   custom:r7  r   key_envmodelsdiscover_modelsTapi_mode)r   r  r  r  r  r  r  r  r  api_key_refbase_url_ref)r  r  r4   r(   r2   r  r3   r  r  r5   r6   r3  )r  r  raw_cfgr  raw_list	raw_entryraw_providersraw_keyr  custom_provider_mapr  r   r  rF  r  r  r  r  r  r  s                  @@r   _named_custom_provider_mapz=select_provider_and_model.<locals>._named_custom_provider_map
  s   555555 .0.0!/##	J	J	J 	J 		J
 	J 	J 	J 	J 	J 	J 	J 	J< ;;122h%% 	%  	!)T22 MM&"--MM'2..T)--QS2T2TMM)R00MM*b11 0 }}UB//0 }}UB//     K00mT** 	&3&9&9&;&;  "!)T22 MM&"--8MM'2..T)--QS2T2TMM)R00MM*b11 0 }}UB//0 }}UB//   	ucz"		 	 		
 	 	 	 	& !44S99 	 	EeT** IIf%%+2244D		*--3::<<H x djjll223<<<C!IIn55;BBDDL ''$$\2222  ' ' '&CCC' $ 99Y33 99Y337B//))Hb11#(99->#E#E!IIj"55 ,*{$dL%))GR:P:P    !,%t\599Wb;Q;Q! !( ($$" #"s   4M  M
	M
r  c                     t          | pd                                                              d                                          S )NrR   r  )r4   r5   rstripr6   )r  s    r   _norm_base_urlz1select_provider_and_model.<locals>._norm_base_urlX  s8    39"~~##%%,,S1177999rH   c                  
   dk    st          t                    sdS                      dd                    } | sdS                                 D ],\  }} |                    dd                    | k    r|c S -dS )NcustomrR   r  )r2   r3   r(   r  )current_baserF  provider_info_custom_provider_mapr  effective_providerr  s      r    _active_custom_key_from_base_urlzCselect_provider_and_model.<locals>._active_custom_key_from_base_url`  s    ))It1L1L)2%~immJ&C&CDD 	2"6"<"<">"> 	 	C~m//
B??@@LPP


 QrrH   r  zUnknown provider 'zb'. Check 'hermes model' for available providers, or run 'hermes doctor' to diagnose config issues.z	Warning: z) Falling back to auto provider detection.
openrouterr  r  )CANONICAL_PROVIDERS_PROVIDER_LABELSgroup_providersprovider_group_for_slugr   nonez  Current model:    z  Active provider:  c                 (    i | ]}|j         |j        S rN   )slugtui_descr  r  s     r   r  z-select_provider_and_model.<locals>.<dictcomp>  s    GGGaqvqzGGGrH   c                     g | ]	}|j         
S rN   r#  r%  s     r   r  z-select_provider_and_model.<locals>.<listcomp>  s    #H#H#HqAF#H#H#HrH   r  groupgroup_iddescriptionr,  u    ▸ (rr   u    ▸zgroup:membersr#  u     ← currently activerB   r  https://http://r      — rq   z$Custom endpoint (enter URL manually)r  remove-customzRemove a saved custom providerz
aux-configzConfigure auxiliary models...cancelzLeave unchangedc                     g | ]\  }}}|	S rN   rN   r  rG  r,  s      r   r  z-select_provider_and_model.<locals>.<listcomp>  s    ***;1eQ***rH   r   
No change.r   c                 <    g | ]}                     ||          S rN   r(   )r  mprovider_labelss     r   r  z-select_provider_and_model.<locals>.<listcomp>  s6     
 
 
*+O1%%
 
 
rH   zSelect z
 provider:r   r  moanousr  openai-codex	xai-oauth
qwen-oauthzminimax-oauthcopilot-acpcopilotr  zyWarning: the selected saved custom provider is no longer available. It may have been removed from config.yaml. No change.	anthropickimi-codingstepfunbedrockvertexazure-foundry>   
openai-apitencent-tokenhubgmixaizaiarceegemininvidiaxiaomialibabaminimaxdeepseekkilocodelmstudiohuggingface
minimax-cnopencode-goollama-cloudopencode-zenkimi-coding-cn>   r0  r  r/  )8r  r  r  r  r  r  r  r  hermes_cli.providersr  r(   r2   r3   r&   r  r4   r  r|   r  r  r  r  r   rV   r  r   r  r3  r  r  _prompt_provider_choicer   rf   _aux_config_menur  r+  r  r  r  r  r  r#  r"  r  rW   r!  _remove_custom_providerr*  r$  r%  r'  r(  r   r  r)  _clear_stale_openai_base_url)8rx  r  r  r  r  configcurrent_modelconfig_providercompatible_custom_providersr  r  r  
active_defwarningr   r  r  r  r   active_labelcanonical_descsgrouped_rowsactive_groupordereddefault_idxr  gid
group_descr,  rF  	is_activer+  r#  r  r   r  	short_urlsaved_model
model_hint_has_saved_custom_listprovider_idxselected_keyselected_membersmember_defaultmember_labelsgroup_label
member_idxselected_providerr  r  r  r  r  r  r8  r  s8                                                   @@@@@@@@r   r  r  
  s            
         
 ;:::::[]]FJJw''M-&& 9%)))R88!0[M O

7##I)T"" 4#--
33 	K29%@AAKV  #B"A&"I"I}#4T#s(^0C+D }# }# }# }# }# }# }# }#~:C :C : : : : 65 	c 	 	 	 	 	 	 	 	 	 .-//F~ R(F22**JJ{##'
 


 !]FF%7    
 PgPPPQQQ 	%%f--FF 	 	 	!V++++C00T'TTTUUUFFFFFF		 --0A"B"B            +,,O Q&000+F3F;>DP**66:::&	GGG	
0
0
0111	
/
/
/000	GGG HG3FGGGO"?#H#H4G#H#H#HIIL 7=D**6222"L
 13GK 2 2v;'!!j/C33J<Fas7|88:8888sSZ|LaLaLaE 3..C\**Bsl/BI)nGGv;D#''o.A.A$.M.MNNECV7IG 	2NNCE!A!A!A7KLLLg,,*KKNNC01111288:: - -]V$ ,$$Z44<<YKKRRSVWW	#''44.9A*[***r
33933z33 	-cVmmNNCE!A!A!A2FGGGg,,*KKNNC+,,,,NNHDbIJJJ'

3E(F(FMM RV

%&&S S  P)I2NOOONNL"A2FGGGNNH/4555***'***  L w|4Q78CCl<(+L|,Q/
  )%%%-33F;;N
 
 
 
/?
 
 
 l+A.44VQ??B,"3K333
 
 


 ,F,Z8(L(( L((v}5555	e	#	#....	f	$	$T:::::	n	,	, 7777	k	)	)fm$?????	l	*	*v}5555	o	-	-!&-dCCCCC	m	+	+6666	i	'	'FM2222	h	&	&6""""$$Y//0O 44422;;==AAEEFWXX H   F 7777	o	-	-''''	k	)	)fm4444	m	+	+////	i	'	'FM2222	i	'	'FM2222	h	&	&6=1111	o	-	-!&-8888	  
 
* 
&&7	8	8+
, 	%V->NNN  !    **955	
 	%&&&&&   s   F G"&GGc                     ddl m} m}m}  |            }|                    di           }t          |t                    r<|                    d          pd                                                                }nd}|dk    s|sdS  | d          }|rB |dd           t          t          |          d	k    rd
|dd	          dnd
| d           dS dS )aQ  Remove OPENAI_BASE_URL from ~/.hermes/.env if the active provider is not 'custom'.

    After a provider switch, a leftover OPENAI_BASE_URL causes auxiliary
    clients (compression, vision, delegation) with provider:auto to route
    requests to the old custom endpoint instead of the newly selected
    provider.  See issue #5161.
    r   )r  r  r  r  r  rR   r  Nr  r  z.Cleared stale OPENAI_BASE_URL from .env (was: z...)rr   )r  r  r  r  r(   r2   r3   r5   r6   r|   r   )r  r  r  r  r  r  	stale_urls          r   r_  r_  R  s)    MLLLLLLLLL
+--C$$I)T"" MM*--3::<<BBDD88/00I 
("---9~~"" RYss^QQQQN)NNN	
 	
 	
 	
 	

 
rH   ))visionVisionzimage/screenshot analysis)compressionCompressionzcontext summarization)web_extractzWeb extractzweb page summarization)approvalApprovalzsmart command approval)r   MCPzMCP tool reasoning)title_generationzTitle generationzsession titles)tts_audio_tagszTTS audio tagszGemini TTS tag insertion)
skills_hubz
Skills hubzskills search/install)triage_specifierzTriage specifierzkanban spec fleshing)kanban_decomposerzKanban decomposerztask decomposition)profile_describerzProfile describerzauto profile descriptions)curatorCuratorzskill-usage review pass
_AUX_TASKSc                      t          t                    } 	 ddlm}  |            D ],}|                     |d         |d         |d         f           -n# t
          $ r Y nw xY w| S )a  Return built-in + plugin-registered auxiliary tasks for picker/menu use.

    Built-in tasks come first (preserving order), followed by plugin tasks
    sorted by key. Used by ``_aux_config_menu``, ``_reset_aux_to_auto``, and
    display-name lookups so plugin-registered tasks (registered via
    :meth:`hermes_cli.plugins.PluginContext.register_auxiliary_task`) appear
    in the same surfaces as built-in ones without core knowing about them.
    r   )get_plugin_auxiliary_tasksrF  display_namer*  )r  r  hermes_cli.pluginsr  r  r   )tasksr  r  s      r   _all_aux_tasksr    s     EAAAAAA//11 	V 	VELL%,n(=u]?STUUUU	V    	 Ls   =A 
A! A!task_cfgc                 P   t          | t                    sdS t          |                     d          pd                                          }t          |                     d          pd                                          pd}t          |                     d          pd                                          }|rM|                    dd                              dd                              d          }d	| d
|rd| ndz   S |dk    rd|rd| ndz   S |r| d| S |S )z;Render the current aux config for display in the task menu.r  r  rR   r  r  r,  r-  r  zcustom (rr       · )r2   r3   r4   r(   r5   r3  r  )r  r  r  r  shorts        r   _format_aux_currentr    sC   h%% v8<<
++1r2288::H8<<
++5v66<<>>H&HW%%+,,2244E G  R0088BGGNNsSS"%"""&EnUnnn2FF658b99 ('''''OrH   r  r  r  taskr  r  c                <   ddl m}m}  |            }|                    di           }t	          |t
                    si }||d<   |                    | i           }	t	          |	t
                    si }	|	|| <   ||	d<   |pd|	d<   |pd|	d<   |pd|	d<    ||           d	S )
u  Persist an auxiliary task's provider/model to config.yaml.

    Only writes the four routing fields — timeout, download_timeout, and any
    other task-specific settings are preserved untouched. The main model
    config (``model.default``/``model.provider``) is never modified.
    r   r  save_config	auxiliaryr  rR   r  r  r  N)r  r  r  r`  r2   r3   )
r  r  r  r  r  r  r  r  auxr  s
             r   _save_aux_choicer    s     ;:::::::
+--C
..b
)
)Cc4   KNN4$$EeT"" D	 E*[bE'N BE*}"E)KrH   c                     ddl m} m}  |             }|                    di           }t	          |t
                    si }||d<   d}t                      D ]\  }}}|                    |i           }t	          |t
                    si }|||<   d}	|                    d          dvrd|d<   d}	d	D ]}
|                    |
          rd
||
<   d}	|	r|dz  } ||           |S )zReset every known aux task back to auto/empty. Returns number reset.

    Includes plugin-registered tasks (via ``_all_aux_tasks``) so a plugin
    that contributed an auxiliary task gets reset alongside built-ins.
    r   r  r  Fr  >   NrR   r  r  Tr  rR   rB   )r  r  r  r`  r2   r3   r  r(   )r  r  r  r  countr  _name_descr  changedfields              r   _reset_aux_to_autor    s4    ;:::::::
+--C
..b
)
)Cc4   KE,..  eUtR((%&& 	ECI99Z  (::: &E*G5 	 	Eyy !e 	QJEKLrH   c                     ddl m}  	  |             }t          |                    d          t                    r|                    di           ni }t                       t          d           t                       t          d           t          d           t          d           t          d	           t          d
           t                       t                      }t          d |D                       dz   }t          d |D                       dz   }g }|D ]\  }}}	t          |                    |          t                    r|                    |i           ni }
t          |
          }|	                    |           d|	z   dz   	                    |           | }|
                    ||f           |
                    d           |
                    d           t          d |D             d          }|dS ||         d         }|dk    rdS |dk    rCt                      }|rt          d| d           nt          d           t                       bt          |           r)u   Top-level auxiliary-model picker — choose a task to configure.

    Loops until the user picks "Back" so multiple tasks can be configured
    without returning to the main provider menu.
    r   rw  Tr  u(     Auxiliary models — side-task routingz@  Side tasks (vision, compression, web extraction, etc.) defaultu@     to your main chat model.  "auto" means "use my main model" —z>  Hermes only falls back to a lightweight backend (OpenRouter,z<  Nous Portal) if the main model is unavailable.  Override az@  task below if you want it pinned to a specific provider/model.c              3   <   K   | ]\  }}}t          |          V  d S r  r   )r  rG  r   s      r   r  z#_aux_config_menu.<locals>.<genexpr>  s.      ==ZQas4yy======rH   r   c              3   <   K   | ]\  }}}t          |          V  d S r  r  )r  rG  descs      r   r  z#_aux_config_menu.<locals>.<genexpr>  s.      ==ZQ4s4yy======rH   r  (rr   )	__reset__zReset all to auto)__back__Backc                     g | ]\  }}|S rN   rN   r2  s      r   r  z$_aux_config_menu.<locals>.<listcomp>  s    +++xq%U+++rH   r3  Nr  r  zReset z auxiliary task(s) to auto.z-All auxiliary tasks were already set to auto.)r  r  r2   r(   r3   r|   r  r  r  ljustr  r\  r  _aux_select_for_task)r  r  r  	all_tasksname_coldesc_colentriestask_keyr   r  r  currentr,  r.  rF  r  s                   r   r]  r]    s    .-----1"kmm*4SWW[5I5I4*P*PXcggk2&&&VX8999PQQQPQQQNOOOLMMMPQQQ #$$	==9=====A==9=====A)+$- 	. 	. HdD)3CGGH4E4Et)L)LT"%%%RT  *(33G::h''V#*s*:)A)A()K)KVWVV  NNHe,----9:::+,,,%++7+++
 
 
 ;Fcl1o*F+"$$A G=q===>>>>EFFFGGGS!!!c1"rH   c                     ddl m} ddlm}  |            }t	          |                    d          t                    r|                    di           ni }t	          |                               t                    r|                     i           ni }t          |                    d          pd                                          pd}t          |                    d          pd                                          }t          |                    d	          pd                                          }t           fd
t                      D                        }		  ||||          }
n+# t          $ r}t          d|            g }
Y d}~nd}~ww xY wg }|dk    r|sdnd}|                    dd| g f           |
D ]}|                    dd          }|                    d          p|}|                    dd          }|                    d          pg }|rd| dnd}||k    r|sdnd}|                    || | | t          |          f           |rdnd}|                    dd| g f           |                    ddg f           t                       t          d|	 dt          |                      t                       t!          d |D             d          }|dS ||         \  }}}|dk    rdS |dk    r(t#           dddd           t          |	 d            dS |dk    rt%           |           dS t'           |||           dS )!ui  Pick a provider + model for a single auxiliary task and persist it.

    Uses ``list_authenticated_providers()`` to only show providers the user
    has already configured. This avoids re-running OAuth/credential flows
    inside the aux picker — users set up new providers through the normal
    ``hermes model`` flow, then route aux tasks to them here.
    r   rw  )list_authenticated_providersr  r  r  r  rR   r  c              3   0   K   | ]\  }}}|k    |V  d S r  rN   r  rF  r   rG  r  s       r   r  z'_aux_select_for_task.<locals>.<genexpr>D  -      RR,#tQcTkkkkkkRRrH   )current_providerra  current_base_urlz*Could not detect authenticated providers: Nu     ← current__auto__zauto (recommended)r#  r   total_modelsr  r.  z models
__custom__zCustom endpoint (direct URL)r  r  z  Configure u    — current: c                     g | ]\  }}}|	S rN   rN   r2  s      r   r  z(_aux_select_for_task.<locals>.<listcomp>l  s    "D"D"D[Qq5"D"D"DrH   r3  r  r  r  r  z: reset to auto.)r  r  hermes_cli.model_switchr  r2   r(   r3   r4   r5   nextr  r   r|   r  r  r  r\  r  _aux_flow_custom_endpoint_aux_flow_provider_model)r  r  r  r  r  r  r  ra  r  r  r  r   r  auto_markerr  r#  r   totalr  rq  r  custom_markerr.  _labels   `                       r   r  r  2  s    .-----DDDDDD
+--C&01E1Et&L&L
T#''+r
"
"
"RTC$.swwt}}d$C$CKswwtR   H8<<
33=v>>DDFFP&W--344::<<M8<<
339r::@@BBRRRR.2B2BRRRTXYYL00-'-
 
 
		
    @3@@AAA						 13G ,v55>N5TV  NNJ B[ B BBGHHH 	L 	LuuVR  uuV}}$na((x&B/4<+U++++"
#'777@P7OOVX 	 	;z;6;;T&\\JKKKK (8?OORMNNL"P"P"PRTUVVVNNJ+,,,	GGG	
T
T
T5H5R5R
T
TUUU	GGG
!"D"DG"D"D"Da
P
P
PC
{"3<D&&zzb2rRRRR///000|!$111 T4?????s   %E4 4
F>FFprovider_slugcurated_modelsra  c                     ddl m} ddlm} t	           fdt                      D                        }i }	  ||          pi }n# t          $ r i }Y nw xY wt          |          }|spt          d| d           t          d           	 t          d          
                                }	n&# t          t          f$ r t                       Y d	S w xY w|	pd
}
n" |||||          }
|
t          d           d	S t           ||
pd
d
d
           |
rt          | d| d|
            d	S t          | d| d           d	S )zHPrompt for a model under an already-authenticated provider, save to aux.r   )_prompt_model_selection)get_pricing_for_providerc              3   0   K   | ]\  }}}|k    |V  d S r  rN   r  s       r   r  z+_aux_flow_provider_model.<locals>.<genexpr>  r  rH   zNo curated model list for .z;Enter a model slug manually (blank = use provider default):zModel: NrR   )ra  pricingconfirm_providerr4  r  : r  z (provider default model))r  r  r  r  r  r  r   r  r|   r)  r5   r*  r+  r  )r  r  r  ra  r  r  r  r  
model_listr  selecteds   `          r   r  r    s    877777::::::RRRR.2B2BRRRTXYYL G**=99?R    n%%J
  ;=;;;<<<KLLL		""((**CC!8, 	 	 	GGGFF	 9"**'*	
 
 
 ,F}HNRQS     K>>>>H>>?????IIIIIJJJJJs#   A AA!B/ /CCc                     ddl m} t           fdt                      D                        }t	          |                    d          pd                                          }t	          |                    d          pd                                          }t                       t          d|            t          d           t                       	 |rd	| d
nd}t          |                                          }n&# t          t          f$ r t                       Y dS w xY w|p|}|st          d           dS 	 |rd| d
nd}t          |                                          }	n&# t          t          f$ r t                       Y dS w xY w|	p|}		  |d                                          }
n&# t          t          f$ r t                       Y dS w xY wt           d|	||
           |                    dd                              dd                              d          }t          | d| d|	rd|	 ndz              dS )zHPrompt for a direct OpenAI-compatible base_url + optional api_key/model.r   masked_secret_promptc              3   0   K   | ]\  }}}|k    |V  d S r  rN   r  s       r   r  z,_aux_flow_custom_endpoint.<locals>.<genexpr>  r  rH   r  rR   r  z  Custom endpoint for zH  Provide an OpenAI-compatible base URL (e.g. http://localhost:11434/v1)z
Base URL [r&  z
Base URL: NzNo URL provided. No change.zModel slug (optional) [zModel slug (optional): z0API key (optional, blank = use OPENAI_API_KEY): r  r  r,  r-  r  z
: custom (rr   r  )hermes_cli.secret_promptr  r  r  r4   r(   r5   r|   r)  r*  r+  r  r3  r  )r  r  r  r  r  ra  
url_promptr  model_promptr  r  ro  s   `           r   r  r    s   ======RRRR.2B2BRRRTXYYL8<<
339r::@@BBW--344::<<M	GGG	
1<
1
1222	
TUUU	GGG2BT.).... 	 J%%''x(    
!!C +,,,	 +8m8888* 	
 l##))++x(    "]E&&>
 

%'' 	 x(        J++33IrBBII#NNI	\
1
1Y
1
1
1u5T^E^^^RT
UVVVVVs6   +D D)(D)+E0 0FFF9 9GGzSelect provider:r9  c          	         	 ddl m}  ||| |          }|dk    rt                       |S n# t          $ r Y nw xY wt          |           t	          | d          D ]*\  }}|dz
  |k    rdnd}t          d| d| d|            +t                       	 	 t          d	t          |            d
|dz    d                                          }|s|S t          |          dz
  }d|cxk    rt          |           k     rn n|S t          dt          |                       n@# t          $ r t          d           Y n%t          t          f$ r t                       Y dS w xY w)zShow provider selection menu with curses arrow-key navigation.

    Falls back to a numbered list when curses is unavailable (e.g. piped
    stdin, non-TTY environments).  Returns the selected index, or None
    if the user cancels.
    r   )_curses_prompt_choicerB   u   →r7  r  r$  T
Choice [1-z] (): Please enter 1-Please enter a numberN)r  r  r|   r   r  r)  r   r5   r   r   r*  r+  )	choicesr   r  r  r.  r   cr  r  s	            r   r\  r\    s   ::::::##E7G<<!88GGGJ      
%LLL'1%% & &1a%7**$6$$A$$$$%%%%	GGG	FS\\FFgkFFFGGMMOOC c((Q,CC&&&&#g,,&&&&&
2CLL223333 	+ 	+ 	+)*****!8, 	 	 	GGG44	s0   (, 
99;D# 0D# D# #E >E E zqwen3-coder-pluszqwen3-codercurrent_api_modec                 &   ddl m}  ||           }t          |pd                                                                          }|p|pd}g d}t                       t          d           t          |d          D ]\  }\  }}	}
g }||k    r|                    d           ||k    r|                    d           |rd	d
                    |           dnd}t          d| d|	 |            t          d|
            	 t          d                                                                          }n%# t          t          f$ r t          d            w xY w|s|pdS |dv rdS |dv rdS |dv rdS |dv rdS t          d| d           dS )zwPrompt for a custom provider API mode.

    Returns an explicit mode string, or None to keep auto-detect behavior.
    r   )_detect_api_mode_for_urlrR   ))rR   zAuto-detectzIUse Hermes URL heuristics; best for standard OpenAI-compatible endpoints.)chat_completionszChat Completionsz=Use /chat/completions for standard OpenAI-compatible servers.)codex_responseszResponses / Codexz:Use /responses for Codex-compatible tool-calling backends.)anthropic_messageszAnthropic Messagesz4Use /v1/messages for Anthropic-compatible endpoints.zSelect API compatibility mode:rB   detectedr  z [z / ]r  r$  z     z.Choice [1-4, Enter to keep current/detected]: z
Cancelled.N>   auto-detectrD   r  detect>   2chatcompletionsr  r  >   3codex	responsesr  r  >   4messagesrA  r  r  zInvalid API mode choice: z. Falling back to auto-detect.)hermes_cli.runtime_providerr  r4   r5   r6   r|   r  r  r*   r)  r*  r+  )r  r  r  detected_modenormalized_currentdefault_modemode_optionsr.  r7   r,  r*  markersr  r<   s                 r   !_prompt_custom_api_mode_selectionr  !  s1   
 EDDDDD,,X66M-344::<<BBDD%<<"L  L. 
GGG	
*+++,5lA,F,F % %((eUKM!!NN:&&&L  NN9%%%07?,ejj)),,,,R)3))%)))***#k##$$$$<
 

%''%%'' 	 x(   n  $#t#
444t
>>>!!
<<<  
BBB##	
Ic
I
I
IJJJ4s   3D; ;"Ec                 l   ddl }|                     dd                              dd                              d          }|                    dd|          }|                    d          d         }d|v sd	|v rd
| d}n1d|                                v rd| d}n|                                }|S )zGenerate a display name from a custom endpoint URL.

    Returns a human-friendly label like "Local (localhost:11434)" or
    "RunPod (xyz.runpod.io)".  Used as the default when prompting the
    user for a display name during custom endpoint setup.
    r   Nr,  rR   r-  r  z/v1/?$	localhost	127.0.0.1zLocal (rr   runpodzRunPod ()r   r3  r  subrf   r6   
capitalize)r  r   cleanr   s       r   _auto_provider_namer	  g  s     IIIZ,,44YCCJJ3OOEFF9b%((E;;sADdkT11    	TZZ\\	!	!!$!!!  KrH   c                    t          |                     dd          pd                                          }|r|S t          |                     dd          pd                                          }|r=t          |                     dd          pd                                          sd| dS t          |pd                                          S )zDReturn the value that should be persisted for a custom provider key.r	  rR   r  r  r  }r4   r(   r5   )r  resolved_api_keyr	  r  s       r   %_custom_provider_api_key_config_valuer  |  s    m''r::@bAAGGIIK -##Ir228b99??AAG !s=,,Y;;ArBBHHJJ ! W    %2&&,,...rH   c                     t          |                     dd          pd                                          }|r|S t          |pd                                          S )zDReturn the value that should be persisted for a custom provider URL.r
  rR   r  )r  resolved_base_urlr
  s      r   &_custom_provider_base_url_config_valuer    s^    }((<<BCCIIKKL  &B''--///rH   c                    ddl m}m}  |            }|                    d          pg }	t	          |	t
                    sg }	|	D ]}
t	          |
t                    r|
                    dd                              d          |                     d          k    rd}|r |
                    d          |k    r||
d<   d	}|r=|r;|
                    d
i           }t	          |t                    si }d|i||<   ||
d
<   d	}|r!|
                    d          |k    r||
d<   d	}nd|
v r|
                    dd           d	}|r|	|d<    ||            dS |st          |           }|| d}
|r||
d<   |r||
d<   |r||
d<   |r|r	|d|ii|
d
<   |	
                    |
           |	|d<    ||           t          d| d           dS )u  Save a custom endpoint to custom_providers in config.yaml.

    Deduplicates by base_url — if the URL already exists, updates the
    model name, context_length, and api_mode but doesn't add a duplicate entry.
    Uses *name* when provided, otherwise auto-generates from the URL.
    r   r  r  r  rR   r  Fr  Tr  context_lengthr  N)r   r  r  u%     💾 Saved to custom providers as "z" (edit in config.yaml))r  r  r  r(   r2   r  r3   r  rb  r	  r  r|   )r  r  r  r  r   r  r  r  r  r  r  r  
models_cfgs                r   _save_custom_providerr    s    ;:::::::
+--C*++1rIi&& 	   eT"" 	uyyR'@'@'G'G(
 (
__S!!(" (" G 7++u44!&g  "YYx44
!*d33 $!#J%5~$F
5!",h 99Z((H44(0E*%"Gu$$		*d+++ !*3&'C   FF  -"8,,x00E #"i g %$j F F #3^"DEhU'CK	
O$
O
O
OPPPPPrH   c                    ddl m}m}  |            }|                    d          pg }t	          |t
                    r|st          d           dS t          d           g }|D ]}t	          |t                    r|                    dd          }|                    d	d
          }|                    dd
                              dd
          	                    d          }	|
                    | d|	 d           |
                    t          |                     |
                    d           	 ddlm}
  |
dt          |          dd          }t                       |dk     rd}n# t          t          t           t"          j        f$ r t'          |d          D ]\  }}t          d| d|            t                       	 t)          dt+          |           d                                          }|rt/          |          dz
  nd}n# t0          t2          t4          f$ r d}Y nw xY wY nw xY w||t+          |          k    rt          d           dS |                    |          }||d<    ||           t	          |t                    r|                    dd          nt          |          }t          d| d           dS )z=Let the user remove a saved custom provider from config.yaml.r   r  r  zNo custom providers configured.NzRemove a custom provider:
r   unnamedr  rR   r,  r-  r  rq   rr   Cancelcurses_radiolistzSelect provider to remove:r  r  cancel_returnsrB   r  r$  r  r&  r4  u   ✅ Removed "z" from custom providers.)r  r  r  r(   r2   r  r|   r3   r3  r  r  r4   hermes_cli.curses_uir  r   NotImplementedErrorrM   rA  r  r  r)  r   r5   r   r   r*  r+  rb  )r`  r  r  r  r  r  r  r   r  ro  r  r.  r   r  r  removedremoved_names                    r   r^  r^    s.   ::::::::
+--C*++1rIi&& i /000	
'(((G ' 'eT"" 	'99VY//D))J++CJ33;;IrJJQQRUVVINNd22i2223333NN3u::&&&&NN8999999(MM	
 
 
 	77C,gz7QR   gq)) 	! 	!DAq-q--A--    	6S\\66677==??C"%/#c((Q,,4CC-x8 	 	 	CCC	 {cS^^++lmmC  G'CK*4Wd*C*CUFI&&&W  

@,
@
@
@AAAAAs8   :8E3 3AH:AHH:H41H:3H44H:9H:_PROVIDER_MODELSc                 z    | t           v rddlm} |t                      | <   |S t	          dt
          d|           )zADefer the model-catalog import until something actually reads it.r   r!  zmodule z has no attribute )_LAZY_MODEL_EXPORTSr  r"  globalsr  __name__)r   r"  s     r   __getattr__r'    sR    """666666*		$
I8IIII
J
JJrH   c                     |                      d          }t          |t                    rHt          |                     d          pd                                                                          S dS )Nagentreasoning_effortrR   )r(   r2   r3   r4   r5   r6   )r`  	agent_cfgs     r   _current_reasoning_effortr,  &  sc    

7##I)T"" L9==!344:;;AACCIIKKK2rH   effortc                 r    |                      d          }t          |t                    si }|| d<   ||d<   d S )Nr)  r*  )r(   r2   r3   )r`  r-  r+  s      r   _set_reasoning_effortr/  -  sD    

7##Ii&& $	#w$*I !!!rH   c           	         t          t                              d | D                                 dfdD             }|                    fdD                        |sdS fdd}d}d	k    rt	          |          }n6|v r|                              }nd
|v r|                    d
          }nd}	 ddlm} fd|D             }|                    |           |                    |            |d||d          }|dk     rdS t                       |t	          |          k     r||         S |t	          |          k    rd	S dS # t          t          t          t          j        f$ r Y nw xY wt          d           t          |d          D ]#\  }	}
t          d|	 d |
                      $t	          |          }t          d|dz    d|            t          d|dz    d|            t                       	 	 t!          d|dz    d                                          }|sdS t%          |          }d|cxk    r|k    rn n||dz
           S ||dz   k    rd	S ||dz   k    rdS t          d|dz               n2# t&          $ r t          d           Y nt(          t*          f$ r Y dS w xY w)zOPrompt for a reasoning effort. Returns effort, 'none', or None to keep current.c              3      K   | ]X}t          |                                          #t          |                                                                          V  Yd S r  r   )r  r-  s     r   r  z5_prompt_reasoning_effort_selection.<locals>.<genexpr>8  sk       
 
,2VARARATAT
KK%%''
 
 
 
 
 
rH   )minimallowmediumhighxhighc                     g | ]}|v |	S rN   rN   )r  r-  dedupeds     r   r  z6_prompt_reasoning_effort_selection.<locals>.<listcomp>=  s#    III&v7H7Hv7H7H7HrH   c              3   $   K   | ]
}|v|V  d S r  rN   )r  r-  canonical_orders     r   r  z5_prompt_reasoning_effort_selection.<locals>.<genexpr>>  s-      QQf63P3P63P3P3P3PQQrH   Nc                     | k    r|  dS | S )Nu     ← currently in userN   )r-  current_efforts    r   r  z2_prompt_reasoning_effort_selection.<locals>._labelB  s"    ^##4444rH   zDisable reasoningzSkip (keep current)r!  r4  r   r  c                 &    g | ]} |          S rN   rN   )r  r-  r  s     r   r  z6_prompt_reasoning_effort_selection.<locals>.<listcomp>V  s!    888f66&>>888rH   zSelect reasoning effort:r  r  rB   r  r$  r   Tr  z] (default: keep current): r  r  )r  r3   fromkeysrZ  r   r   r  r  r  r|   r   r  rM   rA  r  r  r)  r5   r   r   r*  r+  )effortsr<  rj  disable_label
skip_labelrk  r  r  r.  r   r-  r  r  r  r:  r8  s    `           @@@r   "_prompt_reasoning_effort_selectionrB  5  s    
 
6=
 
 
 	
 	
 G
 DOIIIIOIIIGNNQQQQQQQQQQ t    
 (M&J'll	7	"	"mmN33	W		mmH--9999998888888}%%%z"""& 	
 
 
 774W3<#g,,6t,gz7QR    

$%%%w** * *	6(1((v(())))GA	
'q1u
'
'
'
'(((	
$q1u
$
$

$
$%%%	GGG	JAJJJKKQQSSF tf++CC}}}}1}}}}}sQw''a!e||va!e||t+AE++,,,, 	+ 	+ 	+)*****!8, 	 	 	44	sO   AE (E ?E "E;:E;*J <)J &	J 1	J <J K-K Kexisting_keyc                 ,  	
 ddl m	 ddlm} ddlm | j        r| j        d         nd
dt          dt          f	
fd}|sht          d	| j
         d
           
sdS  |d          }|st          d           dS  |
|           t          d           t                       |dfS ddlm} 
r |
          nd}t          d| j
         d|dd          d|            
st                       |dfS 	 t          d                                                                          }n'# t           t"          f$ r t                       d}Y nw xY w|                    d          r\ |d          }|s!t          d           t                       |dfS  |
|           t          d           t                       |dfS |                    d          r& |
d           t          d| j
         d           dS t                       |dfS )u  Shared API-key entry point for ``hermes setup`` / ``hermes model``.

    Handles both first-time entry and the already-configured case.  When a key
    is already present, offers [K]eep / [R]eplace / [C]lear so the user can
    recover from a malformed paste without editing ``~/.hermes/.env`` by hand.

    Returns ``(resolved_key, abort)``.  ``abort=True`` means the caller should
    ``return`` immediately — the user cancelled entry, declined to replace, or
    cleared the key and is now unconfigured.
    r   )LMSTUDIO_NOAUTH_PLACEHOLDER)r  r  rR   allow_lmstudio_defaultr   c                     dk    r| r	 dd}n d}	  |                                           }n&# t          t          f$ r t                       Y dS w xY w|s
dk    r| rS |S )NrT  z (Enter for no-auth default r  z (or Enter to cancel): rR   )r5   r*  r+  r|   )rF  promptenteredrE  r  r  r  s      r   _prompt_new_keyz(_prompt_api_key.<locals>._prompt_new_key  s    *$$)?$__=X___FF888F	**62288::GG!8, 	 	 	GGG22	  	/;*449O4..s   7 AAzNo z API key configured.)rR   TT)rF  
Cancelled.zAPI key saved.F)format_secret_source_suffixr  z
 API key: Nr  u   ... ✓z,  [K]eep / [R]eplace / [C]lear (default K): r  r  z  No change.z  API key updated.r  z7  API key cleared.  Re-run `hermes setup` to configure z again.)r  rE  r  r  r  r  r  rV   r4   r|   r   hermes_cli.env_loaderrL  r)  r5   r6   r*  r+  rW   )r  rC  r  r  rJ  new_keyrL  source_suffixr  rE  r  r  s     `      @@@r   _prompt_api_keyrP    s    <;;;;;000000======-4-EMg&q))2G4 C           6GL666777 	8!/>>> 	,8w(((~ BAAAAA<CK//888M	
Ow|
O
O|BQB'7
O
O
O
OPPP #U""EFFLLNNTTVVx(     	!/??? 	'.!!!GGG&&w((("###~ w###[gl[[[	
 	
 	
 x 
GGGs   =3D1 1!EEc                 b    | pd                                                                 }d|v rdS dS )z>Infer the current StepFun region from the configured endpoint.rR   zapi.stepfun.comchinainternational)r5   r6   )r  r  s     r   _infer_stepfun_regionrT    s8    .b''))//11JJ&&w?rH   regionc                 &    ddl m}m} | dk    r|n|S )Nr   )STEPFUN_STEP_PLAN_CN_BASE_URLSTEPFUN_STEP_PLAN_INTL_BASE_URLrR  )r  rW  rX  )rU  rW  rX  s      r   _stepfun_base_url_for_regionrY    sG            W 	&%,rH   c                    	 ddl m}mm ddlm}m	 dt          f 	fd}	 t                       t          d           t          d           t                        |            }|r* |            rdS  || 	           t          d
           dS t                       t          d           t                       ddl	m
} 	  |d                                          }n&# t          t          f$ r t                       Y dS w xY w|r || 	           t          d           dS t          d           dS # t          $ r/ t                       t          d           t                       t          d           t                       t          d           t          d           t          d           t          d           t                       t          d           t                       ddl	m
} 	  |d                                          }n'# t          t          f$ r t                       Y Y dS w xY w|r || 	           t          d           Y dS t          d           Y dS w xY w)zNRun the Claude OAuth setup-token flow. Returns True if credentials were saved.r   )run_oauth_setup_tokenr  r  )save_anthropic_oauth_token%use_anthropic_claude_code_credentialsr   c                     	              } n# t           $ r d } Y nw xY w| rk |           s"t          |                     d                    r>            t          d           ddlm} t          d |             d           dS d	S )
Nr  save_fnu%     ✓ Claude Code credentials linked.r   display_hermes_homez]    Hermes will use Claude's credential store directly instead of copying a setup-token into z/.env.TF)r   rV   r(   r|   r   rb  )r  _dhh_fnr  r  r  r]  s     r   ._activate_claude_code_credentials_if_availablezQ_run_anthropic_oauth_flow.<locals>._activate_claude_code_credentials_if_available  s    	0022EE 	 	 	EEE	 
	&&u--
	15eii6O6O1P1P
	 21.IIII9:::GGGGGG Bpwpwpypy  B  B  B   4us   
 u<     Running 'claude setup-token' — follow the prompts below.z9  A browser window will open for you to authorize access.Tr_  u     ✓ OAuth credentials saved.z8  If the setup-token was displayed above, paste it here:r  z*  Paste setup-token (or Enter to cancel): Fu     ✓ Setup-token saved.u)     ⚠ Could not detect saved credentials.z/  The 'claude' CLI is required for OAuth login.z  To install and authenticate:zE    1. Install Claude Code:  npm install -g @anthropic-ai/claude-codez/    2. Run:                  claude setup-tokenz.    3. Follow the browser prompts to authorizez)    4. Re-run:               hermes modelz8  Or paste an existing setup-token now (sk-ant-oat-...):z$  Setup-token (or Enter to cancel): u2     Cancelled — install Claude Code and try again.)r  r[  r  r  r  r\  r]  rV   r|   r  r  r5   r*  r+  r   )
r  r[  r\  rd  tokenr  manual_tokenr  r  r]  s
   `      @@@r   _run_anthropic_oauth_flowrg    s[            
       
D         $<LMMMIJJJ%%'' 	==?? t&&unEEEE23334 	HIIIAAAAAA	//< egg L "8, 	 	 	GGG55	  	&&|^LLLL,---49:::u   ?@@@.///UVVV?@@@>???9:::HIIIAAAAAA	(()OPPVVXXEE!8, 	 	 	GGG555	  	&&unEEEE,---44BCCCuu5ss   AD? :D? 1D? 
C( 'D? (DD? 
D!D? .D? ?C I9 HI9I=I9I!I9&I98I9c                 (    ddl m}  ||            dS )z(Authenticate Hermes CLI with a provider.r   )login_commandN)r  ri  )rx  ri  s     r   	cmd_loginrj  Z  s*    ------M$rH   c                 (    ddl m}  ||            dS )zClear provider authentication.r   )logout_commandN)r  rl  )rx  rl  s     r   
cmd_logoutrm  a  s*    ......N4rH   c                 (    ddl m}  ||            dS )zManage pooled credentials.r   )auth_commandN)hermes_cli.auth_commandsro  )rx  ro  s     r   cmd_authrq  h  s*    555555LrH   c                 (    ddl m}  ||            dS )zShow status of all components.r   )show_statusN)hermes_cli.statusrs  )rx  rs  s     r   
cmd_statusru  o  s*    ------KrH   c                 (    ddl m}  ||            dS )zCron job management.r   )cron_commandN)hermes_cli.cronrw  )rx  rw  s     r   cmd_cronry  v  s*    ,,,,,,LrH   c                 (    ddl m}  ||            dS )z Webhook subscription management.r   )webhook_commandN)hermes_cli.webhookr{  )rx  r{  s     r   cmd_webhookr}  }  s*    222222ODrH   c                     t          | dd          }|dv rt          dt          j                   dS |dk    rdd	lm}  ||           S t          d
| t          j                   dS )u   Slack integration helpers.

    Dispatches ``hermes slack <subcommand>``. Currently supports:
      manifest — print or write a Slack app manifest with every gateway
                 command registered as a first-class slash.
    slack_commandN>   NrR   zusage: hermes slack <subcommand>

subcommands:
  manifest   Generate a Slack app manifest with every gateway
             command registered as a native slash

Run `hermes slack manifest -h` for details.r   rB   manifestr   )slack_manifest_commandzUnknown slack subcommand: )r0   r|   rE   r   hermes_cli.slack_clir  )rx  r  r  s      r   	cmd_slackr    s     $
.
.C
j: 		
 		
 		
 		
 q
j??????%%d+++	
,s
,
,3:>>>>1rH   c                 $    ddl m}  ||           S )z"Multi-profile collaboration board.r   )kanban_command)hermes_cli.kanbanr  )rx  r  s     r   
cmd_kanbanr    s$    000000>$rH   c                 $    ddl m}  ||           S )z1Manage projects (named, multi-folder workspaces).r   )projects_command)hermes_cli.projects_cmdr  )rx  r  s     r   cmd_projectr    s%    888888D!!!rH   c                 (    ddl m}  ||            dS )z%Shell-hook inspection and management.r   )hooks_commandN)hermes_cli.hooksr  )rx  r  s     r   	cmd_hooksr    s*    ......M$rH   c                 (    ddl m}  ||            dS )z%Check configuration and dependencies.r   
run_doctorN)hermes_cli.doctorr  )rx  r  s     r   
cmd_doctorr    *    ,,,,,,JtrH   c                     t          | dd          }|dv r4ddlm}  ||           }t          j        t          |pd                     t          d| t          j                   t          j        d           dS )	z$Dispatch `hermes security <subcmd>`.security_commandN)auditNr   )cmd_security_auditzunknown security subcommand: r   r   )r0   hermes_cli.security_auditr  rE   r   r   r|   r   )rx  r  r  rl  s       r   cmd_securityr    s    
$*D
1
1C
o@@@@@@ "!$''TYQ   	
/#
/
/cjAAAAHQKKKKKrH   c                 (    ddl m}  ||            dS )z)Dump setup summary for support/debugging.r   )run_dumpN)hermes_cli.dumpr  )rx  r  s     r   cmd_dumpr    s%    ((((((HTNNNNNrH   c                 (    ddl m}  ||            dS )z!Debug tools (share report, etc.).r   )	run_debugN)hermes_cli.debugr  )rx  r  s     r   	cmd_debugr    s%    ******IdOOOOOrH   c                 (    ddl m}  ||            dS )zConfiguration management.r   )config_commandN)r  r  )rx  r  s     r   
cmd_configr    s*    000000N4rH   c                 p    t          | dd          rddlm}  ||            dS ddlm}  ||            dS )z,Back up Hermes home directory to a zip file.quickFr   )run_quick_backup)
run_backupN)r0   hermes_cli.backupr  r  )rx  r  r  s      r   
cmd_backupr    sg    tWe$$ 666666000000
4rH   c                 (    ddl m}  ||            dS )z(Restore a Hermes backup from a zip file.r   )
run_importN)r  r  )rx  r  s     r   
cmd_importr    r  rH   check_updatesr  c           	         ddl m} t           |                       t          dt                      t          dt          j                                        d                     	 ddlm}m} 	 t          d |d                      n# |$ r t          d           Y nw xY wn# t          $ r t          d           Y nw xY w| sd S 	 dd	l m
} dd
lm}  |            }|r3|dk    r-|dk    rdnd}t          d| d| d |             d           d S |dk    rt          d           d S d S # t          $ r Y d S w xY w)Nr   )format_banner_version_labelrs   rt   )r]   PackageNotFoundErrorru   r`   rv   )check_for_updatesrecommended_update_commandrB   commitcommitszUpdate available: r7  u    behind — run ''z
Up to date)r  r  r|   rQ  rE   r]   rf   importlib.metadatar  r   r  r  r  r   )r  r  _pkg_versionr  r  r  behindcommits_words           r   _print_version_infor    s   ======	
%
%
'
'(((	
$l
$
$%%% 

-S[&&((+
-
-...
+TTTTTTTT	/9h!7!799::::# 	/ 	/ 	/-.....	/ + + +)*****+  777777@@@@@@""$$ 	 fqjj'-{{88	L8V 8 8l 8 822448 8 8     q[[, [   sO   *B* 3B B* B&#B* %B&&B* *CCA	D2 D2 2
E ?E c                 &    t          d           dS )zShow version.Tr  N)r  r  s    r   cmd_versionr    s    d++++++rH   c                 t   t          | dd          r1ddlm} t          t	          j         |                                 dS t          | dd          r3t          | dd          st          d           dd	lm}  ||            dS t          | dd          st          d
           ddlm	}  ||            dS )z9Uninstall Hermes Agent (or just the Chat GUI with --gui).gui_summaryFr   )gui_install_summaryNr  r  zuninstall --gui)run_gui_uninstall	uninstall)run_uninstall)
r0   hermes_cli.gui_uninstallr  r|   r  dumpsr   hermes_cli.uninstallr  r  )rx  r  r  r  s       r   cmd_uninstallr  $  s     t]E** @@@@@@dj,,..//000 tUE"" tUE** 	,*+++::::::$
 4&& "[!!!222222M$rH   c                 &   d}t          j        |           D ]y\  }}}d |D             |dd<   t           j                            |          dk    r?	 t	          j        |           |dz  }n# t          $ r Y nw xY w|                                 z|S )a~  Remove all __pycache__ directories under *root*.

    Stale .pyc files can cause ImportError after code updates when Python
    loads a cached bytecode file that references names that no longer exist
    (or don't yet exist) in the updated source.  Clearing them forces Python
    to recompile from the .py source on next import.

    Returns the number of directories removed.
    r   c                     g | ]}|d v|	S )>   
.worktreesvenvr  r>  .venvrN   )r  ds     r   r  z)_clear_bytecode_cache.<locals>.<listcomp>O  s.     
 
 
OOO OOOrH   N__pycache__rB   )r&   walkr)   basenamerW  r  rM   r  )r   r  dirpathdirnamesrG  s        r   _clear_bytecode_cacher  B  s     G "  1
 

 
 

 7G$$55g&&&1   NNNs   A,,
A98A9)zhermes_cli/main.pyzhermes_cli/config.pyzhermes_cli/__init__.pyzcli.pyzrun_agent.pyzmodel_tools.pyztoolsets.pyzhermes_constants.pyc                     	 t          j        | ddgz   |ddd          }|j                                        pdS # t           j        t
          f$ r Y dS w xY w)z=Return the current HEAD SHA, or None if it can't be resolved.	rev-parserB  Tr  N)rA  rB  r  r5   CalledProcessErrorrM   git_cmdr  r  s      r   _capture_head_shar  n  s{    
{F++
 
 
 }""$$,,)73   tts   8; AAc                    ddl }ddl}t          |           } |                    d          5 }t          D ]}| |z  }|                                st          |          |                    dd          dz   z  }	 |                    t          |          t          |          d	           y# |j	        $ r7}d
t          |          t          |          fcY d}~c cddd           S d}~wt          $ r-}d
t          |          d| fcY d}~c cddd           S d}~ww xY w	 ddd           n# 1 swxY w Y   dS )u  Compile each file in ``_UPDATE_CRITICAL_FILES`` to catch SyntaxErrors.

    These are the files imported on every ``hermes`` startup; if any of them
    has a syntax error (orphan merge-conflict markers, bad ref to a name
    that no longer exists, etc.) the CLI can't bootstrap at all. We validate
    them after a successful ``git pull`` so we can auto-roll-back instead of
    leaving the user with a bricked install.

    The compiled ``.pyc`` is written to a temp directory rather than the
    source tree's ``__pycache__/`` so we don't race with concurrent test
    workers that walk the same dir, and so we don't leave a stale pyc
    behind in production if the next interpreter run picks a different
    Python version. The pyc is discarded on function return either way —
    we only care about the compile-or-not signal.

    Returns ``(ok, failing_path, error_message)``. ``ok=True`` means every
    file parsed cleanly.
    r   Nzhermes-syntax-check-rX   r  __r  T)cfiledoraiseFzcould not read: )TNN)
py_compiler[  r   TemporaryDirectory_UPDATE_CRITICAL_FILESr,   r3  compiler4   PyCompileErrorrM   )r   r  r[  tmpdirrelpathr)   r  r   s           r   _validate_critical_files_syntaxr  }  s'   & OOO::D		$	$,B	$	C	C Bv- 	B 	BG'>D;;==   LLGOOC$>$>$DEEB""3t99CJJ"MMMM, 2 2 2c$iiS11111111B B B B B B B B  B B Bc$ii)AC)A)AAAAAAAAAB B B B B B B BB	BB B B B B B B B B B B B B B B  sZ   AD5<2B/.D5/
D%9C+D%D5+D%8D D%D5 D%%D55D9<D9     r@prompt_textr   r?  c                    ddl }ddl}ddlm}  |            }|dz  }|dz  }|                    d           | |t          |                                          d}	|                    d	          }
|
                    |	                    |	                     |

                    |           t          j                    |z   }t          j                    |k     r|                                rp	 |                                                                }|                    d           |                    d           |r|n|S # t           t"          f$ r Y nw xY wt          j        d
           t          j                    |k     |                    d           |                    d           t'          dt)          |           d|d           |S )aw  File-based IPC prompt for gateway mode.

    Writes a prompt marker file so the gateway can forward the question to the
    user, then polls for a response file.  Falls back to *default* on timeout.

    Used by ``hermes update --gateway`` so interactive prompts (stash restore,
    config migration) are forwarded to the messenger instead of being silently
    skipped.
    r   Nr   z.update_prompt.jsonz.update_responseT)
missing_ok)rH  r   r  z.tmp      ?z  (no response after zs, using default: rr   )r  uuidr   r   rd  r4   uuid4with_suffixre  r  r3  r{  	monotonicr,   r   r5   rM   r   sleepr|   r   )r  r   r?  _json_uuidr   r8   prompt_pathresponse_pathpayloadtmpdeadlineanswers                r   _gateway_promptr    s    000000?D..K--M D))) %++--   G
 
!
!&
)
)CNN5;;w''(((KK   7*H
/

h
&
&!! 	&002288::$$$555""d"333!'4vvW4Z(   C /

h
&
& $'''D)))	
N#g,,
N
N'
N
N
NOOONs   2AE
 
EEweb_dirc                 *   | j         j        dk    r| j         j         n| j         }|dz  dz  }|dz  dz  }|                                s|dz  }|                                sdS |                                j        }t          dd	h          t          j        | d
          D ]x\  }}}fd|D             |dd<   |D ]\}|                    d          rEt          j	        
                    t          j	                            ||                    |k    r  dS ]ydD ];}	| |	z  }
|
                                r |
                                j        |k    r dS <|dz  }|                                r|                                j        |k    rdS dS )a  Return True if the web UI dist is missing or stale.

    Mirrors the staleness logic used by ``_tui_build_needed()`` for the TUI.
    The dashboard source lives under ``web/``, but the Vite build
    still outputs to ``hermes_cli/web_dist/`` (per vite.config.ts
    outDir: "../hermes_cli/web_dist"), NOT to ``web/dist/``, so Python
    packaging can continue serving the same static asset directory. Uses the
    Vite manifest as the sentinel because it is written last and therefore
    has the newest mtime of any build output.
    appsrw   web_distz.vitezmanifest.json
index.htmlTr  r  topdownc                     g | ]}|v|	S rN   rN   )r  r  skips     r   r  z(_web_ui_build_needed.<locals>.<listcomp>  s    <<<QatmmqmmmrH   N)r  r  r  r  z.cssz.htmlz.vue)r  z	yarn.lockzpnpm-lock.yamlzvite.config.tszvite.config.jsr  F)r   r   r,   rR  r  	frozensetr&   r  endswithr)   getmtimer*   )r  r}   dist_dirsentinel
dist_mtimer  r  	filenamesfnmetamp	root_lockr  s               @r   _web_ui_build_neededr    s    -4N,?6,I,I7>((w~Ll*Z7H'!O3H?? +l*?? t)Jnf-..D(*(F(F(F    $9<<<<(<<< 	  	 B{{RSS  7##BGLL"$=$=>>KK444	  	 	 t^99;; 	27799-
::4422I inn..7*DDt5rH          )idle_timeout_secondsindentr,  r  r  r  c                   g t          j                    t          j                    	 t	          j        | |t          j        t          j        dddd|	  	        n<# t          $ r/}t	          j	        | ddt          |                    cY d	}~S d	}~ww xY wdfd}t          j        |d          }|                                 d}	 	                     d          }	n# t          j        $ r 5  t          j                    z
  }
d	d	d	           n# 1 swxY w Y   |
|k    rmd}                                 	                     d          }	n=# t          j        $ r+                                                                  }	Y nw xY wY nY nw xY w|                    d           d                              }|rd| d}||z  }|	dk    rd}	t	          j	        | |	|d          S )u  Run a subprocess that streams output, with an idle-output timeout.

    Issue #33788: ``npm run build`` (Vite) was invoked with
    ``capture_output=True`` and no timeout. On low-memory hosts (notably
    WSL2 with the default 4 GB cap) the build can stall or sit silent for
    minutes; users see a frozen terminal, assume the update is hung, and
    reboot — leaving the editable install in a half-state with the
    ``hermes`` launcher present but ``hermes_cli`` not importable.

    This helper fixes both halves: stdout is streamed (so the user sees
    progress), and if no bytes have appeared on stdout/stderr for
    ``idle_timeout_seconds``, the process is terminated and the call
    returns with a non-zero ``returncode``. The caller's existing
    stale-dist fallback (#23817) takes over from there.

    Returns a ``CompletedProcess`` with merged stdout (text), empty
    stderr, and an integer returncode. Never raises on idle timeout —
    propagation of failure is via the returncode.
    Tr   r3  rB   )r  r  r   r9  r   r5  bufsizer,  r  rR   )r  r   Nr   c                     j         J j         D ]} 	 t           |                                  d           n# t          $ rs t	          t
          j         dd           pd}|                                                     |d                              |d          }t           | d           Y nw xY w5                      |            t          j
                    d d d            n# 1 swxY w Y   d S )NTflushr   asciir3  r5  )r  r|   r  UnicodeEncodeErrorr0   rE   encodedecoder  r{  r  )rj   encsafer  last_output_tsr  merged_chunksprocs      r   _readerz'_run_with_idle_timeout.<locals>._reader3  sd   {&&&K 
	3 
	3D5000=====% 5 5 5cj*d;;Fw{{}}++C	+BBII#V_I``'''t444444	5
  3 3$$T***!&!2!23 3 3 3 3 3 3 3 3 3 3 3 3 3 3
	3 
	3s"   '=A:B:9B:?)C44C8	;C8	r~  daemonFr  r?  r  r   u$   
  ⚠ Build produced no output for u   s — terminated.
    Common causes: out-of-memory on a low-RAM host (WSL/container),
    a stuck Node process, or an antivirus scan stalling I/O.
r   |   r   N)r{  r  	threadingLockrA  Popenr  STDOUTrM   CompletedProcessr4   Threadr   waitrC  	terminatekillr*   )r9  r  r  r  r,  r   r%  reader_threadidle_killedr  idler  r  r"  r  r#  r$  s      `         @@@@r   _run_with_idle_timeoutr7    s   6  "M_&&N>DQ?$

 

 

  Q Q Q*3Bs3xxPPPPPPPPPQ3 3 3 3 3 3 3 3 3 3 $GDAAAMK	1%%B( 	 	 	 : :((>9: : : : : : : : : : : : : : :***"   %1--BB!0 % % %IIKKKBBB%  +*		$ q!!!ww}%%H 	M4H M M M 	
 	C77B&sBxKKKKsw   1A! !
B+$BBBC, ,F!=D F! D$	$F!'D$	(F!EF!7FF!FF! F!c                     ddl } 	 t          d                              d          }n# t          $ r Y dS w xY w|                     d|| j                  sdS t          j        d          rdS dD ]G}t          |z  d	z  dz  }|	                                r!i t          j        d
t          |          ic S H	 t          j        g ddddd          }|j        dk    rN|j                                        }|r3t          |          	                                ri t          j        d
|iS n# t$          $ r Y nw xY wdS )u  Return extra env vars for native module builds on NixOS.

    On NixOS, python3 is typically not on the system PATH (it lives in
    the Nix store and only enters PATH inside a nix-shell or when
    explicitly installed as a system package).  node-gyp uses Python to
    compile native addons like ``node-pty`` and its ``find-python.js``
    does a bare ``PATH`` lookup — which fails on NixOS.

    Two-tier resolution:
    1. Fast path — the hermes venv's python3 (present in managed installs)
    2. Fallback — resolves the absolute python3 path via ``nix-shell``

    Returns an env dict suitable for ``subprocess.run(env=...)`` or
    ``None`` when we are not on NixOS or python3 is already on PATH.
    r   Nz/etc/os-releaser   r   z
^ID=nixos$python3)r  r  r  PYTHON)z	nix-shellr   r9  z--runzwhich python3TFr   )r>  r9  r.  r?  )r   r   r   rM   searchMrW  rX  rQ  r,   r&   r'   r4   rA  rB  rY  r  r5   r   )r   
os_release	venv_namevenv_pythonr  python3_paths         r   _nixos_build_envrA  h  s     III+,,666HH

   tt99]J55 t |I t ' > >	"Y.6B 	>=bj=(C,<,<=====	>
DDDd%
 
 
 !!!=..00L >\ 2 2 9 9 ; ; >="*=h===    4s   #* 
88:A3D/ /
D<;D<rN   
extra_argsr>  r,  r  rC  r>  c          
         i t           j        |pi ddi}|dz  }|                                r/| dg|}t          j        ||||dddd	          }|j        d
k    r|S | dg|}	t          j        |	|||dddd	          S )u  Run a deterministic npm install that does not mutate ``package-lock.json``.

    Prefers ``npm ci`` (strict, lockfile-preserving) when a lockfile is present;
    falls back to ``npm install`` only if ``npm ci`` fails (e.g. lockfile out of
    sync on a WIP checkout).  Without this, ``npm install`` on npm ≥ 10 silently
    rewrites committed lockfiles (stripping ``"peer": true`` etc.), which leaves
    the working tree dirty and causes the next ``hermes update`` to stash the
    lockfile — repeatedly.
    r  rD   r  ciTr   r3  F)r  r,  r>  r9  r   r5  r.  r   r  )r&   r'   r,   rA  rB  rY  )
r  r  rC  r>  r,  run_envlockfileci_cmd	ci_resultinstall_cmds
             r   _run_npm_install_deterministicrK    s    ( 76	r6D#66G((H t)j)N)	
 	
 	
	 1$$ 	/J/K>%	 	 	 	rH   fatalrM  c                   | dz                                   sdS t          |           sdS dt          ddfdddlm}m}  |d	          }|s|r d
            d           | S  |            } d           d*fd}t          |           }|| k    rdnd}t                      rt          |           \  }}t          ||g |dR |          }	|	j
        dk    r4 d|rdnd d|rdndz               ||	           |r d           dS t          |ddg| |          }
|
j
        dk    r)t          j        d           t          |ddg| |          }
|
j
        dk    r|
j        pd|
j        pdz   }|                                }|r/d                     |                                d!d                   nd}| j        j        d"k    r| j        j        n| j        }|d#z  d$z  }|d%z  }|                                 r d&           |r d'|            dS  d|rdnd d(|rdndz               ||
           |r d           dS  d)           dS )+aW  Build the web UI frontend if npm is available.

    Args:
        web_dir: Path to the dashboard frontend source directory.
        fatal: If True, print error guidance and return False on failure
               instead of a soft warning (used by ``hermes web``).

    Returns True if the build succeeded or was skipped (no package.json).
    r  Tr9  r   Nc                     	 t          |            d S # t          $ rZ t          t          j        dd           pd}t          |                     |d                              |d                     Y d S w xY w)Nr   r  r3  r  )r|   r  r0   rE   r  r  r  )r9  r   s     r   _sayz_build_web_ui.<locals>._say  s    	^$KKKKK! 	^ 	^ 	^sz:t<<GH$++hy+99@@R[@\\]]]]]]	^s    A A76A7r   r  r  z3Web UI frontend not built and npm is not available.zBInstall Node.js, then run:  cd web && npm install && npm run buildu   → Building web UI...r  subprocess.CompletedProcessc                     | j         | j        fD ]d}|st          |t                    r)|                    dd                                          n|                                }|r |           edS )aR  Print captured npm output so users can see *why* a step failed.

        Windows users hitting `rm -rf` / `cp -r` errors (or any other
        sync-assets / Vite failure) would otherwise see only ``Web UI
        build failed`` with no hint of the underlying cause, because
        the npm calls run with ``capture_output=True``.
        r   r3  r  N)r  r   r2   bytesr  r  )r  blobr9  rP  s      r   _relayz_build_web_ui.<locals>._relay  s     ]FM2 	 	D FPQUW\F]F]p4;;wy;99@@BBBcgcncncpcpD T


	 	rH   rN   )r  webr  )rC  r,  r  u   ✗u   ⚠z Web UI npm install failedrR   z# (hermes web will not be available)zD  Run manually:  npm install --workspace web && npm run build -w webFrB  r  rU  r  
  ir  rw   r   r  u<     ⚠ Web UI build failed — serving stale dist as fallbackz  Build error:
  z Web UI build failedu     ✓ Web UI built)r  rQ  r   N)r,   r  r4   r   r  r  r  r/  r  rK  rY  r7  r{  r  r   r  r5   r*   r8  r   r   )r  rM  r  r  r  	build_envrU  r  r  r1r2build_outputstderr_previewstderr_tailr}   r	  
dist_indexrP  s                    @r   _build_web_uir_    s    n$,,.. t(( t^3 ^4 ^ ^ ^ ^ MLLLLLLL

u
%
%C  	WDFGGGDUVVVy%%''ID	!"""      g&&G 1870B0B""H^%'' Q&G&P&P##	'4'444	
 
 
B 
}F%*UFFFErr EG	
 	
 	
 	r


 	YDWXXXu 
 eW 57		R	R	RB	} 	A#S%$9wIVVV	}
 	RBIO<%++--HV^fkk.";";"="=cdd"CDDD\^070Cv0M0Mw~,,SZSa,.;,

  	DOPPP 97+778884@%*U@@@Err EG	
 	
 	
 	r


 	YDWXXXuD	4rH   desktop_dirc                 6    | dz  dz                                   S )z;Return True when a local desktop renderer build is present.r  r  r,   )r`  s    r   _desktop_dist_existsrc  N  s    & </77999rH   r}   c                 6    t          j                    dt          ddf fd}ddlm}  dz  }g }|                                r(|                    d	                                          }|                    d
|          dD ]]} |z  }|                                rBt          |
                                         }                    |          s ||           ^ dz  dz  }t          j        |d          D ]\  }	}
 fd|	D             |	dd<   t          |
          D ]V}t                    |z  }t          |
                                         }                    |          s ||           W                                S )a  Return a SHA-256 hex digest of all source files that feed the desktop build.

    Covers ``apps/desktop/`` (excluding anything matched by .gitignore)
    plus the root ``package.json`` / ``package-lock.json`` (workspace config
    that determines dependency resolution for the desktop workspace).

    Parses the repo-root ``.gitignore`` via *pathspec* so we automatically
    skip ``node_modules/``, ``dist/``, ``*.pyc``, etc. without maintaining
    a hardcoded skip-list.
    r)   r   Nc                    t          |                                         }                    |                                                               d           	 t	          | d          5 t          fdd          D ]}                    |           	 d d d            n# 1 swxY w Y   n# t          t          f$ r Y nw xY w                    d           d S )N    rbc                  .                          d          S )Ni   )r  r  s   r   <lambda>zC_compute_desktop_content_hash.<locals>._hash_file.<locals>.<lambda>{  s    !&&-- rH   rH   )r4   r  r  r  r.   iterrM   IOError)r)   rI  chunkr  hr}   s      @r   
_hash_filez1_compute_desktop_content_hash.<locals>._hash_fileu  s#   $""<0011			dD!! $Q!"7"7"7"7== $ $EHHUOOOO$$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ! 	 	 	D		s6   "B7 2,B+B7 +B//B7 2B/3B7 7C
Cr   )PathSpecz
.gitignorer   r   	gitignore)r  r  r  r  Tr  c           	          g | ]I}                     t          t                    |z                                                    G|JS rN   )
match_filer4   r   r  )r  r  r  r}   specs     r   r  z1_compute_desktop_content_hash.<locals>.<listcomp>  s\     
 
 
??3W(9'F'F|'T'T#U#UVV

 
 
rH   )hashlibsha256r   pathspecrp  rC  r   r8  
from_linesr4   r  rs  r&   r  r  	hexdigest)r}   ro  rp  rq  linesr   r  rI  r`  r  r  r  fpr  rn  rt  s   `            @@@r   _compute_desktop_content_hashr|  h  s    	A
 
$ 
 
 
 
 
 
 
 "!!!!!|+IE C##W#55@@BB{E22D 6  499;; 	ammL1122C??3'' 
1 ')3K(*T(J(J(J  $9
 
 
 
 
 

 
 

 ## 	 	Bg#Bbnn\2233C??3'' 
2		 ;;==rH   c                  (    ddl m}   |             dz  S )zCReturn the path to the desktop build stamp file under $HERMES_HOME.r   r   zdesktop-build-stamp.json)r   r   r   s    r   _desktop_stamp_pathr~    s(    000000?999rH   source_modec                   |rt          |           sdS nt          |           dS t                      }|                                sdS 	 t	          j        |                    d                    }n## t          t          j        t          f$ r Y dS w xY w|
                    d          |k    rdS |
                    d          }|sdS t          |          }||k    S )a   Return True when the desktop build output is stale or missing.

    Compares the current content hash against the saved stamp. Also returns
    True if the expected build artifact doesn't exist (e.g. first run after
    ``hermes update`` that pulled new source but hasn't built yet).
    TNr   r   
sourceModecontentHash)rc  _desktop_packaged_executabler~  rC  r  r  r   rM   r  KeyErrorr(   r|  )r`  r}   r  
stamp_file
stamp_data
saved_hashcurrent_hashs          r   _desktop_build_neededr    s     #K00 	4	 (44<4$&&J tZ
 4 4g 4 F FGG

T)84   tt ~~l##{22t..J t0>>L:%%s   (A4 4BBc                   t                      }	 |j                            dd           t          |           }ddlm}m} || |j        |j                                                  d}|	                    t          j        |d          dz   d	
           dS # t          $ r&}t                              d|           Y d}~dS d}~ww xY w)z7Write the desktop build stamp after a successful build.Tr`  r   r  timezone)r  r  builtAtr   )r  rc  r   r   z'Failed to write desktop build stamp: %sN)r~  r   rd  r|  r  r  nowutc	isoformatre  r  r  r   r]  r^  )r}   r  r  content_hashr  r  r  r   s           r   _write_desktop_build_stampr    s   $&&JEt<<<4\BB////////'%#x|HL11;;==
 


 	djA>>>EPWXXXXX E E E>DDDDDDDDDEs   BB 
C(C		Cc                 B   | dz  }t           j        dk    r#t          |                    d                    }nFt           j        dk    r|dz  dz  |dz  dz  |dz  dz  g}n|d	z  d
z  |d	z  dz  |dz  d
z  |dz  dz  g}d |D             }|sdS t	          |d           S )z?Return the current platform's unpacked Electron app executable.releasedarwinz%mac*/Hermes.app/Contents/MacOS/Hermeswin32zwin-unpackedz
Hermes.exezwin-ia32-unpackedzwin-arm64-unpackedzlinux-unpackedr   Hermeszlinux-arm64-unpackedc                 :    g | ]}|                                 |S rN   rb  r%  s     r   r  z0_desktop_packaged_executable.<locals>.<listcomp>  s%    444a4444rH   Nc                 4    |                                  j        S r  )rR  r  )r  s    r   rj  z._desktop_packaged_executable.<locals>.<lambda>  s    qvvxx'8 rH   )rF  )rE   r   r  globr  )r`  release_dirr  existings       r   r  r    s    	)K
|x+**+RSSTT

		 	 .(<7--<..=


 **X5**X5008;008;	

 54:444H tx889999rH   c                     t          j                    } g }t          j                            d          pt          j                            d          }|r"|                    t          |                     t          j        dk    r|                    | dz  dz  dz             nt          j        dk    rkt          j                            d          }|r(|                    t          |          dz  d	z             |                    | d
z  dz  dz  d	z             nat          j                            d          }|r%|                    t          |          dz             |                    | dz  dz             t                      }g }|D ]D}|	                                }||vr*|
                    |           |                    |           E|S )u  Return the per-user Electron download cache directories for this OS.

    electron-builder's ``app-builder unpack-electron`` extracts the Electron
    distribution from a zip stored in this cache (NOT from node_modules), so a
    corrupt zip here — not a bad workspace install — is what poisons the build.
    Honors the ``electron_config_cache`` / ``ELECTRON_CACHE`` overrides that
    ``@electron/get`` respects, then falls back to the platform defaults.
    electron_config_cacheELECTRON_CACHEr  LibraryCacheselectronr  LOCALAPPDATACacheAppDataLocalXDG_CACHE_HOMEz.cache)r   r8   r&   r'   r(   r  rE   r   setr+   r   )	r8   r  overridelocalxdgseenoutr  r  s	            r   _electron_download_cache_dirsr    s    9;;DJz~~566Z"*..IY:Z:ZH *$x..)))
|x$*X5
BCCCC		 	 
~.. 	Bd5kkJ6@AAA$*W4zAGKLLLLjnn-.. 	6d3ii*4555$/J6777eeDC  \\^^T>>HHRLLLJJrNNNJrH   c                    g }t                      D ]v}|                                st          |                    d                    D ]<}	 |                                 |                    |           -# t          $ r Y 9w xY ww| dz  }|                                rT|                    d          D ]>}	 t          j	        |d           |                    |           /# t          $ r Y ;w xY w|S )u   Clear the cached Electron download + half-written unpacked dir so the
    next ``pack`` re-downloads and re-stages from scratch.

    Root cause of the ``ENOENT … rename '…/linux-unpacked/electron' ->
    '…/linux-unpacked/Hermes'`` desktop build failure: a corrupt zip in the
    per-user Electron download cache (a partial download resumed into the same
    file leaves prepended/concatenated junk, or an interrupted write truncates
    it). electron-builder's ``app-builder unpack-electron`` extracts the
    distribution from that cached zip (NOT from node_modules); a bad zip yields
    a partial tree MISSING the 193 MB ``electron`` binary, so the final rename
    dies. Re-running repeats the same broken extraction forever.

    We deliberately do NOT try to detect corruption ourselves. stdlib
    ``zipfile`` silently tolerates the prepended/concatenated junk that is the
    most common corruption here — it reads from the end-of-central-directory
    backward, so ``testzip()`` returns clean on exactly the zips ``unzip -t``
    and ``@electron/get`` reject. Gating the purge on a self-rolled validator
    would therefore skip the real-world case and never self-heal. Instead, on a
    packaged-build failure we unconditionally remove the version's cached zips
    and the stale unpacked dir, then let the caller retry once: ``@electron/get``
    re-downloads with its own SHASUM verification (the real source of truth),
    and ``before-pack.cjs`` re-wipes the unpacked dir. If the failure was
    unrelated, a clean re-download is harmless and the retry fails the same way.

    Best-effort: never raises. Returns the paths removed so the caller can log
    them and decide whether a retry is worthwhile (empty list ⇒ nothing to
    clear, so no point retrying).
    zelectron-*.zipr  z
*-unpackedTr  )
r  r   r  r  rd  r  rM   r  rW  r  )r`  r  	cache_dirzip_pathr  unpackeds         r   _purge_electron_build_cacher    s=   : G244 
 
	!! 	y/?@@AA 	 	H!!!x((((    		 	)K #((66 	 	Hhd;;;;x((((    Ns$   )A77
BB9+C%%
C21C2z'https://npmmirror.com/mirrors/electron/c                 Z    | dz  dz  dz  dz  }|                                 r|S | dz  dz  S )a{  Return the Electron package directory the desktop workspace installs.

    npm may keep workspace-only dev dependencies under
    ``apps/desktop/node_modules`` instead of hoisting them to the repo root.
    Which layout you get depends on the npm version and what else is installed,
    so a build path that assumes one or the other breaks intermittently across
    machines. ``apps/desktop/package.json`` points electron-builder's
    ``electronDist`` at ``node_modules/electron/dist`` relative to the desktop
    project, so prefer the workspace-local package and fall back to the root
    hoist when that's where npm landed it.
    r  r  r  r  rb  )r}   desktop_locals     r   _electron_dirr  ^  sG     !6)I5FSM .(:55rH   c                     t          |           dz  }t          j        dk    r|dz  dz  dz  dz  S t          j        dk    r|dz  S |d	z  S )
at  Return the path to the Electron main binary inside the installed package.

    electron-builder reads the binary from ``build.electronDist`` since #38673,
    so this is the exact file whose absence makes a pack fail with "The
    specified electronDist does not exist". The basename differs per OS (the
    platform Electron is named for the host the build runs on).
    r  r  zElectron.appContentsMacOSElectronr  zelectron.exer  )r  rE   r   )r}   r  s     r   _electron_dist_binaryr  p  s`     &&/D
|xn$z1G;jHH
|wn$$*rH   c                 h    	 t          |                                           S # t          $ r Y dS w xY w)aP  True when ``node_modules/electron/dist`` holds a usable Electron binary.

    A directory that exists but is missing the binary (a partial extraction from
    a corrupt cached zip, or an interrupted postinstall) counts as NOT ok, since
    that is exactly the shape that makes electron-builder throw on the pinned
    electronDist.
    F)r  r,   rM   )r}   s    r   _electron_dist_okr    sC    $\2299;;;   uus    # 
11c                     t          |           }|dz                                  o&|dz                                  ot          |            S )uU   electron staged (package.json + install.js) but dist missing — blocked postinstall.r  
install.js)r  rC  r  )r}   electron_dirs     r   !_electron_pkg_staged_missing_distr    sT     ..L		&//11 	0L(1133	0!,///rH   mirrorr  c                   t          |           rdS t          |           }|dz  }|                                sdS ddlm}m}  |d          }|sdS |dz  }t          j        |d           	 |d	z                                   n# t          $ r Y nw xY w ||          }	|r||	d
<   	 t          j        |t          |          gt          |          |	d           n# t          $ r Y dS w xY wt          |           S )zKBest-effort: run electron's install.js to populate dist/ (optional mirror).Tr  Fr   r  r  r  r  zpath.txtELECTRON_MIRRORr  r,  r.  )r  r  rC  r   r  r  rW  r  rd  rM   rA  rB  r4   )
r}   r,  r  r  	installerr  r  r  r	  dl_envs
             r   _redownload_electron_distr    sf    && t ..L|+I uLLLLLLLL''D uf$H
M($////	
	"**,,,,    #"3''F +$* !c)nn-3|3D3D&X]^^^^^   uu\***s$   /B 
BB*4C 
C-,C-c                     t          | |          rdS |                    d          rdS t          | |t                    S )zDCanonical download, then fallback mirror unless the user pinned one.Tr  Fr  )r  r(   _ELECTRON_FALLBACK_MIRROR)r}   r,  s     r   _try_redownload_electron_distr    sI     s33 t
ww !! u$\3?XYYYYrH   c                    t           j        dk    rg S 	 ddl}n# t          $ r g cY S w xY w	 | dz                                  }n# t
          $ r g cY S w xY w|                                sg S t          j                    }g }	 |	                    ddg          }n# t          $ r g cY S w xY w|D ]}	 |j
        }n# t          $ r Y w xY w|                    d          }|                    d          }	|	r|||k    rP	 t          |	                                          }
n# t
          t          f$ r Y w xY w||
j        v r|                    |           g }|D ]N}	 |                                 |                    t#          |j                             ?# t          $ r Y Kw xY w|rV	 |                    |d          \  }}|D ]'}	 |                                 # t          $ r Y $w xY wn# t          $ r Y nw xY w|S )	u  Terminate any running desktop app executing from this build's ``release``
    dir so a rebuild can replace its (otherwise locked) executable.

    On Windows a running ``Hermes.exe`` keeps an exclusive lock on
    ``release/win-unpacked/Hermes.exe``. electron-builder's pack then can't
    delete the stale binary and dies with ``remove …\Hermes.exe: Access is
    denied`` / ``ERR_ELECTRON_BUILDER_CANNOT_EXECUTE`` (before-pack hits the same
    EPERM cleaning the dir). The retry path repeats the failure because the lock
    is still held. POSIX lets you unlink a running binary, so this is a no-op
    off-Windows.

    Scope is deliberately narrow: only processes whose executable lives *inside*
    this desktop's ``release`` tree are stopped — a packaged install elsewhere or
    an unrelated "Hermes" process is never touched. Best-effort: never raises.
    Returns the PIDs we asked to stop.
    r  r   Nr  pidexer  r(  )rE   r   psutilr   rD  rM   r   r&   getpidprocess_iterinfor(   r   r   ra  r  r2  r   r  
wait_procsr3  )r`  r  r  mevictims	proc_iterr$  r  r  r  exe_pathstoppedrG  alives                 r   %_stop_desktop_processes_locking_buildr    s   " |w	   			"Y.7799   			 		BG''77		   			 ! !	9DD 	 	 	H	hhuoohhuoo 	ckSBYY	Cyy((**HH$ 	 	 	H	(***NN4   G  	NNNN3tx==)))) 	 	 	H	 
	((!(<<HAu  IIKKKK    H
  	 	 	D	Ns    ((A AAB B)(B)1B99
CC?!D!!D54D5;F
F'&F'-G2 G! G2 !
G.+G2 -G..G2 2
G?>G?c           
         t           j        dk    rdS t          j                            d          st          j                            d          rdS t          |           }|dS |j        d         }t          |                              d          r|	                                sdS t          j        d          }|sdS 	 t          j        dd	t          |          gd
           t          j        |ddddt          |          gd
           dS # t          $ r}t          d| d           Y d}~dS d}~ww xY w)a  Make a locally-built (unsigned) macOS desktop app survive in-place self-update.

    An ad-hoc-signed .app has no stable Designated Requirement (no Team ID), so
    when the self-updater rebuilds the bundle in place with a fresh build (a new,
    different cdhash) Gatekeeper/LaunchServices treats the changed code as
    tampering and macOS reports "Hermes is damaged and can't be opened." The
    bundle also inherits the com.apple.quarantine flag from the downloaded
    installer process chain. Both make the relaunch fail.

    Clearing the quarantine xattrs and re-applying a clean deep ad-hoc signature
    (omitting the hardened-runtime flag, which is meaningless without a real
    Developer ID) lets the rebuilt app relaunch. No-op when a real signing
    identity is configured (CSC_LINK / APPLE_SIGNING_IDENTITY) so a properly
    signed/notarized build is never clobbered. Best-effort: never raises.
    r  NCSC_LINKAPPLE_SIGNING_IDENTITYr   z.appcodesignxattrz-crFr.  z--forcez--deepz--signr   z*  (warning: macOS relaunch fixup skipped: rr   )rE   r   r&   r'   r(   r  ra  r4   r  r   rW  rX  rA  rB  r   r|   )r`  r  appr  r   s        r   !_desktop_macos_relaunchable_fixupr    s[     |x	z~~j!! RZ^^4L%M%M 
&{
3
3C
{
+a.Cs88V$$ CJJLL |J''H CC1????)Xxc#hhOW\]]]]]] C C CA3AAABBBBBBBBBCs   AD 
D=D88D=c                    t           j        dk    s|rdS |                     d          s|                     d          rdS d| v rdS d| d<   dS )u"  Stop electron-builder grabbing a random keychain identity on self-update.

    The desktop self-updater rebuilds *and re-signs the .app on the end user's
    machine* (``hermes desktop --build-only`` → electron-builder ``--dir``).
    With ``CSC_IDENTITY_AUTO_DISCOVERY`` on (its default), electron-builder
    signs the ``type=distribution``, hardened-runtime bundle with whatever it
    finds in that user's keychain — typically a personal "Apple Development"
    cert. That stalls/fails the sign step (no Developer ID + no provisioning
    profile) or clobbers your real notarized signature with an unusable one, so
    every post-update launch trips Gatekeeper.

    Force ad-hoc signing for the local packaged rebuild instead: deterministic,
    and exactly what ``_desktop_macos_relaunchable_fixup`` already finishes off.
    No-op for source runs, off-macOS, when a real identity is configured
    (``CSC_LINK`` / ``APPLE_SIGNING_IDENTITY``), or when the caller already
    pinned the flag. Mutates ``env``; returns True when it set the flag.
    r  Fr  r  CSC_IDENTITY_AUTO_DISCOVERYfalseT)rE   r   r(   )r,  r  s     r   _force_adhoc_macos_signingr  4  sh    $ |x;u
wwz cgg&>?? u$++u)0C%&4rH   c                  P   t           j        dk    rdS t          t          d          rt          j                    dk    rdS 	 t          dd          5 } |                                                                 dk    cd	d	d	           S # 1 swxY w Y   d	S # t          $ r Y dS w xY w)
a4  Return True when Chromium/Electron should bypass the Linux sandbox.

    Ubuntu 23.10+ can enable AppArmor's
    ``apparmor_restrict_unprivileged_userns`` hardening, which breaks
    Chromium/Electron's user-namespace sandbox for normal users unless the app
    ships a working root-owned 4755 ``chrome-sandbox`` helper. In headless or
    non-interactive CLI contexts we may be unable to ``sudo chown/chmod`` that
    helper, so detect the host restriction and fall back to ``--no-sandbox``
    rather than hard-failing the launcher.

    We intentionally do NOT return True for root users here: running Electron as
    root without a sandbox is a qualitatively riskier path than launching as an
    unprivileged desktop user on an AppArmor-restricted host. The root case
    should remain an explicit user choice.
    linuxFr   r   z6/proc/sys/kernel/apparmor_restrict_unprivileged_usernsr   r   rD   N)	rE   r   r   r&   r   r.   r  r5   rM   ri  s    r   _desktop_linux_needs_no_sandboxr  P  s      |wur9 "*,,!"3"3uJU\]]] 	+ab6688>>##s*	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+   uus6   B *B
=B 
BB BB 
B%$B%packaged_executablec                     t           j        dk    rdS | j        dz  }	 |                                }n# t          $ r Y dS w xY wt          j        |j                  S )z=Return True when ``chrome-sandbox`` exists as a regular file.r  Fchrome-sandbox)rE   r   r   lstatrM   rR  S_ISREGst_mode)r  sandboxsandbox_lstats      r   -_desktop_linux_sandbox_helper_is_regular_filer  k  sk    
|wu!(+;;G   uu<-...s   3 
A Ac                    t           j        dk    rdS | j        dz  }|                                st	          d|            dS 	 |                                }n## t          $ r t	          d|            Y dS w xY wt          j        |j	                  st	          d|            dS |j
        dk    rt          j        |j	                  d	k    rdS t          j        d
          }|st	          d           dS t	          d           |ddt          |          g|ddt          |          gfD ]6}t          j        |d          j        dk    rt	          d|             dS 7dS )z=Configure Electron's Linux SUID sandbox helper when required.r  Tr  u?   ✗ Hermes Desktop is missing Electron's Linux sandbox helper: Fu1   ✗ Cannot stat Electron's Linux sandbox helper: u;   ✗ Electron's Linux sandbox helper is not a regular file: r   i	  rM  uN   ✗ Hermes Desktop requires sudo to configure Electron's Linux sandbox helper.u@   → Configuring Electron Linux sandbox helper (sudo required)...chownz	root:rootchmod4755r  u9   ✗ Failed to configure Electron's Linux sandbox helper: )rE   r   r   r,   r|   r  rM   rR  r  r  st_uidS_IMODErW  rX  r4   rA  rB  rY  )r  r  r  rM  commands        r   _desktop_linux_sandbox_fixupr  x  s   
|wt!(+;;G>> YPWYYZZZu
   K'KKLLLuu <-.. UGUUVVVuq  T\-2G%H%HF%R%Rt<D ^___u	
LMMM7KW>wPVX[\cXdXd@ef  >'///:a??WgWWXXX55 @ 4s   A A;:A;c                  t   g } d}	 ddl m}  |            pi                     d          pi }n# t          $ r | |fcY S w xY w|                    d          }t	          |t
                    r%t          j        |t          j	        dk              } n(t	          |t          t          f          rd |D             } |                    d	d          }t	          |t                    r|rd
nd}nKt	          |t
                    r6|                                                                }|dv rd
}n	|dv rd}nd}| |fS )a  Read `desktop.*` launch options from config.yaml.

    Returns ``(electron_flags, disable_gpu)`` where ``electron_flags`` is a list
    of extra Electron CLI flags and ``disable_gpu`` is one of "auto"/"1"/"0"
    (normalized for the HERMES_DESKTOP_DISABLE_GPU env var the Electron app
    reads). Best-effort: any config error yields the safe defaults
    ``([], "auto")`` so a malformed config never blocks the launch.
    r  r   rw  r  electron_flagsnt)posixc                 n    g | ]2}t          |                                          #t          |          3S rN   )r4   r5   r  r  s     r   r  z+_desktop_launch_options.<locals>.<listcomp>  s1    ===Ac!ffllnn=Q===rH   disable_gpurD   0)rD   r  r  r  )r  r  norN  )r  r  r(   r   r2   r4   shlexrf   r&   r   r  r  rV   r5   r6   )flagsr  r  desktop_cfg	raw_flagsraw_gpur3  s          r   _desktop_launch_optionsr
    sw    EK"111111"{}}*//	::@b " " "k!!!!"  011I)S!! >Ibgo???	Ie}	-	- >=====oomV44G'4   	!$-cc#	GS	!	! !mmoo##%%,,,KK///KK K+s   '. ??rx  c           	      j   t           dz  dz  }|dz                                  s&t          d|            t          j        d           	 ddlm}  |d	           n# t          $ r Y nw xY wdd
lm	}m
}  |            }t          | dd          rd|d<   t          | dd          rd|d<   t          | dd          rHt          t          | j                                                                                            |d<   t          | dd          rHt          t          | j                                                                                            |d<   t%                      \  }}|dk    rdt&          j        vr||d<   t          | dd          }t          | dd          }	t          | dd          }
t+          |          }|s|	s@ |d          }|s2t          d           t          d           t          j        d           nd}|	rA|rt-          |          sGt          d|dz              t          d            t          d!           t          j        d           t/          t                     dz                                  sJt          d"           t          d#t            d$           t          d!           t          j        d           t          d%|dz              n*|It          d&|d'z              t          d(           t          d)           t          j        d           nt          d*|            n|
pt1          |t           |+          }|s|rd,nd-}t          d.| d/           nt          d0           t3                      }t5          |t           d|1          }|j        dk    rt9          t                     sBt          d2           t          d3t            d$           t          j        |j        pd           t;          t           |          }|rt          d4           nt          d5           |rd,nd-}t          d6| d7           |rd8nd9}t=          ||+          rt          d:           |sJt?          |          }|r9t          d;d<                     tC          t          |                     d=           tE          j#        |d>|g||d?          }|j        dk    r|st+          |          g }d}tI          t                     s$tK          |          }tM          t           |          }|rPt          d@           |D ]}t          dA|            t?          |           tE          j#        |d>|g||d?          }|j        dk    r|s|'                    dB          st+          |          t          dC           tP          }tS          |          }||dB<   tI          t                     stM          t           ||D           t?          |           tE          j#        |d>|g||d?          }|j        dk    rt          dE           t          dF|            t          j*        dGk    rt          dH           t          dI           t          dJ           t          dK           t          j        |j        pd           t+          |          }|stW          |           tY          t           |+           t          | dLd          r|rOt-          |          s)t          dM|dz              t          j        d           t          dN|dz   dO           nN|9t          dP|d'z              t          dQ           t          j        d           nt          dR| dO           dS |rEt          dS           tE          j#        |dTdUdVdWg||d?          }t          j        |j                   |8t          dX|d'z              t          dQ           t          j        d           t          |          g}t[          |          sVt]                      r4t_          |          r%t          dY           |0                    dZ           nt          j        d           |1                    |           t          d[d\                     |                      tE          j#        |||d?          }t          j        |j                   dS )]z1Build and launch the native Electron desktop GUI.r  r  r  z!Desktop GUI source not found at: rB   r   r  r  r  r  	fake_bootFrD   HERMES_DESKTOP_BOOT_FAKEignore_existingHERMES_DESKTOP_IGNORE_EXISTINGhermes_rootNHERMES_DESKTOP_HERMES_ROOTr  HERMES_DESKTOP_CWDr  HERMES_DESKTOP_DISABLE_GPUr  
skip_buildforce_buildr  z@Desktop GUI requires Node.js/npm, but npm was not found on PATH.z&Install Node.js, then run:  hermes guiuC   ✗ --skip-build --source was passed but no desktop dist found at: r  z4  Pre-build first:  cd apps/desktop && npm run buildzG  Or drop --skip-build to install dependencies and build automatically.uK   ✗ --skip-build --source requires existing desktop workspace dependencies.z  Install first:  cd z
 && npm ciuI   → Skipping desktop source build (--skip-build --source); using dist at uF   ✗ --skip-build was passed but no packaged desktop app was found at: r  z3  Pre-build first:  cd apps/desktop && npm run packz0  Or drop --skip-build to package automatically.u9   → Skipping desktop package build (--skip-build); using )r  zsource buildzpackaged appu   ✓ Desktop z& is up to date (content stamp matches)u0   → Installing desktop workspace dependencies...)r>  r,  u%   ✗ Desktop dependency install failedz  Run manually:  cd u\     ⚠ Dependency install failed with a missing Electron dist; repopulated it and continuing.u     ⚠ Dependency install failed with a missing Electron dist; continuing to the build so electron-builder can attempt the Electron fetch itself.u   → Building desktop r#  r  packug     → No Developer ID configured; ad-hoc signing this local rebuild (CSC_IDENTITY_AUTO_DISCOVERY=false)u@     ⚠ Stopped running desktop app to free the build output (pid , rr   rB  r  uP     ⚠ Desktop build failed; refreshed the Electron download and retrying once...z    - r  u     ⚠ Desktop build still failing; the Electron download from GitHub looks blocked. Re-downloading via a public mirror (npmmirror.com)... (set ELECTRON_MIRROR to use another mirror)r  u   ✗ Desktop GUI build failedz,  Run manually:  cd apps/desktop && npm run r  z:  If this says "Access is denied" on Hermes.exe, close anyz*  running Hermes desktop window and retry.zC  If the log shows Electron download retries, rebuild via a mirror:zB    ELECTRON_MIRROR=<mirror-base-url> hermes desktop --force-build
build_onlyu/   ✗ --build-only --source produced no dist at: u"   ✓ Desktop source build ready at z (not launching; --build-only)u0   ✗ --build-only produced no launchable app at: z7  Expected an unpacked Electron app for the current OS.u    ✓ Desktop packaged app ready: u1   → Launching Hermes Desktop from source build...rV  r   r  r  uH   ✗ Desktop package build completed but no launchable app was found at: u   ⚠ Falling back to --no-sandbox because this Linux host restricts unprivileged user namespaces and the Electron sandbox helper could not be configured.z--no-sandboxu'   → Launching packaged Hermes Desktop: r7  )2rQ  r,   r|   rE   r   hermes_loggingr  r   r   r  r  r0   r4   r   r  r+   rD  r  r
  r&   r'   r  rc  r  r  rA  rK  rY  r  r  r  r  r*   maprA  rB  r  r  r  r(   r  r3   r   r  r  r  r  r  r  rZ  )rx  r`  _setup_logging_guir  r  r,  config_electron_flagsconfig_disable_gpur  r  r  r  r  build_neededbuild_label	nixos_envinstall_resultrepairedbuild_scriptr  build_resultpurgedrestoredr  r  
mirror_envlaunch_resultlaunch_commands                               r   cmd_guir*    s
   ')3K.(0022 ?+??@@@FFFFFF&&&&&    MLLLLLLL  

!
!Ct[%(( .*-&'t&.. 403,-t]D)) _,/T5E0F0F0Q0Q0S0S0[0[0]0],^,^()tUD!! O$'TX(A(A(C(C(K(K(M(M$N$N ! 1H0I0I--V##(DBJ(V(V,>()$%00K|U33J$u55K6{CC * ""5)) 	TUUU:;;;HQKKK }N 	e'44 r\gjp\prrsssLMMM_```!,//.@HHJJ cdddFlFFFGGG_```t^ilr^rttuuuu (t[fir[rttuuuGHHHDEEEHQKKKKcNaccdddd # 
&;;'
 '
 '
  a	N,7K..^KTTTTUUUUDEEE(**I;C^cirsssN(A--8FF =ABBBIIIIJJJH^6;!<<<8sKK 8 ; < < < <  7 8 8 8 -8K..^K:+:::;;;&1=77vL)#;GGG = < = = = ~ @LL ~|]a]f]fgjknpwgxgx]y]y|||}}}%>3|*D+[^fklllL'1,,# -0==E &( (66 L8EEF8sKKH ulmmm# , ,lqll++++ :+FFF#->3|2LR]cfns#t#t#tL'1,,# - 122 - 1==E W X X X 3!#YY
06
,-(66 P-lCOOOO5kBBB)~sE<.Hk_iqvwww&!++4555S\SSTTT<7**XYYYFGGG[\\\Z[[[05A666">{"K"K ? 2+>>> '|MMMM t\5))  
	j'44 ^V\H\^^___k{V7Kkkkllll (^[S\E\^^___KLLLHQKKKKh5Hhhhiii +ABBB"VT:s'KQ\bemrsss)***"rYdgpYprrsssGHHH-../N'(;<< *,, 	1^_r1s1s 	  m  n  n  n!!.1111HQKKK/000	
NCHH^4L4L
N
NOOON>{SXYYYMH]%&&&&&s   A 
A,+A,exclude_pidsr,  c           
          g d}t          j                    }g }	 t          j        dk    r.ddlm} t          j        g dddddd	 |            
          }|j        dk    s|j	        g S d|j	        
                    d          D ]}|                                }|                    d          r|t          d          d         C|                    d          ry|t          d          d         }t          fd|D                       rGt          |          |k    r4	 |                    t          |                     # t"          $ r Y w xY wnt          j        g dddd          }|j        dk    rt%          |dd          
                    d          D ]}|                                }|rd|v r|
                    dd          }	t          |	          dk    rG	 t          |	d                   }
n# t"          $ r Y jw xY w|	d         t          fd|D                       r|
|k    r|                    |
           n$# t&          t          j        t*          f$ r g cY S w xY w r fd|D             }|S )uk  Return PIDs of ``hermes dashboard`` processes other than ourselves.

    ``hermes dashboard`` is a long-lived server process commonly started and
    forgotten.  When ``hermes update`` replaces files on disk, the running
    process keeps the old Python backend in memory while the JS bundle on
    disk is updated, causing a silent frontend/backend mismatch (e.g. new
    auth headers the old backend doesn't recognise → every API call 401s).

    The dashboard has no service manager (systemd / launchd), no PID file,
    and we can't know the original launch args — so the only sane action
    after an update is to kill the stale process and let the user restart
    it.  This helper is just the detection step; see
    ``_kill_stale_dashboard_processes`` for the kill.

    *exclude_pids* is an optional set of PIDs that must never be returned.
    This is used by the Hermes Desktop Electron app to protect its own
    backend child process: when the desktop spawns ``hermes serve`` as
    a backend and triggers an auto-update, the update must not kill the
    backend that the desktop itself manages.  The desktop sets the
    environment variable ``HERMES_DESKTOP_CHILD_PID`` on the spawned
    backend process; ``_kill_stale_dashboard_processes`` reads it and
    passes it here.  (#37532)

    Returns an empty list on any scan error (missing ps/wmic, timeout, etc.).
    )zhermes dashboardzhermes_cli.main dashboardzhermes_cli/main.py dashboardzhermes servezhermes_cli.main servezhermes_cli/main.py server  r   )windows_hide_flags)wmicprocessr(   zProcessId,CommandLinez/FORMAT:LISTTr  r   ignore)r>  r9  r?  r   r5  creationflagsNrR   rc  zCommandLine=z
ProcessId=c              3       K   | ]}|v V  	d S r  rN   )r  r  current_cmds     r   r  z-_find_stale_dashboard_pids.<locals>.<genexpr>  s(      ??A,??????rH   )psz-Az-ozpid=,command=r=  r  greprB   r   c              3       K   | ]}|v V  	d S r  rN   )r  r  r  s     r   r  z-_find_stale_dashboard_pids.<locals>.<genexpr>  s'      ::A1<::::::rH   c                     g | ]}|v|	S rN   rN   )r  r  r,  s     r   r  z._find_stale_dashboard_pids.<locals>.<listcomp>  s#    MMMq7L7L!7L7L7LrH   )r&   r  rE   r   hermes_cli._subprocess_compatr.  rA  rB  rY  r  rf   r5   rW   r   r  r   r  r   r0   r   rC  rM   )r,  patternsself_piddashboard_pidsr.  r  rj   pid_strrk   r:  r  r  r4  s   `          @@r   _find_stale_dashboard_pidsr>    s3   :
 
 
H y{{H "NE<7"" IHHHHH^SSS# 0022  F  A%%)>	K++D11 ! !zz||??>22 !"&s>':':'<'<"=KK__\22 	!"3|#4#4#6#67G????h?????!LLH44!*11#g,,????) ! ! ! D!!*  ^333#	  F  A%%#FHb99??EE 3 3D#zz||H# !v'9'9 $NN433E5zzQ !!%(mm% ! ! ! !#AhG::::::::: 3sh&--c222z8'B   			  NMMMM^MMMsc   AI .B9I ("E
I 
EI EBI .HI 
HI HAI I54I5c                     	 ddl m}  n# t          $ r Y dS w xY w	 |                                 sdS |                                 }n# t          $ r Y dS w xY w|                    d          rdS 	 |                                 }n# t          $ r d}Y nw xY wt          d|dz            }t                       t          d           t          d	| d
           t          d           t          d           t          d           dS )a  Print a short heads-up about the skill curator after `hermes update`.

    Only fires when the curator is enabled AND has no recorded run yet, which
    is exactly the window where the gateway ticker used to fire Curator
    against a fresh skill library immediately after an update. We defer the
    first real pass by one ``interval_hours``; this notice tells the user how
    to preview or disable before then. Silent on steady state.
    r   r  Nlast_run_at   rB      u   ℹ Skill curatorzC  Background skill maintenance is enabled. First pass is deferred ~zwd after installation; only agent-created skills are in scope and nothing is ever auto-deleted (archive is recoverable).z,  Preview now:  hermes curator run --dry-runz$  Pause it:     hermes curator pausezV  Docs:         https://hermes-agent.nousresearch.com/docs/user-guide/features/curator)	r)  r  r   
is_enabled
load_stater(   get_interval_hoursr  r|   )r  statehoursdayss       r   _print_curator_first_run_noticerJ    s}   !!!!!!!   !!## 	F""$$   yy **,,   q%2+D	GGG	
		L	L 	L 	L  
 

8999	
0111	`    s4   	 
A A 
AA/B BBc                     	 ddl m}  n# t          $ r Y dS w xY w	 |                                 }n# t          $ r Y dS w xY w|                    d          }|sdS |                    d          |k    rdS |                    d          pd}|sdS d|vr.	 ||d<   |                     |           n# t          $ r Y nw xY wdS t          |          }t                       t          d	|            |                                D ]}t          d
|            t          d           	 ||d<   |                     |           dS # t          $ r Y dS w xY w)u  Print the most recent curator run summary, exactly once.

    The curator runs in the background (gateway tick + CLI session start),
    so users learn about skill consolidations only by stumbling into a
    rename. ``hermes update`` is a high-attention surface — surface the
    most recent run's rename map here, once.

    Show-once: state stamps ``last_run_summary_shown_at`` after printing.
    Subsequent ``hermes update`` invocations skip the block until a newer
    curator run lands. Silent when the curator has never run, when the
    most recent summary has already been shown, or when the summary has
    no rename information to display (no archives).
    r   r@  NrA  last_run_summary_shown_atlast_run_summaryrR   rc  u   ℹ Skill curator — last run r  zP  (This message shows once per curator run. View anytime: hermes curator status))	r)  r  r   rE  r(   
save_state_format_time_agor|   r8  )r  rG  rA  summarywhenrj   s         r    _print_curator_recent_run_noticerR  @  s   !!!!!!!   ""$$    ))M**K yy,--<<ii*++1rG  7	1<E-.u%%%% 	 	 	D	 K((D	GGG	
2D
2
2333""$$  k4kk		/  -8)*5!!!!!   s<   	 
0 
>>B0 0
B=<B=*E 
EEiso_tsc                    	 ddl m }m}  |j        |                     dd                    }|j        |                    |j                  } |j        |j                  |z
  }t          |                                          }|dk     rdS |d	k     r|dz   d
S |dk     r|d	z   dS |dz   dS # t          $ r Y dS w xY w)zGRender an ISO timestamp as `Xh ago` / `Xd ago` / `Xm ago`. Best effort.r   r  Zz+00:00N)tzinfors  rt  ru  rv  rw  rx  rz  recently)
r  r  fromisoformatr3  rV  r  r  r   total_secondsr   )rS  r  r  r  r  secss         r   rO  rO    s   ////////#X#FNN3$A$ABB98<00BX\**R/5&&(())"99:$;;bj''''%<<dl))))%-&&&&   zzs$   BB5 B5 B5 -B5 5
CC:the running backend no longer matches the updated frontendreasonc                    d}t           j                            d          }|r{t                      }|                    d          D ]S}|                                }|s	 |                    t          |                     =# t          t          f$ r Y Pw xY w|r|}t          |          }|sdS t                       t          dt          |           d|  d           g g t          j        dk    r|D ]}	 t          j        d	d
t#          |          dgddd          }|j        dk    r                    |           n7                    ||j        p|j        pd                                f           # t,          t          j        t0          f$ r.}                    |t#          |          f           Y d}~d}~ww xY wnddl}	ddl}
|D ]}	 t          j        ||	j                   # t:          $ r                     |           Y ?t<          t0          f$ r.}                    |t#          |          f           Y d}~xd}~ww xY w |
j                    dz   }fd|D             }|r} |
j                    |k     rj |
j         d           g }ddl!m"} |D ]8} ||          r|                    |           #                    |           9|}|r |
j                    |k     j|D ]}	 t          j        ||	j#                                       |           3# t:          $ r                     |           Y Tt<          t0          f$ r.}                    |t#          |          f           Y d}~d}~ww xY wD ]}t          d|            D ]\  }}t          d| d|            r t          d           t          d           dS dS )u  Kill running ``hermes dashboard`` processes.

    Called at the end of ``hermes update`` (default ``reason``) and also
    from ``hermes dashboard --stop`` (which overrides ``reason``).  The
    dashboard has no service manager, so after a code update the running
    process is guaranteed to be serving stale Python against a
    freshly-updated JS bundle.  Leaving it alive produces silent
    frontend/backend mismatches (new auth headers the old backend doesn't
    recognise → every API call 401s).

    POSIX: SIGTERM, wait up to ~3s for graceful exit, SIGKILL any survivors.
    Windows: ``taskkill /PID <pid> /F`` since there's no clean SIGTERM
    equivalent for background console apps.

    The dashboard isn't auto-restarted because we don't know the original
    launch args (--host, --port, --insecure, --tui, --no-open).  The user
    restarts it manually; a hint is printed.
    NHERMES_DESKTOP_CHILD_PIDr  r+  u   ⟲ Stopping z dashboard process(es) (rr   r  taskkillz/PIDz/FTr  r=  r   rR         @c                 8    g | ]}|v|d  D             v|S )c                     h | ]
}|d          S )r   rN   r  s     r   	<setcomp>z=_kill_stale_dashboard_processes.<locals>.<listcomp>.<setcomp>  s    <R<R<RaQqT<R<R<RrH   rN   )r  r  failedkilleds     r   r  z3_kill_stale_dashboard_processes.<locals>.<listcomp>  sA     
 
 
q1<R<R6<R<R<R3R3RA3R3R3RrH   g?_pid_existsu       ✓ stopped PID u       ✗ failed to stop PID r  z*  Restart the dashboard when you're ready:z"    hermes dashboard --port <port>)$r&   r'   r(   r  rf   r5   r   r   r   	TypeErrorr>  r|   r   rE   r   rA  rB  r4   rY  r  r   r  r   rC  rM   signalr|  r3  SIGTERMProcessLookupErrorPermissionErrorr  r  gateway.statusrg  SIGKILL)r\  excluderaw_pidparsedr  pidsr  r  r  _signalr{  r  pendingstill_pendingrg  err_msgrd  re  s                   @@r   _kill_stale_dashboard_processesrw    s   0  $Gjnn788G  55MM#&& 	 	D::<<D 

3t99%%%%	*    	G%7;;;D 	GGG	
F#d))
F
FV
F
F
FGGGF$&F
|w 	- 	-C-#S48#'	   $))MM#&&&&MM3)M&-)M2(T(T(V(V"WXXX%z'@'J - - -sCFFm,,,,,,,,-	- 	!     	- 	-C-W_----% # # #c"""""#W- - - -sCFFm,,,,,,,,- #5?$$s*
 
 
 
 

 
 
  	$/%/++h66EKM 322222 ' ';s## '!((----MM#&&&&#G  	$/%/++h66  	- 	-C-W_---c""""% # # #c"""""#W- - - -sCFFm,,,,,,,,-  , ,*S**++++ > >W<C<<7<<==== 4:;;;2333334 4sg   #"BBB?BFG$GGG88II)$II?/L//NN $N		Nr-  dstc                    | d}| d}||fD ]7}t           j                            |          rt          j        |d           8t          j        | |           t           j                            |          rt          j        ||           	 t          j        ||           nb# t          $ rU t           j                            |          r4t           j                            |          st          j        ||            w xY wt           j                            |          rt          j        |d           dS dS )u  Replace directory *dst* with *src* without leaving *dst* half-deleted.

    The naive ``rmtree(dst); copytree(src, dst)`` has a destructive window: if
    the copy fails partway (common on the Windows ZIP-update path, which only
    runs because file I/O is already flaky on that machine), the old directory
    is already gone and nothing replaced it — the install is left with a
    deleted tree (issue #49145, where ``ui-tui/`` vanished and broke the TUI).

    Instead, stage the new copy into a sibling temp dir first; only once that
    fully succeeds do we swap it in. A failure during staging raises with the
    original *dst* still intact.
    z.hermes-update-stagingz.hermes-update-oldTr  N)r&   r)   r,   rW  r  copytreerenamerM   )r-  rx  stagingbackupleftovers        r   _atomic_replace_dirr    sP    ,,,G'''Ff% 8 87>>(## 	8M($7777 OC!!! 
w~~c 
	#v
	'3   7>>&!! 	#"'..*=*= 	#Ifc"""
 
w~~f 2fD1111112 2s   B' 'ADc           	         ddl }ddl}ddlm} t	          |           }|dk    r:t          d| d           t          d| d           t          j        d	           d
| d}t          d           |                    d          }	 t          j
                            |d| d          } |||           t          d           ddl}|                    |d          5 }	t          j
                            |          }
|	                                D ]}t          j
                            t          j
                            ||j                            }|                    |
t          j        z             s||
k    rt'          d|j         d          |j        dz	  dz  }|                    |          rt'          d|j                   |	                    |           ddd           n# 1 swxY w Y   t          j
                            |d|           }t          j
                            |          s`t          j        |          D ]K}t          j
                            ||          }t          j
                            |          r
|dk    r|} nLh d}d}t          j        |          D ]}||v rt          j
                            ||          }t          j
                            t3          t4                    |          }t          j
                            |          rt7          ||           nt9          j        ||           |d	z  }t          d| d           n=# t<          $ r0}t          d|            t          j        d	           Y d}~nd}~ww xY wt9          j        |d           n# t9          j        |d           w xY wtA          t4                    }|rt          d| d|d	k    rd nd!            t          d"           dd#l!m"}m#}  |              |            }t          j$        d$d%g}|stK          |          }|rvi t          j&        d&t3          t4          d'z            i}tO          |          r,|(                    d(d           |(                    d)d           tS          |d%g|*           nr	 tU          j+        |d+gz   t4          dd,           n@# tT          j,        $ r. tU          j+        t          j$        d$d-d.d/gt4          d0           Y nw xY wtS          |           t[                       t]          t4          d1z             	 dd2l/m0} t          d3            |d4          }|d5         rAt          d6tc          |d5                    d7d8                    |d5                               |2                    d9          rAt          d:tc          |d9                    d;d8                    |d9                               |2                    d<          r5t          d=tc          |d<                    d>           t          d?           |2                    d@          r&t          dAtc          |d@                    dB           |d5         s$|2                    d9          st          dC           n# t<          $ r Y nw xY w	 ddDl3m4}  |t4                    rt          dE           n2# t<          $ r%}tj          6                    dF|           Y d}~nd}~ww xY wt                       t          dG           	 to                       n2# t<          $ r%}tj          6                    dH|           Y d}~nd}~ww xY w	 tq                       n2# t<          $ r%}tj          6                    dI|           Y d}~nd}~ww xY wts                       dS )JzUpdate Hermes Agent by downloading a ZIP archive.

    Used on Windows when git file I/O is broken (antivirus, NTFS filter
    drivers causing 'Invalid argument' errors on file creation).
    r   N)urlretriever  u   ✗ --branch=z: is not supported on the Windows ZIP-fallback update path.z  This path runs when git file I/O is broken on the system. Either resolve the git-side breakage (typically an antivirus or NTFS filter holding files open) and rerun `hermes update --branch z/`, or update against main with `hermes update`.rB   z@https://github.com/NousResearch/hermes-agent/archive/refs/heads/z.zipu!   → Downloading latest version...zhermes-update-rX   zhermes-agent-u   → Extracting...r  zZip-slip detected: z escapes extraction directory   i   z)ZIP contains unsupported symlink member: __MACOSX>   r  r  r   r>  u   ✓ Updated z items from ZIPu   ✗ ZIP update failed: Tr       ✓ Cleared  stale __pycache__ directorr  ies#   → Updating Python dependencies...	ensure_uvupdate_managed_uvr   r  VIRTUAL_ENVr  
PYTHONPATH
PYTHONHOMEr2  r\   r  r.  r>  	ensurepip	--upgrade--default-pipr  r.  rV  rh     → Syncing bundled skills...rj  copied  +  new: r  updated     ↑ 
 updated: user_modified  ~  user-modified (kept)M       → see them: hermes skills list-modified  (diff/reset to resume updates)cleaned     −  removed from manifest     ✓ Skills are up to dateseed_cache_from_checkout1     ✓ Model catalog cache refreshed from checkoutz/Model catalog seed during zip update failed: %s   ✓ Update complete!#Curator first-run notice failed: %s$Curator recent-run notice failed: %s):r[  zipfileurllib.requestr  _resolve_update_branchr|   rE   r   mkdtempr&   r)   r*   rR  ZipFilerealpathinfolistfilenamerW   sepr   external_attrS_ISLNK
extractallisdirlistdirr4   rQ  r  rW  copy2r   r  r  hermes_cli.managed_uvr  r  ra  _ensure_uv_for_termuxr'   _is_termux_envrb  3_install_python_dependencies_with_optional_fallbackrA  rB  r  _update_node_dependenciesr_  rl  ri  r   r(   hermes_cli.model_catalogr  r]  r^  rJ  rR  rw  ) rx  r[  r  r  branchzip_urltmp_dirr  _statzftmp_dir_realmembermember_pathr  	extractedr  r   preserveupdate_countr  r-  rx  r  r  r  r  uv_binpip_cmduv_envri  r  r  s                                    r   _update_via_zipr  7  s	    OOONNN****** $D))FF   	
 	
 	
 	P P P P	
 	
 	
 	W6WWW  

-...&677G?37<<)E)E)E)EFFGX&&&!"""__Xs++ 	#r 7++G44L++--   g..rw||GV_/U/UVV#..|bf/DEE#|33$\fo\\\  
 ,2h>==&& $UFOUU   MM'"""/	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	#4 GLL*B&*B*BCC	w}}Y'' 	Z((  GLL!44	7==++ Z )IE <;;Jy)) 	 	Dx',,y$//C',,s<00$77Cw}}S!! ' $C----S#&&&ALL:\:::;;;;   +++,,, 	gT22222gT22222 $L11G 
aWaaSTZ_aa	
 	
 	
 

/000BBBBBBBB Y[[F~tU+G 0&w// EJBJJs<&3H/I/IJJ&!! 	+JJ|T***JJ|T***;VUOQWXXXXX	N;-' #	     , 	 	 	N{KQ      	 	<GDDD,&'''111111-...4(((( 	USVH-..SSdiix@P6Q6QSSTTT::i   	YVI.//YY499VIEV;W;WYY   ::o&& 	LVO455LLLMMM1   ::i   	KI3vi011IIIJJJh 	1

9(=(= 	1/000   
KEEEEEE##L11 	GEFFF K K KFJJJJJJJJK 
GGG	
 !!!?')))) ? ? ?:A>>>>>>>>?@(**** @ @ @;Q????????@#%%%%%s   
AM5 #DG6*M5 6G::M5 =G:>E6M5 4O	 5
N/?&N*%O	 *N//O	 	O!!S= =:T:9T:2E1[$ $
[10[15%\ 
]
%]]
+]: :
^)^$$^)-^< <
_+_&&_+r  c                 ^   t          j        | ddgz   |ddd          }|j                                        sd S t          j        | ddgz   |dd          }|j                                        r*t	          d           t          j        | d	gz   |d
           ddlm}m}  |j        |j                  	                    d          }t	          d           t          j        | dddd|gz   |d           t          j        | g dz   |ddd          j                                        }|S )Nr  z--porcelainTr  zls-filesz
--unmergedr  r>  r9  u?   → Clearing unmerged index entries from a previous conflict...resetr  r>  r   r  z%hermes-update-autostash-%Y%m%d-%H%M%Su8   → Local changes detected — stashing before update...stashpushz--include-untrackedr   r  )r  --verifyz
refs/stash)
rA  rB  r  r5   r|   r  r  r  r  r~  )r  r  r  unmergedr  r  
stash_name	stash_refs           r   _stash_local_changes_if_neededr    s   ^8]++  F =   t ~:|,,	  H  JOPPPw'*DIIII++++++++hl++44/ J 

DEEEN7F$94LL   
 9999   UUWW  rH   r  c                    t          j        | g dz   |ddd          }|j                                        D ]I}|                    d          \  }}}|                                |k    r|                                c S Jd S )N)r  r  z--format=%gd %HTr  r7  )rA  rB  r  r8  re   r5   )r  r  r  
stash_listrj   selectorrG  r  s           r   _resolve_stash_selectorr  ,  s     6666  J !,,.. $ $"nnS11!V<<>>Y&&>>##### '4rH   stash_selectorc                     t          d           t          d           |rt          d|            d S t          d|  d           d S )NzS  Check `git status` first so you don't accidentally reapply the same change twice.z@  Find the saved entry with: git stash list --format='%gd %H %s'z!  Remove it with: git stash drop z  Look for commit z7, then drop its selector with: git stash drop stash@{N})r|   )r  r  s     r   _print_stash_cleanup_guidancer  =  sy     
]   

LMMM 
B.BBCCCCCeeee	
 	
 	
 	
 	
rH   prompt_userc                 p   |rt                       t          d           t          d           t          d           t          d           | |dd          }n2t                                                                                      }|dvr2t          d           t          d           t          d	|            d
S t          d           t	          j        | dd|gz   |dd          }t	          j        | g dz   |dd          }t          |j                                                  }|j        dk    s|r@t          d           |j                                        r&t          |j                                                   |j	                                        r&t          |j	                                                   |j                                        }	|	r8t          d           |	
                                D ]}
t          d|
            t          d           t          d|            t	          j        | g dz   |d           t          d           t          d|            d
S t          | ||          }|.t          d           t          d           t          |           nt	          j        | dd|gz   |dd          }|j        dk    rt          d           |j                                        r&t          |j                                                   |j	                                        r&t          |j	                                                   t          d           t          ||           t          d           t          d            dS )!Nu/   ⚠ Local changes were stashed before updating.zL  Restoring them may reapply local customizations onto the updated codebase.z=  Review the result afterward if Hermes behaves unexpectedly.z Restore local changes now? [Y/n]r  >   rR   r  r  z Skipped restoring local changes.z.Your changes are still preserved in git stash.z'Restore manually with: git stash apply Fu   → Restoring local changes...r  applyTr  )diff--name-onlyz--diff-filter=Ur   uF   ✗ Update pulled new code, but restoring local changes hit conflicts.z
Conflicted files:u     • u8   
Your stashed changes are preserved — nothing is lost.z  Stash ref: )r  --hardrB  r  z"Working tree reset to clean state.z1Restore your changes later with: git stash apply uR   ⚠ Local changes were restored, but Hermes couldn't find the stash entry to drop.zT  The stash was left in place. You can remove it manually after checking the result.dropuP   ⚠ Local changes were restored, but Hermes couldn't drop the saved stash entry.u?   ⚠ Local changes were restored on top of the updated codebase.zB  Review `git diff` / `git status` if Hermes behaves unexpectedly.)r|   r)  r5   r6   rA  rB  rV   r  rY  r   r8  r  r  )r  r  r  r  input_fnr  r  r  has_conflictsconflicted_filesr  r  r  s                r   _restore_stashed_changesr  L  s"     ?@@@Z	
 	
 	
 	MNNN0111x BCHHHHww}},,..H+++4555BCCCGIGGHHH5	
*+++n7GY//	  G ~<<<<	  H ..0011MQ-VWWW>!! 	*'.&&(()))>!! 	*'.&&(())) $?0022 	$'(((%0022 $ $lqll####IJJJ)i))***
 	1111	
 	
 	
 	

 	2333M)MMNNN u,Wc9EEN`	
 	
 	
 	b	
 	
 	
 	&i0000~w77	
 
 
 ?ab   {  "" +dk''))***{  "" +dk''))***f   *)^DDD	
KLLL	
NOOO4rH   c                    t          | ||          }| t          d           t          |           dS t          j        | dd|gz   |dd          }|j        dk    r{t          d	           |j                                        rAt          d
|j                                                                        d                     t          ||           dS t          d           dS )u  Throw away a stash created before an update, without applying it.

    Used only on a NON-interactive update when the user has set
    ``updates.non_interactive_local_changes: discard`` — i.e. they've opted out
    of keeping local source edits on this machine. Drops the stash entry
    instead of re-applying it, so the working tree stays clean at the freshly
    pulled HEAD. Unlike ``git reset --hard`` + ``git clean -fd``, this only
    affects what was stashed (tracked changes + the untracked files we
    explicitly captured) — ignored paths like node_modules/venv/build outputs
    are never touched, since they were never stashed.

    Returns True if the stash was dropped, False on a git failure (in which
    case the stash is left in place for safety).
    Nut   ⚠ Configured to discard local changes on non-interactive update, but Hermes couldn't find the stash entry to drop.Fr  r  Tr  r   uX   ⚠ Configured to discard local changes, but Hermes couldn't drop the saved stash entry.r  uS   → Discarded local source changes (updates.non_interactive_local_changes=discard).)	r  r|   r  rA  rB  rY  r   r5   r8  )r  r  r  r  r  s        r   _discard_stashed_changesr    s   & -Wc9EEN@	
 	
 	
 	&i000u>7FN33	  D !%	
 	
 	
 ; 	><t{((**5577:<<===%i@@@u	
_```4rH   >   (git@github.com:NousResearch/hermes-agent,git@github.com:NousResearch/hermes-agent.git,https://github.com/NousResearch/hermes-agent0https://github.com/NousResearch/hermes-agent.gitr  z.skip_upstream_promptc                     	 t          j        | g dz   |dd          }|j        dk    r|j                                        S n# t
          $ r Y nw xY wdS )z5Get the URL of the origin remote, or None if not set.)remoteget-urloriginTr  r   N)rA  rB  rY  r  r5   r   r  s      r   _get_origin_urlr    s    
5555	
 
 
 !!=&&((( "   4s   A A 
AA
origin_urlc                    | sdS |                      d          }|                    d          r
|dd         }t          D ]?}|                     d          }|                    d          r
|dd         }||k    r dS @dS )zDCheck if the origin remote points to a fork (not the official repo).Fr  r>  NT)r  r  OFFICIAL_REPO_URLS)r  r  officialofficial_normalizeds       r   _is_forkr    s     u""3''J6"" %_
&  &ooc22''// 	;"5crc":,,,55 -4rH   c                 v    	 t          j        | g dz   |dd          }|j        dk    S # t          $ r Y dS w xY w)z-Check if an 'upstream' remote already exists.)r  r  upstreamTr  r   FrA  rB  rY  r   r  s      r   _has_upstream_remoter    sg    	7777	
 
 
  A%%   uu   '* 
88c                     	 t          j        | dddt          gz   |dd          }|j        dk    S # t          $ r Y dS w xY w)zHAdd the official repo as the 'upstream' remote. Returns True on success.r  r   r  Tr  r   F)rA  rB  OFFICIAL_REPO_URLrY  r   r  s      r   _add_upstream_remoter  %  sj    	x
4EFF	
 
 
  A%%   uus   .1 
??rg   rK  c                     	 t          j        | dd| d| gz   |dd          }|j        dk    r&t          |j                                                  S n# t          $ r Y nw xY wdS )zDCount commits on `head` that are not on `base`. Returns -1 on error.rev-list--countz..Tr  r   r  )rA  rB  rY  r   r  r5   r   )r  r  rg   rK  r  s        r   _count_commits_betweenr  3  s    
z9.?.?.?.?@@	
 
 
 !!v}**,,--- "   2s   AA 
A$#A$c                  V    ddl m}   |             t          z                                  S )z2Check if user previously declined to add upstream.r   r   )r   r   SKIP_UPSTREAM_PROMPT_FILEr,   r   s    r   _should_skip_upstream_promptr	  C  s4    000000O 99AACCCrH   c                  ~    	 ddl m}   |             t          z                                   dS # t          $ r Y dS w xY w)z3Create marker file to skip future upstream prompts.r   r   N)r   r   r  touchr   r   s    r   _mark_skip_upstream_promptr  J  sa    444444			6	6==?????   s   *. 
<<c                 v    	 t          j        | g dz   |dd          }|j        dk    S # t          $ r Y dS w xY w)znAttempt to push updated main to origin (sync fork).

    Returns True if push succeeded, False otherwise.
    )r  r  r  z--force-with-leaseTr  r   Fr  r  s      r   _sync_fork_with_upstreamr  T  sg    
	FFFF	
 
 
  A%%   uur  c                     t          | |          }|st                      rdS t                       t          d           t          d           t                       	 t          d                                                                          }n'# t          t          f$ r t                       d}Y nw xY w|dv rBt          d           t          | |          rt          d           d	}n0t          d
           dS t          d           t                       dS t                       t          d           	 t          j        | g dz   |d	d	           n%# t          j        $ r t          d           Y dS w xY wt          | |dd          }t          | |dd          }|dk     s|dk     rt          d           dS |dk    rPt                       t          d| d           t          d           t          d           t          d           dS |dk    rt          d           dS t                       t          d| d           t          d           	 t          j        | g dz   |d	           n%# t          j        $ r t          d           Y dS w xY wt          d            t          d!           t          | |          rt          d"           dS t          d#           t          d$           dS )%a[  Check if fork is behind upstream and sync if safe.

    This implements the fork upstream sync logic:
    - If upstream remote doesn't exist, ask user if they want to add it
    - Compare origin/main with upstream/main
    - If origin/main is strictly behind upstream/main, pull from upstream
    - Try to sync fork back to origin if possible
    Nu=   ℹ Your fork is not tracking the official Hermes repository.zA  This means you may miss updates from NousResearch/hermes-agent.z/Add official repo as 'upstream' remote? [Y/n]: r  >   rR   r  r  u   → Adding upstream remote...uF     ✓ Added upstream: https://github.com/NousResearch/hermes-agent.gitTu<     ✗ Failed to add upstream remote. Skipping upstream sync.zg  Skipped. Run 'git remote add upstream https://github.com/NousResearch/hermes-agent.git' to add later.u   → Fetching upstream...)fetchr  r  --quiet)r  r>  r.  u7     ✗ Failed to fetch upstream. Skipping upstream sync.zupstream/mainzorigin/mainr   u9     ✗ Could not compare branches. Skipping upstream sync.u   ℹ Your fork has z commit(s) not on upstream.z2  Skipping upstream sync to preserve your changes.z-  If you want to merge upstream changes, run:z    git pull upstream mainu&     ✓ Fork is up to date with upstreamu   → Fork is z commit(s) behind upstreamu   → Pulling from upstream...)pull	--ff-onlyr  r  r  uO     ✗ Failed to pull from upstream. You may need to resolve conflicts manually.u     ✓ Updated from upstreamu   → Syncing fork...u     ✓ Fork synced with upstreamuL     ℹ Got updates from upstream but couldn't push to fork (no write access?)zF    Your local repo is updated, but your fork on GitHub may be behind.)r  r	  r|   r)  r5   r6   r+  r*  r  r  rA  rB  r  r  r  )r  r  has_upstreamr  origin_aheadupstream_aheads         r   _sync_with_upstream_if_neededr  e  s    (55L !')) 	F 	MNNNQRRR	GHHNNPPVVXX H +, 	 	 	GGGHHH	 '''1222#GS11 \    $TUUUy   '(((F
 
GGG	
$%%%	>>>>		
 	
 	
 	
 	
 (   GHHH
 *'3WWL+m_ N a>A--IJJJ aL<LLLMMMBCCC=>>>*+++ 6777 
GGG	
C
C
C
CDDD	
()))
????	
 	
 	
 	
 	

 (   ]	
 	
 	
 		 

'((( 

   -- X/00000Z	
 	
 	
 	VWWWWWs6   3B !B76B7=E E=<E=I< <JJc                     g } ddl m}  |            }|                     |           |dz  }|                                r@|                                D ]+}|                                r|                     |           ,| D ]@}	 |dz  }|                                r|                                 1# t          $ r Y =w xY wdS )u  Delete the update-check cache for ALL profiles so no banner
    reports a stale "commits behind" count after a successful update.

    The git repo is shared across profiles — when one profile runs
    ``hermes update``, every profile is now current.
    r   r   r   z.update_checkN)r   r   r  r   r  r,   rd  r   )homesr   default_homeprofiles_rootr  r8   
cache_files          r   _invalidate_update_cacher    s    E888888**,,L	LL :-M $"**,, 	$ 	$E||~~ $U###  	/J  "" $!!### 	 	 	D	 s   -B44
C Callr(  c                 4   	 ddl }t          dz                      d          5 }|                    |                              di           }ddd           n# 1 swxY w Y   n# t
          $ r g cY S w xY w|                    di           }t          |t                    sg S |                    | g           }g }|D ]Y}d|v rSd|v rO|                    dd	          d	                             dd	          d         }||v r|	                    |           Z|S )
zReturn optional extras referenced by a dependency group.

    ``group`` is usually ``all`` (desktop/server broad install) or
    ``termux-all`` (Termux-compatible broad install).
    r   Npyproject.tomlrg  projectzoptional-dependencies[r  rB   )
tomllibrQ  r.   r/   r(   r   r2   r3   rf   r  )	r(  r#  ri   r!  optional_depsr  
referencedr1  r   s	            r   !_load_installable_optional_extrasr&    s|   --33D99 	>Vll6**..y"==G	> 	> 	> 	> 	> 	> 	> 	> 	> 	> 	> 	> 	> 	> 	>   			 KK 7<<MmT** 	UB''DJ ( (#::#**99S!$$Q'--c155a8D}$$!!$'''s4   !A% *AA% AA%  A!A% %A43A4c                      t           dz  S )Nz.update-incomplete)rQ  rN   rH   r   _update_marker_pathr(    s    ...rH   c                     	 t                                          dt          j                     dt	          j                     dd           dS # t          $ r&} t                              d|            Y d} ~ dS d} ~ ww xY w)z6Drop the interrupted-install breadcrumb. Never raises.zstarted=z
pid=rc  r   r   z,Could not write update-incomplete marker: %sN)	r(  re  r{  r|  r&   r  rM   r]  r^  r   s    r   _write_update_incomplete_markerr+    s    J((:uz||::29;;:::W 	) 	
 	
 	
 	
 	
  J J JCSIIIIIIIIIJs   AA 
B A;;B c                      	 t                                                       dS # t          $ r Y dS t          $ r&} t                              d|            Y d} ~ dS d} ~ ww xY w)z8Remove the interrupted-install breadcrumb. Never raises.z,Could not clear update-incomplete marker: %sN)r(  rd  r   rM   r]  r^  r*  s    r   _clear_update_incomplete_markerr-  '  s    J$$&&&&&    J J JCSIIIIIIIIIJs    $ 
A 	A AA c                     t                                                      sdS t          dz                                  st	                       dS t          dz  } 	 t          j        | t
          j        t
          j        z  t
          j	        z            }t          j
        |t          j                     d                                           t          j        |           n# t          $ r[ 	 t          j                    |                                 j        z
  dk    r|                                  n# t(          $ r Y nw xY wY dS t(          $ r%}t*                              d|           Y d}~nd}~ww xY wt/                      rt1                      }|t3          |          }|rt5                      }|D ]}	 |                    t9          |                                                                                     J# t(          $ r7 |                    t9          |                                                     Y w xY w	 ddl}|                                 }|gtC          |"                                          z   D ]}		 |	#                                }
t9          tI          |
                                                                                    }n# tJ          $ r Y dw xY w||v rtM          d           tM          d	           tM          d
t           d           tM          dtN          j(         d           t	                       	 |                                  n# t(          $ r Y nw xY w dS n# tJ          $ r Y nw xY wd}tN          j)        }	 	 t          j*        d          }t          j+        dd           n# t(          $ r d}Y nw xY wtN          j,        tN          _)        tM          d           	 ddl-m.} 	 t_          j0        tN          j(        ddddgt          d           n2# tJ          $ r%}t*                              d|           Y d}~nd}~ww xY w |            }|ri t
          j1        dt9          t          dz            i}te          |          r,|3                    dd           |3                    dd           ti          |dg|te          |          rdnd            n.ti          tN          j(        ddgte                      rdnd!           t	                       tM          d"           n# tJ          $ r}t*                              d#|           tM          d$           tM          d%           tM          d&t                      tM          d'tN          j(         d(           tM          d'tN          j(         d)           Y d}~nd}~ww xY w|tN          _)        |;	 t          j+        |d           t          j        |           n# t(          $ r Y nw xY w	 |                                  dS # t(          $ r Y dS w xY w# |tN          _)        |;	 t          j+        |d           t          j        |           n# t(          $ r Y nw xY w	 |                                  w # t(          $ r Y w w xY wxY w)*u  Finish a dependency install that a prior ``hermes update`` left half-done.

    Triggered on launch when ``.update-incomplete`` is present — meaning the
    code was pulled but the dep install was killed before it verified clean.
    Unconditionally bootstraps pip via ``ensurepip`` (a killed ``pip install``
    can wipe pip from the venv entirely, which blocks the venv from recovering
    on its own), then re-runs the editable ``.[all]`` install + core-dependency
    verification, then clears the marker.

    Never raises: a recovery failure must not block launch.  If it can't
    self-heal it prints the one-line manual command and leaves the marker so
    the next launch tries again.

    Concurrency: the marker lives next to the shared venv, so a gateway start
    plus a CLI launch (or two profiles starting at once) can both see it.  An
    ``O_EXCL`` lockfile ensures only one process runs the reinstall; the
    others skip and let the winner clear the marker.

    Output: everything — our status lines AND the streamed pip/uv install
    (which inherits fd 1) — is routed to stderr.  Launches whose stdout is a
    protocol stream (``hermes acp`` speaks JSON-RPC on stdout) must never get
    install noise on stdout.
    Nr   z.update-incomplete.lockrc  ru  z*Could not create install-recovery lock: %sr   u|   ✗ Hermes is running from the binary that needs to be replaced — the auto-recovery cannot overwrite a running executable.zW  Restart Hermes from a different terminal, then run the manual recovery command below:z    cd /d "r|  z    "z" -m pip install -e ".[all]"rB   r   ug   ⚠ A previous `hermes update` was interrupted mid-install — finishing dependency installation now...r  r   r  r  r  Tr  z,ensurepip during install recovery failed: %sr  r  r  r  r  
termux-allr  r,  r(  r(  uH   ✓ Dependency installation recovered — your install is healthy again.z'Interrupted-install recovery failed: %su3   ✗ Could not auto-recover the interrupted install.z  Recover manually with:    cd r  z -m ensurepip --upgradez -m pip install -e '.[all]')5r(  r,   rQ  rC  r-  r&   r.   O_CREATO_EXCLO_WRONLYrL   r  r  r6  FileExistsErrorr{  r|  rR  r  rd  rM   r]  r^  _is_windows_venv_scripts_dir_hermes_exe_shimsr  r   r4   rD  r6   r  Processr  ra  r  r   r   r|   rE   ra  r  dupdup2r   r  r  rA  rB  r'   r  rb  r  )	lock_pathfdr   scripts_dirshims	_shim_set_sr  _me_anc_anc_exe	_anc_normsaved_stdout_fdsaved_sys_stdoutr  r  r  s                    r   !_recover_from_interrupted_installrJ  1  s   0   '')) 
 ++4466 '))) 88IHWY
RY 6 DEE
	'''..00111
   	z||inn..77$>>  """ 	 	 	D	 H H H 	A3GGGGGGGGH }} *'))"%k22E &&)ee	 7 7B7!c"**,,&7&7&=&=&?&?@@@@" 7 7 7!c"ggmmoo666667!MMM ..**C!$S[[]](;(; ; # #%'+xxzzH(+DNN,B,B,D,D(E(E(K(K(M(MII( % % %$H%$	11!!I  
 "!N   ""?"?"?"?@@@!!. !. !. !.   <===% ) 0 0 2 2 2 2#* % % % $%"FF) 2#6 !   D OzE	# fQiiOGAqMMMM 	# 	# 	#"OOO	#Z
7	
 	
 	

*	F777777
R^T;_U$#'    
  R R RKSQQQQQQQQR Y[[F RBJRs<&;P7Q7QRR!&)) 3JJ|T222JJ|T222CUO*8*@*@K,,e     D^T51*8*:*:E,,   
 ,---\]]]] 	F 	F 	F LLBCHHHGHHH,---*L**+++@@@@AAADDDDEEEEEEEE	F &
&+++))))   	 	 	 	DD	 &
&+++))))   	 	 	 	D	s  BC# #
E5.AD43E54
E>E5 EE5	E5E00E5=AH>II	?M> 	AKM> 
K+(M> *K++A(M> M)(M> )
M63M> 5M66M> <M> >
N
N)O Y OY O(Y  U +P3 2U 3
Q"=QU Q""C"U Y 
W#B
WY W##Y 5)X 
X,+X,0Y 
YY[')Z[
Z[Z["Z76[7
[[[[   )r,  heartbeat_interval_secondsrL  c                   t          j                    t          j                    d	fd}t          j        |d          }|                                 	 t          j        | t          d|           	                                 |
                    d           dS # 	                                 |
                    d           w xY w)
a3  Run dependency install command with periodic heartbeat output.

    Some resolvers/build backends (especially when compiling Rust/C extensions)
    can stay quiet for minutes. Emit a simple elapsed-time heartbeat so users
    know ``hermes update`` is still progressing even if pip/uv itself is silent.
    r   Nc                                                     sOt          t          j                    z
            } t	          d|  dd                                          Md S d S )Nu%     … still installing dependencies (uC   s elapsed) — compiling Rust/C extensions can take several minutesTr  )r1  r   r{  r|  r|   )elapseddonerL  r   s    r   
_heartbeatz/_run_install_with_heartbeat.<locals>._heartbeat  s    ))677 	%*,,.//GL L L L    ))677 	 	 	 	 	rH   Tr&  )r  r.  r,  g?r(  r*  )r+  Eventr{  r|  r0  r   rA  rB  rQ  r  r*   )r9  r,  rL  rQ  rS  rP  r   s     `  @@r   _run_install_with_heartbeatrS    s     ?DJLLE        	
4888AGGIII			
 	
 	
 	
 	


	s 	


	ss   B& &,Cc                  "    t           j        dk    S )Nr  rE   r   rN   rH   r   r8  r8    s    <7""rH   c                      t           dz  } |                                 sdS | t                      rdndz  }|                                r|ndS )zKReturn the venv Scripts directory if we're running inside the project venv.r  NScriptsr  )rQ  r   r8  )venv_dirr  s     r   r9  r9    sS    f$H?? t{}}?))%@Gnn&&077D0rH   r@  c                      t                      sg S t          t                                ph d}|                    d            fdt	          |          D             S )u  Entry-point shims that uv may try to rewrite during ``pip install -e .``.

    On Windows these are .exe launchers generated by setuptools/uv. On POSIX
    they're regular Python scripts which can be replaced atomically — no
    self-replacement hazard exists outside Windows.
    >   
hermes-acpr   hermes-agentzhermes-gatewayc                      g | ]
}| d z  S z.exerN   r  r   r@  s     r   r  z%_hermes_exe_shims.<locals>.<listcomp>&  s$    BBBDKT---'BBBrH   )r8  r  _load_console_script_namesr   r  )r@  namess   ` r   r:  r:    sk     == 	*,,--Y1Y1Y1YE 
IIBBBBF5MMBBBBrH   )exclude_pidra  c                N   t                      sg S 	 ddl}n# t          $ r g cY S w xY wt                      }t	          |           D ]}	 |                    t          |                                                                                     J# t          $ r7 |                    t          |                                                     Y w xY w|sg S |t          |          h}nt          j                    h}	 t          t          |                    }	 |                    |                                          }n# t          $ r g }Y nw xY w|D ]}	 |                                }	n# t          $ r Y $w xY w|	s+	 t          t%          |	                                                                                    }
n8# t          t&          f$ r$ t          |	                                          }
Y nw xY w|
|v r9	 |                    t          |j                             # t          $ r Y w xY wn# t          $ r Y nw xY wg }	 |                    g d          }n# t          $ r g cY S w xY w|D ]'}	 |j        }n# t          $ r Y w xY w|                    d          }|                    d          }|r|||v rO	 t          t%          |                                                                                    }n8# t          t&          f$ r$ t          |                                          }Y nw xY w||v rZ|                    d          pt%          |          j        }|                    t          |          t          |          f           )|S )um  Find other live processes whose .exe is one of our entry-point shims.

    Windows blocks DELETE/REPLACE on a running .exe — and even RENAME on the
    same .exe when another process opened it without ``FILE_SHARE_DELETE``.
    The Hermes Desktop Electron app spawns ``hermes.EXE`` as a backend child,
    so during ``hermes update`` the user-invoked process and the desktop's
    child both hold the same file. The quarantine rename then fails with
    ``[WinError 32]`` and uv inherits the lock.

    This helper enumerates processes whose ``exe`` matches one of the venv's
    shims (``hermes.exe`` / ``hermes-gateway.exe``) and returns ``(pid,
    process_name)`` pairs. The caller's own PID and its entire ancestor
    chain are excluded so the running ``hermes update`` invocation never
    reports itself — this matters on Windows where the setuptools .exe
    launcher (``hermes.exe``) is a separate process from the Python
    interpreter it loads (``python.exe``).

    Returns an empty list off-Windows, on missing psutil, or when no other
    instances exist. Never raises — process enumeration is best-effort.
    r   N)r  r  r   r  r  r   )r8  r  r   r  r:  r   r4   rD  r6   rM   r   r&   r  r  rk  r;  ra  r  r   r   r  r  r  r(   r   r  )r@  ra  r  
shim_pathsshimr,  seed	ancestorsancestoranc_exeanc_normmatchesr  r$  r  r  r  exe_normr   s                      r   #_detect_concurrent_hermes_instancesrl  )  s1   . == 	   			 55J!+.. . .	.NN3t||~~..44667777 	. 	. 	.NN3t99??,,-----	. 	. "%k"2"2!3	}D&&''	t,,4466II 	 	 	III	! 	 	H",,..    0tG}}446677==??Z( 0 0 0w<<--//0:%% $$S%6%67777    H &	      &(G''(>(>(>??		   			  2 2	9DD 	 	 	H	hhuoohhuoo 	ckSL%8%8	(499,,..//5577HH$ 	( 	( 	(3xx~~''HHH	(z!!88F##5tCyy~DNNCHHc$ii0111Ns    &&	AB>CC H= 'E H= EH= EH= E10H= 1
E>;H= =E>>H= A GH= 2G;8H= :G;;H= 'H+*H= +
H85H= 7H88H= =
I
	I
I( (I76I7 J
JJA L2MMrj  c                    |dz  }dg}| D ] \  }}|                     d| d|            !|                     d           |                     d| d           |                     d           |                     d           |                     d	           |                     d
           |                     d           | rwd                    d | D                       }|                     d           |                     d           |                     d| d           |                     d           |                     d           |                     d           d                    |          S )zCBuild a human-readable explanation + remediation hint for the user.z
hermes.exeu"   ✗ Another hermes.exe is running:    PID r  rR   z'  Updating now would fail to overwrite z becausez1  Windows blocks REPLACE on a running executable.z9  Close Hermes Desktop, exit any open `hermes` REPLs, andz;  stop the gateway (`hermes gateway stop`) before retrying.r7  c              3   &   K   | ]\  }}d | V  dS )z/PID NrN   )r  r  rG  s      r   r  z7_format_concurrent_instances_message.<locals>.<genexpr>  s,      @@fc1MCMM@@@@@@rH   z8  If you've already closed everything and these PIDs arez8  stale, terminate them directly, then retry the update:z      taskkill z /Fz9  Override with `hermes update --force` if you've alreadyz7  confirmed those processes will not write to the venv.rc  )r  r*   )rj  r@  rd  rz  r  r   pid_argss          r   $_format_concurrent_instances_messagerq    s    %D12E / /	T---t--....	LL	LLI4IIIJJJ	LLDEEE	LL	LLLMMM	LLNOOO	LL 88@@@@@@@OPPPOPPP4x444555R	LLLMMM	LLJKKK99UrH   r  )max_attemptsrr  c          	      J   g }t                      s|S ddl}t          |                                dz            }g d}t          dt	          |t          |                              }t          |           D ](}|                                s|                    |j	        d| z             }d}	t          |          D ]m}
||
         dz  }|r|                    |           	 |                    |           |                    ||f           d}	 n# t          $ r}|}	Y d}~fd}~ww xY w|	t          ||          }|r(t!          d|j         d	           t!          d
           t!          d|j         d|	j        j         d           t!          d           *|S )uP  Pre-empt Windows file lock on the running ``hermes.exe``.

    Windows allows RENAMING a mapped/running executable (the kernel tracks the
    file by handle, not path), but blocks DELETE/REPLACE while it's loaded. uv
    needs to overwrite the entry-point shims during ``pip install -e .``;
    when ``hermes update`` runs, ``hermes.exe`` IS the live process, and uv
    fails with ``Access is denied. (os error 5)``.

    We rename live shims to ``hermes.exe.old.<unix-ms>`` first. uv then writes
    fresh shims at the original paths. The ``.old`` files are cleaned up on
    the next hermes invocation by ``_cleanup_quarantined_exes``.

    Rename can still fail when *another* process has opened the .exe without
    ``FILE_SHARE_DELETE`` — typically AV real-time scanners with transient
    handles (recovers in <1s), or the Hermes Desktop backend child process
    (won't recover until the user closes it). We mitigate:

    1. Retry up to ``max_attempts`` times with exponential backoff
       (100/250/500/1000 ms). Handles the AV-scanner case.
    2. If all retries fail, schedule the .exe for replacement on next
       reboot via ``MoveFileExW(MOVEFILE_DELAY_UNTIL_REBOOT)``. This still
       lets uv create a fresh shim at the original path (Windows will keep
       the old file's content under a new name until the reboot), so the
       update can complete; the user just needs to reboot to fully unload
       the stale image.
    3. Print a clear warning naming the most likely culprit (running
       Hermes Desktop / gateway / REPL) and pointing to ``--force``.

    Returns the list of (original, quarantined) pairs so the caller can roll
    back if the install itself fails before uv writes a replacement. Pairs
    where we used ``MOVEFILE_DELAY_UNTIL_REBOOT`` are NOT returned — they
    are already deferred and roll-back is meaningless.
    r   N  )r   d        rt  rB   z.old.g     @@     ⚠ zD is locked by another process; scheduled replacement on next reboot.zb    The new shim was written at the same path, but a reboot is needed to fully unload the old one.u     ⚠ Could not quarantine rq   z&: another process is holding it open).zy    Close Hermes Desktop, exit other `hermes` REPLs, stop the gateway, or pause AV scanning, then re-run `hermes update`.)r8  r|  r   r  r  r   r:  r,   r  r  r  r  r{  r  rM   _schedule_replace_on_rebootr|   r   	__class__r&  )r@  rr  movedr|  r]  
backoff_msattemptsrd  r~  last_excattemptdelayr  	scheduleds                 r   _quarantine_running_hermes_exer    s.   H &(E== KKK		d"##E *))J1c,J8899H!+.. 2
 2
{{}} 	!!$+"?@@#'X 	 	Gw'&0E "

5!!!F###dF^,,,     0f==	 	/ / / /   @    	4$) 4 4x7I7R 4 4 4	
 	
 	
 	J	
 	
 	
 	

 Ls   '.D
D-!D((D-rd  quarantine_targetc                 @   t                      sdS 	 ddl}ddlm} d}d}|j        j        j        }|j        |j        |j        g|_        |j	        |_
         |t          |           t          |          ||z            }t          |          S # t          $ r Y dS w xY w)u7  Schedule ``shim`` -> ``quarantine_target`` via PendingFileRenameOperations.

    Uses Win32 ``MoveFileExW`` with ``MOVEFILE_REPLACE_EXISTING |
    MOVEFILE_DELAY_UNTIL_REBOOT``. The OS persists the rename in
    ``HKLM\System\CurrentControlSet\Control\Session Manager\
    PendingFileRenameOperations`` and applies it before any user-mode code
    runs on next boot — at which point no process can hold the .exe.

    Returns ``True`` if the schedule call succeeded, ``False`` otherwise
    (non-Windows, ctypes failure, lack of privilege, etc.). Never raises.
    Fr   N)wintypesrB   r  )r8  r   r  windllkernel32MoveFileExWLPCWSTRDWORDargtypesBOOLrestyper4   rV   r   )rd  r  r   r  MOVEFILE_REPLACE_EXISTINGMOVEFILE_DELAY_UNTIL_REBOOTr  rL  s           r   ry  ry    s     == u######$'!&)#m,8 ( 0(2BHNS&m[II!""%(CC
 

 Bxx   uus   A<B 
BBr{  c                     | D ]S\  }}	 |                                 s)|                                 r|                    |           D# t          $ r Y Pw xY wdS )zMRoll back ``_quarantine_running_hermes_exe`` if uv didn't write replacements.N)r,   r{  rM   )r{  originalquarantineds      r   _restore_quarantined_exesr  B  s    !&  +	??$$ -););)=)= -""8,,, 	 	 	D		 s   =A
AAr,  r@  c                    g }|t          |          }	 t          | |           dS # t          $ r |t          |            w xY w)uF  Run an editable install, quarantining the running ``hermes.exe`` first.

    Any ``pip install -e .`` (or ``--reinstall``) rewrites the entry-point
    shims, and on Windows the live ``hermes.exe`` is the running process —
    pip can neither delete nor overwrite it, so without quarantine the shim
    is left missing and ``hermes`` drops off PATH. This wraps
    :func:`_run_install_with_heartbeat` with the same rename-out-of-the-way /
    restore-on-failure dance that the primary install path uses, so EVERY
    install that touches the shims is protected — including the
    verification-repair reinstalls in
    :func:`_verify_core_dependencies_installed`, which previously called
    ``_run_install_with_heartbeat`` directly and bypassed quarantine.

    Off-Windows (``scripts_dir is None``) this is a thin pass-through.
    Nr2  )r  rS  BaseExceptionr  )r9  r,  r@  r{  s       r   _run_quarantined_installr  L  sn    * &(E.{;;#CS111111    "%e,,,s	   ( Ac                     t                      sdS | t                      } | dS 	 |                     d          D ]'}	 |                                 # t          $ r Y $w xY wdS # t          $ r Y dS w xY w)a  Sweep ``hermes.exe.old.*`` left by prior updates.

    Called early on every hermes invocation. The .old files are unlocked once
    their owning process exited, so deletion succeeds the next run. Silent
    no-op when nothing's there or on file-locked / permission errors.
    Nz*.exe.old.*)r8  r9  r  rd  rM   )r@  stales     r   _cleanup_quarantined_exesr  n  s     == ')) %%m44 	 	E   	 	
    s4   A% AA% 
A A% A  A% %
A32A3c                  j   	 ddl m}  n3# t          $ r&}t                              d|           Y d}~dS d}~ww xY w	 |                                 }n3# t          $ r&}t                              d|           Y d}~dS d}~ww xY w|sdS t                       t          dt          |           d           	 |                     d	          }n*# t          $ r}t          d
|            Y d}~dS d}~ww xY wd |	                                D             }d |	                                D             }d |	                                D             }d |	                                D             }|r5t          dt          |           dd
                    |                      |r t          dt          |           d           |rld
                    d |D                       }|d         d                             dd          d         }	t          dt          |           d|	 d|            |ry|D ]V\  }
}|                    dd          d         }	t          |	          dk    r|	dd         dz   }	t          d|
 d|	            Wt          d           t          d            dS dS )!u  Refresh lazy-installed backends after a code update.

    When pyproject.toml's ``[all]`` extra was slimmed down (May 2026), most
    optional backends moved to ``tools/lazy_deps.py`` and only install on
    first use. ``hermes update`` runs ``uv pip install -e .[all]`` which
    leaves those packages untouched — so if we bump a pin in
    :data:`LAZY_DEPS` (CVE response, transitive bug fix), users who already
    activated the backend keep the stale version forever.

    This function asks lazy_deps which features the user has previously
    activated and reinstalls them under the current pins. Features the
    user never enabled stay quiet — no churn for cold backends.

    Never raises. A failure here must not block the rest of the update.
    r   )	lazy_depsz(Lazy refresh skipped (import failed): %sNz1Lazy refresh skipped (active_features failed): %su   → Refreshing z active lazy backend(s)...FrH  u(     ⚠ Lazy refresh failed unexpectedly: c                 $    g | ]\  }}|d k    |S )	refreshedrN   r  r  r  s      r   r  z1_refresh_active_lazy_features.<locals>.<listcomp>  s&    CCCtq!!{2B2B2B2B2BrH   c                 $    g | ]\  }}|d k    |S )r  rN   r  s      r   r  z1_refresh_active_lazy_features.<locals>.<listcomp>  s!    ???TQYqrH   c                 F    g | ]\  }}|                     d           ||fS )zfailed:r
  r  s      r   r  z1_refresh_active_lazy_features.<locals>.<listcomp>  s1    LLLAALL4K4KLq!fLLLrH   c                 F    g | ]\  }}|                     d           ||fS )zskipped:r
  r  s      r   r  z1_refresh_active_lazy_features.<locals>.<listcomp>  s1    NNN$!QQ\\*5M5MN1vNNNrH   r  z refreshed: r       ✓ z already currentc              3       K   | ]	\  }}|V  
d S r  rN   )r  r  rG  s      r   r  z0_refresh_active_lazy_features.<locals>.<genexpr>  s&      001!000000rH   rB   r  r  u     · z
 skipped (r     r#  rx  z failed to refresh: z9  Backends keep their previously-installed version; rerunz6  `hermes update` once the upstream issue is resolved.)toolsr  r   r]  r^  active_featuresr|   r   refresh_active_featuresr  r*   rf   )r  r   r  resultsr  r  rd  skippedr`  r\  featurer  s               r   _refresh_active_lazy_featuresr    sF    #######   ?EEE**,,   H#NNN  	GGG	
CCKK
C
C
CDDD3353AA    	>>>???	 DCw}}CCCI??W]]__???GLLLLLFNN'--//NNNG KIs9~~II499Y3G3GIIJJJ 75s7||555666 B 		0000000A$$T1--b1@c'll@@f@@@@AAA H% 	B 	BOGV\\$**2.F6{{S  -@7@@@@AAAAIJJJFGGGGGH Hs?   	 
949A 
BA==B8C 
C6C11C6r1  install_cmd_prefixc                    t                      rt                      nddt          t                   ddf fd}	  |ddd| dg           t	           	           dS # t
          j        $ r t          d
           Y nw xY w |g d           g }g }t          |          D ]T}	  |ddd| dg           |	                    |           +# t
          j        $ r |	                    |           Y Qw xY w|r%t          dd
                    |                      |r%t          dd
                    |                      t           |           t	           	           dS )a   Install base deps plus as many optional extras as the environment supports.

    By default this targets ``.[all]``; Termux callers can pass
    ``group='termux-all'`` to use the curated Android-compatible profile.

    On Windows, pre-renames live ``hermes.exe`` / ``hermes-gateway.exe`` shims
    in the venv Scripts dir before each install attempt so uv can write fresh
    copies (Windows blocks REPLACE on a running .exe but allows RENAME). See
    ``_quarantine_running_hermes_exe`` for the rationale.
    Nrx  r   c                 2    t          | z              d S )Nr  )r  )rx  r,  r  r@  s    r   _installzE_install_python_dependencies_with_optional_fallback.<locals>._install  s0     %3K	
 	
 	
 	
 	
 	
rH   r  rU  z.[r  r2  u`     ⚠ Optional extras failed, reinstalling base dependencies and retrying extras individually...)r  rU  r  r2  u0     ✓ Reinstalled optional extras individually: r  u1     ⚠ Skipped optional extras that still failed: r1  )r8  r9  r  r4   !_verify_console_scripts_installedrA  r  r|   r&  r  r*   #_verify_core_dependencies_installed)r  r,  r(  r  failed_extrasinstalled_extrasr  r@  s   ``     @r   r  r    s     *5@#%%%DK
tCy 
T 
 
 
 
 
 
 
 


)T====1222)*<#FFFF( 
 
 
n	
 	
 	
 	
 	


 H###$$$!M"$2??? ( (	(Hi}E}}}5666##E****, 	( 	( 	(  '''''	(  
\tyyIY?Z?Z\\	
 	
 	
  
Z		-@X@XZZ	
 	
 	
 ((:5QQQQ%&8cBBBBBBs#   #A$ $BB,'C$C;:C;c                     	 ddl } n# t          $ r g cY S w xY wt          dz  }|                                sg S 	 t	          |d          5 }|                     |          }ddd           n# 1 swxY w Y   |                    di                               di           pi }d |D             S # t          $ r'}t          	                    d|           g cY d}~S d}~ww xY w)	zCReturn ``[project.scripts]`` entry-point names from pyproject.toml.r   Nr   rg  r!  r  c                 0    g | ]}|t          |          S rN   )r4   )r  r   s     r   r  z._load_console_script_names.<locals>.<listcomp>  s#    666d6D		666rH   z>console script verification: failed to read pyproject.toml: %s)
r#  r   rQ  rC  r.   r/   r(   r   r]  r^  )r#  	pyprojectr  rq  r  r  s         r   r_  r_    sR      			 //I 	)T"" 	#a<<??D	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	#((9b))--i<<B66g6666   UWXYYY						sM    B/ 
A, B/ ,A00B/ 3A04:B/ /
C 9CC C r2  c                   t                      sdS t                      dS t                      sdS dt          t                   ffd} |            }|sdS t          dt          |           dd                    |                      t          d           	 t          | g dz   |	           nG# t          j
        $ r5}t                              d
|           t          d           Y d}~dS d}~ww xY w |            }|r(t          dd                    |           d           dS t          d           dS )u  Ensure every declared console_script shim exists on disk after install.

    On Windows, ``uv pip install -e .`` can register ``hermes.exe`` in the
    wheel RECORD while the file never lands on disk — typically when the live
    ``hermes.exe`` shim is locked during ``hermes update``, or when uv/distlib
    skips a launcher write. The symptom is ``hermes-agent.exe`` and
    ``hermes-acp.exe`` present but ``hermes.exe`` missing, so ``hermes`` drops
    off PATH even though the install reported success (issue #52931).

    If any shim is missing we reinstall with ``--reinstall -e .`` under the
    same quarantine dance as the primary install path, then re-check.
    Nr   c                       fd D             S )Nc                 H    g | ]}| d z                                   |S r]  )rC  r^  s     r   r  zG_verify_console_scripts_installed.<locals>._missing.<locals>.<listcomp>=  sF     
 
 
T---/88::

 
 
rH   rN   )r`  r@  s   r   _missingz3_verify_console_scripts_installed.<locals>._missing<  s.    
 
 
 

 
 
 	
rH        ⚠ Verification: z$ console script(s) missing on disk: r  u3     → Reinstalling entry points with --reinstall...r  --reinstallrU  r  r  z6console script verification: repair install failed: %sub     ⚠ Entry point repair failed; try `hermes update --force` after closing other hermes processes."     ⚠ Still missing after repair: z1. Workaround: python -m hermes_cli.main <command>u'     ✓ All console entry points restored)r8  r9  r_  r  r4   r|   r   r*   r  rA  r  r]  re  )r  r,  r  missingr  still_missingr`  r@  s         @@r   r  r     s   " == #%%K&((E 
d3i 
 
 
 
 
 
 
 hjjG 		 s7|| 	  	 99W	  	    

?@@@ !F!F!FF#	
 	
 	
 	
 	

 (   OQRSSS.	
 	
 	
 	 HJJM 9>=1I1I > > >	
 	
 	
 	
 	

 	788888s   #B; ;C?
*C::C?c          
      2
   	 ddl }n# t          $ r Y dS w xY wt          dz  }|                                sdS 	 t	          |d          5 }|                    |          }ddd           n# 1 swxY w Y   |                    di                               dg           pg }n3# t          $ r&}t          	                    d|           Y d}~dS d}~ww xY wg }		 ddl
m}
 |D ]?}	  |
|          }|	                    |j        |j        f           0# t          $ r Y <w xY wn# t          $ r |D ]}|                    d	d
          d         }dD ]$}||v r|                    |d
          d         } n%|                                                    dd
          d                                         }|r|	                    |df           Y nw xY wg |	D ]l\  }}|                    |           	 |                                r                    |           H# t          $ r                     |           Y iw xY wsdS t%          |           dS dt&          t(                   ffd} |            }|sdS t+          dt-          |           dd                    |dd                    t-          |          dk    rdnd            t+          d           t1                      rt3                      nd}g d}	 t5          | |z   |           nG# t6          j        $ r5}t                              d|           t+          d           Y d}~dS d}~ww xY w |            }|st+          d           dS i |D ]}|                    d	d
          d                                         }|}dD ]$}||v r|                    |d
          d         } n%||                                                    dd
          d                                         <   fd|D             }t+          dd                    |                      	 t=          | ddg|z              n^# t6          j        $ rL}t                              d |           t+          d!d                    |           d"           Y d}~dS d}~ww xY w |            }|r(t+          d#d                    |           d"           dS t+          d           dS )$u  Check that every base dep from pyproject.toml is importable; if not, retry.

    Reads ``pyproject.toml`` directly (so we don't trust the venv's stale
    metadata), filters out deps gated by ``;`` environment markers that don't
    apply to this platform, and runs ``importlib.metadata.version()`` in the
    venv interpreter for each one. If anything is missing we reinstall the
    base group with ``--reinstall`` to force uv to re-resolve, then check
    again. We treat the final state as a warning rather than a hard failure
    so a single broken-on-PyPI dep can't block an otherwise-successful
    update — but the warning makes the partial install visible at the spot
    that caused it, instead of hours later in a downstream subprocess.
    r   Nr   rg  r!  dependenciesz3dep verification: failed to read pyproject.toml: %s)Requirement;rB   )z==z>=z<=z~=>r  z!=r"  r   c                     d} 	 t          j        t                    d| gddd          }n4# t          $ r'}t                              d|           g cY d }~S d }~ww xY wd |j                                        D             S )Nzimport importlib.metadata as md, sys
missing=[]
for name in sys.argv[1:]:
    try: md.version(name)
    except md.PackageNotFoundError: missing.append(name)
print('\n'.join(missing))
r   TF)r>  r9  r.  r,  z'dep verification: subprocess failed: %sc                 ^    g | ]*}|                                 |                                 +S rN   r  r  rj   s     r   r  zN_verify_core_dependencies_installed.<locals>._missing_deps.<locals>.<listcomp>  s-    TTTtzz||T

TTTrH   )rA  rB  r4   r   r]  r^  r  r8  )check_scriptr  r  
applicabler,  r?  s      r   _missing_depsz:_verify_core_dependencies_installed.<locals>._missing_deps  s    + 	
	^[!!4C
C#  FF  	 	 	LLBAFFFIIIIII	 UT)A)A)C)CTTTTs   +1 
A"AA"A"r  z( declared dep(s) missing after install: r  r  r#  rR   u;     → Reinstalling base group with --reinstall to repair...r  r  z+dep verification: repair install failed: %su@     ⚠ Repair install failed; check `hermes update` output above.u2     ✓ All declared core dependencies now installedc                 <    g | ]}                     ||          S rN   r6  )r  r  name_to_specs     r   r  z7_verify_core_dependencies_installed.<locals>.<listcomp>  s)    ;;;\a##;;;rH   u1     → Force-installing remaining missing dep(s): r  r  r2  z/dep verification: per-package repair failed: %su     ⚠ Could not install: zC. Run `hermes update --force` after closing other hermes processes.r  )r#  r   rQ  rC  r.   r/   r(   r   r]  r^  packaging.requirementsr  r  r   r  rf   r5   evaluate_resolve_install_target_pythonr  r4   r|   r   r*   r8  r9  r  rA  r  re  rS  )r  r,  r(  r#  r  r  rq  raw_depsr  depsr  rt  reqrK  opr   r  r  r  r@  repair_argsr  barespecsfinal_missingr  r  r?  s    `                       @@@r   r  r  e  s   $    //I )T"" 	#a<<??D	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	#88Ir**..~rBBHb   JANNN /1D*666666 	 	D!k$''SXsz23333   		  	* 	* 	* 	* 	*D::c1%%a(D>  ::::b!,,Q/DE  ::<<%%c1--a06688D *T4L)))	* 	*	* J $ $f>d###	$   (!!$''' 	$ 	$ 	$d#####	$   11CSIIKU49 U U U U U U U U, mooG 		Gs7|| 	G 	G99WRaR[!!	G,/LL1,<,<55"	G 	G   

GHHH *5@#%%%DK777K ,#;	
 	
 	
 	
 	
 (   DaHHHPQQQ
 "MOOM BCCC
 L C Czz#q!!!$**,,: 	 	BTzzzz"a((+  ?CTZZ\\''Q//288::;;;;;;];;;E	NDIIe<L<LNN  
#)]!CU!CC	
 	
 	
 	
 	
 (   H!LLLP		-(@(@ P P P	
 	
 	
 	 "MOOM DP=1I1I P P P	
 	
 	
 	
 	

 	BCCCCCs    
B' A/#B' /A33B' 6A37/B' '
C1CC
D& (,DD& 
D"D& !D""D& &B+GG9)H##IIL$ $M(3*M##M(Q6 6SASSc                 L   |rYd|v rUt          |d                   }|t                      rdndz  }|t                      rdndz  }|                                r|S | rFt          | d                   }|                                rd|j                                        vr|S dS )	a  Figure out which Python interpreter the install just targeted.

    ``_install_python_dependencies_with_optional_fallback`` is called with
    either ``[uv, pip]`` (and a ``VIRTUAL_ENV`` env var pointing at the
    target venv) or ``[sys.executable, -m, pip]`` (the in-process Python).
    The verification step needs the *resulting* environment's Python so
    ``importlib.metadata`` queries the right site-packages.
    r  rW  r  
python.exepythonr   uvN)r   r8  r,   r   r6   )r  r,  	venv_rootr  r   firsts         r   r  r    s      }##]+,,	KMMDyyuE{}}J||(K	 	  '*++<<>> 	d%**:*:*<*<<<L4rH   c                      t          |           S r  )r/  r2  s    r   r  r  8  s    )#...rH   c                  "    t           j        dk    S )NandroidrU  rN   rH   r   _is_android_pythonr  <  s    <9$$rH   c          	      >   ddl }ddl}ddlm}m} |                                5 }t          |          }|dz  }|j                            ||            |||          }	t          | ddt          |	          gz   |           ddd           dS # 1 swxY w Y   dS )a  Install psutil on Android by patching upstream platform detection.

    psutil's setup currently gates Linux sources behind
    ``sys.platform.startswith('linux')``. On Termux Python reports
    ``sys.platform == 'android'``, so setup aborts with
    "platform android is not supported" despite compiling fine when using the
    Linux source path.

    We patch only the extracted build tree used for this install attempt;
    nothing is persisted in the repository.

    Stopgap: remove this once https://github.com/giampaolo/psutil/pull/2762
    merges and ships in a release. The standalone installer script uses the
    same shared helper and should be removed together.
    r   N)
PSUTIL_URLprepare_patched_psutil_sdistzpsutil.tar.gzr  z--no-build-isolationr2  )r[  r  hermes_cli.psutil_androidr  r  r  r   requestr  rS  r4   )
r  r,  r[  urllibr  r  r  tmp_patharchivesrc_roots
             r   _install_psutil_android_compatr  @  s   ( OOORRRRRRRR		$	$	&	& 	
#99_,"":w777//BB#)-CS]]!SS	
 	
 	
 	
	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
s   A BBBr  c                 T   ddl m}  |            }|r|S t                      sdS t          j        d          }|r|S 	 t          d           t          j        | g dz   t          d          }|j	        dk    rdS n# t          $ r Y nw xY w |            pt          j        d          S )	a  Best-effort uv bootstrap on Termux for faster update installs.

    The normal path (``ensure_uv()`` in managed_uv) installs the managed
    standalone uv into ``$HERMES_HOME/bin/uv``, but on Termux the official
    installer may not work (glibc vs bionic).  Prefer a uv already on PATH
    (e.g. ``pkg install uv``); only if there is none do we fall back to a
    wheel-only ``pip install uv`` so we never source-build the Rust crate.
    r   )
resolve_uvNr  uL     → Termux detected: trying to install uv for faster dependency updates...)r  r  z--only-binaryz:all:Fr  )r  r  r  rW  rX  r|   rA  rB  rQ  rY  r   )r  r  r  	system_uvr  s        r   r  r  d  s     100000z||H  t T""I 
\]]]AAAA
 
 

 !!4 "    :<<-6<---s   ;A< <
B	B	c                  R   ddl m} m}  | d          }|sd S t          dz                                  sd S t          d           g d} |t                                }g |d}t          |t          t          |          d|	          }|j	        dk    rat          d
           |j
        r|j
        pd                                nd}|r*t          d|                                d                     d S g |dddd}t          |t          t          |          d|	          }	|	j	        dk    rt          d           d S t          d           |	j
        r|	j
        pd                                nd}|r,t          d|                                d                     d S d S )Nr   r  r  r  u$   → Updating Node.js dependencies...)r  r  r  z--workspaces=falseFrB  u%     ⚠ npm install failed in repo rootrR   r  r  r  r  rV  u:     ✓ repo root + ui-tui, web workspaces (desktop skipped)u"     ⚠ npm workspace install failed)r   r  r  rQ  r,   r|   rA  rK  r  rY  r   r5   r8  )
r  r  r  rC  r   	root_argsroot_resultr   ws_args	ws_results
             r   r  r    s#   LLLLLLLL

u
%
%C >)1133  

0111@@@J%%&6&8&899I 4*323I0##  K ""56667B7IQ+$*11333r 	42**,,R022333 K
JMJ8J]JEJG.>>  I q  JKKKKK23335>5EM)"(b//1112 	42**,,R02233333	4 	4rH   c                   6    e Zd ZdZd Zd Zd Zd Zd Zd Z	dS )	_UpdateOutputStreamu  Stream wrapper used during ``hermes update`` to survive terminal loss.

    Wraps the process's original stdout/stderr so that:

    * Every write is also mirrored to an append-only log file
      (``~/.hermes/logs/update.log``) that users can inspect after the
      terminal disconnects.
    * Writes to the original stream that fail with ``BrokenPipeError`` /
      ``OSError`` / ``ValueError`` (closed file) no longer cascade into
      process exit — the update keeps going, only the on-screen output
      stops.

    Combined with ``SIGHUP -> SIG_IGN`` installed by
    ``_install_hangup_protection``, this makes ``hermes update`` safe to
    run in a plain SSH session that might disconnect mid-install.
    c                 0    || _         || _        d| _        d S NF)	_original_log_original_broken)selfr  log_files      r   __init__z_UpdateOutputStream.__init__  s    !	 %rH   c                    | j         ,	 | j                             |           n# t          $ r Y nw xY w| j        r-t	          |t
          t          f          rt          |          ndS 	 | j                            |          S # t          t          t          f$ r7 d| _        t	          |t
          t          f          rt          |          ndcY S w xY w)Nr   T)r  rL   r   r  r2   r4   rS  r   r  BrokenPipeErrorrM   r   )r  rq  s     r   rL   z_UpdateOutputStream.write  s    9 	%%%%      	F *4#u > >E3t999AE	F>''---*5 	F 	F 	F %)D! *4#u > >E3t999AEEE		Fs   $ 
11)B ACCc                     | j         +	 | j                                          n# t          $ r Y nw xY w| j        rd S 	 | j                                         d S # t
          t          t          f$ r d| _        Y d S w xY w)NT)r  r  r   r  r  r	  rM   r   r  s    r   r  z_UpdateOutputStream.flush  s    9 	!!!!     	F	)N  """""*5 	) 	) 	)$(D!!!!	)s   # 
00A A:9A:c                 j    | j         rdS 	 | j                                        S # t          $ r Y dS w xY wr  )r  r  rK   r   r	  s    r   rK   z_UpdateOutputStream.isatty  sM      	5	>((*** 	 	 	55	s   $ 
22c                 4    | j                                         S r  )r  filenor	  s    r   r	  z_UpdateOutputStream.fileno   s     ~$$&&&rH   c                 ,    t          | j        |          S r  )r0   r  )r  r   s     r   r'  z_UpdateOutputStream.__getattr__   s    t~t,,,rH   N)
r&  
__module____qualname____doc__r 	  rL   r  rK   r	  r'  rN   rH   r   r  r    s{         "& & &
F F F() ) )  ' ' '
- - - - -rH   r  gateway_modec                    t           j        t           j        ddd}| r|S ddl}t	          |d          r9	 |                    |j        |j                   n# t          t          f$ r Y nw xY w	 ddl	m
}  |            dz  }|                    dd	           |d
z  }t          |ddd          }ddl}|                    d|j                                                            d           d           ||d<   t#          |d         |          t           _        t#          |d         |          t           _        d|d<   n# t$          $ r d|d<   Y nw xY w|S )u$  Protect ``cmd_update`` from SIGHUP and broken terminal pipes.

    Users commonly run ``hermes update`` in an SSH session or a terminal
    that may close mid-install.  Without protection, ``SIGHUP`` from the
    terminal kills the Python process during ``pip install`` and leaves
    the venv half-installed; the documented workaround ("use screen /
    tmux") shouldn't be required for something as routine as an update.

    Protections installed:

    1. ``SIGHUP`` is set to ``SIG_IGN``.  POSIX preserves ``SIG_IGN``
       across ``exec()``, so pip and git subprocesses also stop dying on
       hangup.
    2. ``sys.stdout`` / ``sys.stderr`` are wrapped to mirror output to
       ``~/.hermes/logs/update.log`` and to silently absorb
       ``BrokenPipeError`` when the terminal vanishes.

    ``SIGINT`` (Ctrl-C) and ``SIGTERM`` (systemd shutdown) are
    **intentionally left alone** — those are legitimate cancellation
    signals the user or OS sent on purpose.

    In gateway mode (``hermes update --gateway``) the update is already
    spawned detached from a terminal, so this function is a no-op.

    Returns a dict that ``cmd_update`` can pass to
    ``_finalize_update_output`` on exit.  Returning a dict rather than a
    tuple keeps the call site forward-compatible with future additions.
    NF)prev_stdoutprev_stderrr  r  r   SIGHUPr   logsTr`  z
update.logarB   r   )	bufferingr   z
=== hermes update started seconds)timespecz ===
r  r	  r	  r  )rE   r  r   ri  r   r	  SIG_IGNr   rM   r  r   rd  r.   r  rL   r  r  r  r   )r	  rG  rs  _get_hermes_homelogs_dirlog_pathr  _dts           r   _install_hangup_protectionr	     s   < zz	 E   w!! 	NN7>7?;;;;G$ 	 	 	 D	! 	JIIIII##%%.td333l*#WEEEH|!!++Y+??H H H	
 	
 	

 %j(})=xHH
(})=xHH
!k ! ! ! !j!
 Ls$    A A*)A*.CE EEr9  c                     | sdS t           j        }t          |dd          }|dS 	 |                    |                     d          r| n| dz              |                                 dS # t          $ r Y dS w xY w)a  Write ``text`` to ``~/.hermes/logs/update.log`` only, never the terminal.

    During ``hermes update`` ``sys.stdout`` is an ``_UpdateOutputStream`` that
    mirrors to both the terminal and ``update.log``. Loud, low-signal
    subprocess output (npm installs, the Electron/vite build, the cua-driver
    installer's "Next steps" wall) should be captured and tucked into the log
    so failures stay debuggable, without flooding the user's terminal. This
    reaches past the mirroring stream straight to the underlying log handle.
    Nr  rc  )rE   r  r0   rL   r  r  r   )r9  streamr  s      r   _log_only_writer	  a   s      ZFvvt,,Ht}}T22CtttDDD   s   AA, ,
A:9A:rU  c                    t          j        | ||dt           j        t           j        ddd	  	        }t	          |j        pd           |S )zRun ``cmd`` capturing combined output into update.log (not the terminal).

    Returns the ``CompletedProcess`` (with ``stdout`` populated) so the caller
    can decide whether to surface the captured output on failure.
    FTr   r3  )r  r,  r.  r  r   r9  r   r5  rR   )rA  rB  r  r.  r	  r  )r9  r  r,  r  s       r   _run_logged_subprocessr 	  x   sV     ^ 
 
 
F FM'R(((MrH   c                    | sdS |                      d          rx	 |                      dt          j                  t          _        n# t          $ r Y nw xY w	 |                      dt          j                  t          _        n# t          $ r Y nw xY w|                      d          }|<	 |                                 |                                 dS # t          $ r Y dS w xY wdS )zWRestore stdio and close the update.log handle opened by ``_install_hangup_protection``.Nr  r	  r	  r  )r(   rE   r  r   r   r  r6  )rG  r  s     r   _finalize_update_outputr"	     s    yy 	=#*==CJJ 	 	 	D		=#*==CJJ 	 	 	D	yy$$H	NNNN 	 	 	DD		 s5   *A 
AA*B 
BB*(C 
C"!C"c                 P    t          | dd          pd                                pdS )a5  Normalize ``args.branch`` into a non-empty branch name.

    Centralizes the "default to main, accept --branch override, treat empty
    or whitespace-only values as the default" parsing so every consumer of
    ``--branch`` (check path, git-update path, ZIP-fallback path) agrees on
    the same answer.
    r  Nr  )r0   r5   r  s    r   r  r     s,     D(D))3V::<<FFrH   r  )branch_explicitr  r$	  c          	      	   ddl m}  |t                    }|dk    r1ddl m} t	           |                       t          j        d           |dk    rddl m} ddlm	} |r| d	k    rt	          d
|  d            |            }|$t	          d           t          j        d           n@|dk    rt	          d           n*t	          d           t	          d |             d           dS t          dz  }|
                                s#t	          d           t          j        d           dg}	t
          j        dk    rg d}	t          j        |	ddgz   t          dd          j                                        dk    }
|
rddgng }| d	k    rt	          d           t          j        |	dgz   |z   d | gz   t          dd          }|j        dk    r@t	          d!           t          j        |	dgz   |z   d"| gz   t          dd          }d#}d$|  }nGd}d%|  }n?t	          d!           t          j        |	dgz   |z   d"| gz   t          dd          }d#}d$|  }|j        dk    r|j                                        }d&|v sd'|v rt	          d(           nSd)|v sd*|v rt	          d+           n;t	          d,           |r*t	          d-|                                d                     t          j        d           t          j        |	dd.d/|gz   t          dd          }|j        dk    rDt	          d0|  d1|                    d2d          d          d3           t          j        d           |
rt          j        |	dd4gz   t          dd          j                                        }t          j        |	d|gz   t          dd          j                                        }|r|r||k    rt	          d           n4t	          d5| d6           ddl m} t	          d |             d           dS t          j        |	d7d8| d9gz   t          ddd:          }t)          |j                                                  }|dk    rt	          d           dS |dk    rd;nd<}t	          d=| d>| d?| d3           ddl m} t	          d |             d           dS )@a  Implement ``hermes update --check``: fetch and report without installing.

    ``branch`` selects which branch the check compares against. Default is
    "main"; callers can pass another branch to ask "are there new commits
    on origin/<branch>?" without performing the update.

    ``branch_explicit`` is True iff the caller passed --branch on the CLI.
    PyPI installs can't honor non-default branches, so when this is True
    on a PyPI install we surface a one-line notice instead of silently
    dropping the flag.
    r   detect_install_methoddocker)format_docker_update_messagerB   r  r  )check_via_pypir  u?   ⚠ --branch is ignored for PyPI installs (would have checked 'z').Nu.   ✗ Could not reach PyPI to check for updates.u   ✓ Already up to date.u   ⚕ Update available on PyPI.z  Run 'z' to install.r>  u6   ✗ Not a git repository — cannot check for updates.r  r  r  r   windows.appendAtomically=falser  z--is-shallow-repositoryTr  r  z--depthrD   u   → Fetching from upstream...r  r  u   → Fetching from origin...r  Forigin/z	upstream/Could not resolve hostunable to access9   ✗ Network error — cannot reach the remote repository.Authentication failedcould not read UsernameD   ✗ Authentication failed — check your git credentials or SSH key.u   ✗ Failed to fetch.r  r  r     ✗ Branch 'z' not found on r  r  rB  u   ⚕ Update available (behind ).r  zHEAD..r  r  r  r  u   ⚕ Update available: r7  z behind )r  r'	  rQ  r)	  r|   rE   r   r  r  r*	  r,   r   rA  rB  r  r5   rY  r   r8  rf   r   )r  r$	  r'	  methodr)	  r  r*	  r  rE  r  
is_shallow
depth_argsfetch_resultupstream_existscompare_branchr   verify_resulthead_sha
target_sha
rev_resultr  r  s                         r   _cmd_update_checkr@	     sc    877777""<00F
 	CBBBBB**,,---@@@@@@444444 	av//_TZ___```!!>BCCCHQKKKKq[[+,,,,1222G6688GGGHHHV#G>> FGGGgG
|wAAA 	{$=>>		
 	
 	

 	  &07)S!!RJ-...!~wi*,
F/CC	
 
 
 "a''/000%>7)#j0Hf3EE #	  L $O/v//NN"O111NN 	+,,,!~wi*,&/AA	
 
 
  +6++!##$**,,#v--1Cv1M1MMNNNN$..2Kv2U2UXYYYY())) 536,,..q133444 N;
I~FF	  M 1$$VVVVN4H4Ha4P4PQR4SVVVWWW  >{F++T
 
 
  	  ^{N33T
 
 
  	  	I
 	Ix:'='=+,,,,D.DDDEEEDDDDDDG6688GGGHHH:888)DD  J "((**++F{{'(((((#)Q;;xxIWvWWWWnWWWXXX@@@@@@C2244CCCDDDDDrH   c            
      t   t           j        dk    rdS 	 t          j                    dk    rdS n# t          $ r Y dS w xY wt          d          } |                                 s|                                 sdS t          j        	                    d          pd}	 t          j        ddd	| d
t          j        	                    dd           ddddgddd          }n# t          t          j        f$ r Y dS w xY w|j        dk    rdS d}d}d}dD ]}t          |          |z  }|                                s*	 |                    d          }n# t"          $ r Y Nw xY wt%          d |                                D                       }	|	r	 |                    dd          5 }
|
                    d|z   dz   |z   dz              ddd           n# 1 swxY w Y   n,# t"          $ r}t-          d| d|            Y d}~d}~ww xY wt-          d |            d}|rt-          d!           dS dS )"aH  Ensure /usr/local/bin is on PATH for RHEL-family root non-login shells.

    Mirrors the post-symlink probe added to ``scripts/install.sh`` so that
    existing FHS-layout root installs on RHEL/CentOS/Rocky/Alma 8+ get
    repaired on ``hermes update`` without requiring a reinstall.  The
    installer's assumption that ``/usr/local/bin`` is on PATH for every
    standard shell breaks on those distros in non-login interactive shells
    (su, sudo -s, tmux panes, some web terminals): /etc/bashrc doesn't
    add /usr/local/bin and /root/.bash_profile doesn't either.  Symptom:
    ``hermes`` prints ``command not found`` even though the symlink lives
    at /usr/local/bin/hermes.

    Silent no-op on: non-Linux, non-root, non-FHS installs, and any system
    where ``bash -i -c 'command -v hermes'`` already resolves.  Idempotent.
    r  Nr   z/usr/local/bin/hermesHOMEz/rootr,  rP  zHOME=zTERM=rQ  dumbr  r   zcommand -v hermesTr  r=  z"export PATH="/usr/local/bin:$PATH"uK   # Hermes Agent — ensure /usr/local/bin is on PATH (RHEL non-login shells)F)z.bashrcz.bash_profiler3  r  c              3   v   K   | ]4}d |v o+d|v o'|                                                     d           V  5dS )z/usr/local/binr  rc   N)lstriprW   r  s     r   r  z)_ensure_fhs_path_guard.<locals>.<genexpr>!  sj       
 
  $ 2$2KKMM,,S111
 
 
 
 
 
rH   r	  r   r   rc  u     ⚠ Could not update r  u&     ✓ Added /usr/local/bin to PATH in z?    (reload your shell or run 'source ~/.bashrc' to pick it up))rE   r   r&   r   r  r   
is_symlinkr,   r'   r(   rA  rB  r   rC  rY  rC  r   rM   r  r8  r.   rL   r|   )fhs_linkr8   r^  	path_linepath_comment	wrote_anyr   r  r  already_guardedr  r  s               r   _ensure_fhs_path_guardrL	  V!  sM     |w:<<1F     +,,H   ):): 
 :>>&!!,WD8
vv6688#	  
 
 
 z89   14IX  I1  	4jj9${{}} 		}}I}66HH 	 	 	H	  
 
 !++--	
 
 
 
 
  		#00 GA|+d2Y>EFFFG G G G G G G G G G G G G G G 	 	 	6C66166777HHHH	 	<s<<===		 QOPPPPPQ Qsm   . 
<<AC C98C9<E
E E G#)"GG#G	G#G	G##
H-HHc           	      <   t          | dd          rt          d           t                       dS t          t          | dd                    }	 ddlm}  |            }nF# t
          $ r9}t          j        t                    	                    d|           i }Y d}~nd}~ww xY wt          |t                    r|                    d	i           ni }|                    d
d          }|                    dd          }|s|sdS 	 ddlm} n9# t
          $ r,}t          d| d           t                       Y d}~dS d}~ww xY wt          d           t          j                    }		  |t#          |                    }
nG# t
          $ r:}t          d|            t          d           t                       Y d}~dS d}~ww xY wt          j                    |	z
  }|
t          d           t                       dS 	 |
                                j        }n# t(          $ r d}Y nw xY w| d}dD ]}|dk     r n|dz  }|dd| }	 ddlm}m}  |            }	  |             d|
                    |           }n# t2          $ r t5          |
          }Y nw xY wn# t
          $ r t5          |
          }Y nw xY wt          d| d| d|dd           t          d |
            t          d!           t          d"           t                       dS )#u  Create a full zip backup of HERMES_HOME before running the update.

    Gated on ``updates.pre_update_backup`` in config (default false).  Off
    by default because the zip can add minutes to every update on large
    HERMES_HOME directories.  The ``--backup`` flag on ``hermes update``
    opts in for a single run; ``--no-backup`` forces it off when config
    has it enabled.  Never raises — a backup failure should not block the
    update itself.
    	no_backupFu,   ◆ Pre-update backup: skipped (--no-backup)Nr}  r   rw  z/Could not load config for pre-update backup: %supdatespre_update_backupbackup_keepr  )create_pre_update_backupu5   ⚠ Pre-update backup: could not load backup module (z); continuing update.u!   ◆ Creating pre-update backup...)keepu     ⚠ Backup failed: z  Continuing with update.uI     ⚠ Backup skipped (no files found or write failed); continuing update.z B)KBMBGBi   .1fr7  r   rb  r  z  Saved:    rq   r  zs)z  Restore:  hermes import z6  Disable:  omit --backup (backups are off by default)z?            set updates.pre_update_backup: false in config.yaml)r0   r|   rV   r  r  r   logging	getLoggerr&  r^  r2   r3   r(   r  rR	  r{  r  r   rR  rT  rM   r   r   rb  r  r   r4   )rx  force_backupr  r  r   updates_cfgenabledrS	  rR	  t0out_pathrO  
size_bytessize_strunitr   rb  r8   display_paths                      r   _run_pre_update_backuprd	  !  s     t[%(( <===h6677L111111kmm   (##))=s	
 	
 	
 	 -7sD,A,AI#'')R(((rK oo1599G??=!,,D <  	>>>>>>>   ^C^^^	
 	
 	
 	 

-...			B++T;;;   +c++,,,)***	 o"$GYZZZ]]__,

   


    H" . .Ed
 ---t--	%IIIIIIII  	)1133RRh6J6J46P6PRRLL 	) 	) 	)x==LLL	) % % %8}}% 

D
D
D
D
DW
D
D
D
DEEE	
1x
1
1222	
BCCC	
KLLL	GGGGGs   A! !
B$+/BB$D 
E!D??E*F 
G/GGH H,+H,J% ""J J% J!J%  J!!J% %K Kprofile_pathr  c                 .   	 ddl m} ddlm} ddlm} | ||          t          j                    t          j        |j	                  
                                d} |t          |           dz  |dd	           d
S # t          t          f$ r Y dS w xY w)z9Write a planned-stop marker into a specific profile home.r   )r  )_get_process_start_time)atomic_json_write)
target_pidtarget_start_timestopper_pid
written_atz.gateway-planned-stop.jsonN)r  r?  )r  
separatorsTF)r  r  rm  rg	  utilsrh	  r&   r  r  r  r  r   rM   rl  )re	  r  r  rg	  rh	  records         r   !_write_update_planned_stop_markerrp	  "  s    %%%%%%::::::++++++ !8!8!=!=9;;",x|44>>@@	
 
 	!==!		
 	
 	
 	
 t_%   uus   A;A? ?BBrr  c                `   | st                      S ddlm} t          |           }t          j                    t          |d          z   }|rt          j                    |k     rt          |          D ]H}	  ||          s|                    |           $# t          $ r |                    |           Y Ew xY w|rt          j	        d           |rt          j                    |k     t                      }|D ]3}	  ||          r|
                    |           $# t          $ r Y 0w xY w|S )z=Wait for the given gateway PIDs to exit, returning survivors.r   rf          g      ?)r  rm  rg  r{  r  r  r  discardr   r  r   )rr  r?  rg  	remainingr  r  	survivorss          r   %_wait_for_windows_update_gateway_exitrv	  -"  s|     uu******D		I  3w#4#44H
 ))H44	?? 	' 	'C'"{3'' +%%c*** ' ' '!!#&&&&&' 	K  ))H44 %%I  	{3 #c""" 	 	 	D	s$   5 BB87B8= D
D+*D+c                     t           dz  } t                      rdnd}t                      rdnd}| |z  |z  }|                                s;t           dz  t                      f}t	          d |D                       rdd	| d
fS dS d}	 t          j        t          |          d|gdddt                     }n3# t          $ r&}t          
                    d|           Y d}~dS d}~ww xY wd |j        pd                                D             }|j        dk    r=|s;|j        pd                                                                }	d|	r|	d         ndfS |rdd                    |dd                   fS dS )uN  Probe the project venv for the core imports the backend needs to boot.

    Runs a tiny import check inside the venv interpreter (NOT this process —
    ``hermes update`` may be driven by a different Python). Catches the
    half-updated-venv state: git checkout current but a dependency sync that
    failed or was killed partway (e.g. Windows access-denied on a loaded
    .pyd), leaving imports like ``fastapi``'s new transitive deps missing.
    Without this probe, ``hermes update`` on a current checkout prints
    "Already up to date!" and returns without ever re-syncing dependencies —
    the user's install stays broken no matter how many times they update
    (ryanc's incident, July 2026).

    Returns ``(healthy, detail)``. Never raises; unknown states report
    healthy so a probe failure can't force needless reinstalls.
    r  r  r  rW  r  z.hermes-bootstrap-completec              3   >   K   | ]}|                                 V  d S r  rb  )r  r7  s     r   r  z-_venv_core_imports_healthy.<locals>.<genexpr>m"  s*      33aqxxzz333333rH   Fzvenv python missing (rr   )TrR   zimport importlib
mods = ['fastapi', 'uvicorn', 'pydantic', 'openai', 'yaml']
missing = []
for m in mods:
    try: importlib.import_module(m)
    except Exception as e: missing.append(f'{m}: {e}')
print('\n'.join(missing))
r   Trs  )r>  r9  r?  r  z#venv health probe failed to run: %sNc                 ^    g | ]*}|                                 |                                 +S rN   r  r  s     r   r  z._venv_core_imports_healthy.<locals>.<listcomp>"  s-    [[[djjll[tzz||[[[rH   rR   r   zvenv python failed to runz; r  )rQ  r8  r,   r(  r  rA  rB  r4   r   r]  r^  r  r8  rY  r   r5   r*   )
rX  python_namebin_dirr?  managed_markersr.  r  r   r  details
             r   _venv_core_imports_healthyr~	  L"  s     f$H"---=,,XK&==3iieGW${2K  77!!
 33?33333 	A@+@@@@@x	' 

tU+
 
 
    :C@@@xxxxx \[)<"(H(H(J(J[[[GAg-%2,,..99;;6JfQii/JJJ -dii,,,,8s   	.B8 8
C(C##C(c                 D	   t                      sg S 	 ddl}n# t          $ r g cY S w xY wt          dz  }	 t	          |                                                                                              t          j	                  t          j	        z   }n[# t          $ rN t	          |                                                              t          j	                  t          j	        z   }Y nw xY w	 t	          t                                                                                                        t          j	                  t          j	        z   }n`# t          $ rS t	          t                                                                        t          j	                  t          j	        z   }Y nw xY wt          | pt                                }|                    t          j                               	 |                                                                D ])}|                    t!          |j                             *n# t          $ r Y nw xY wg }	 |                    g d          }n# t          $ r g cY S w xY w|D ]}		 |	j        }
n# t          $ r Y w xY w|
                    d          }|
                    d          }|r|t!          |          |v r\	 t	          t+          |                                                                                    }n8# t          t,          f$ r$ t	          |                                          }Y nw xY wd                    |
                    d          pg           }|                                }t	          |
                    d	          pd
                                                              t          j	                  t          j	        z   }|                    |          }|s||v rd}|sd|v r||v s|                    |          rd}|s|
                    d          pt+          |          j        }|                    t!          |          t	          |          |dd         f           |S )u  Find live processes running from the project venv's interpreter.

    The hermes.exe shim guard misses the biggest lock-holder class on
    Windows: the Desktop app's backend (``python.exe -m hermes_cli.main
    serve``) and anything else running straight off ``venv\Scripts\python
    (w).exe``. Those processes keep native ``.pyd`` extensions mapped, so a
    dependency sync mid-update dies with access-denied and strands the venv
    half-updated (ryanc's brotlicffi/_sodium.pyd incidents, July 2026).

    Killing them from here is pointless — the Desktop app supervises its
    backend and respawns it within seconds — so the caller should refuse and
    tell the user to close the app instead. Returns ``(pid, name, cmdline)``
    tuples; empty off-Windows / without psutil / when nothing matches. The
    calling process and its ancestors are always excluded (a CLI ``hermes
    update`` itself runs from the venv python). Never raises.
    r   Nr  )r  r  r   cmdliner  r  r  r7  r	  r  rR   Thermes_cli.mainr   x   )r8  r  r   rQ  r4   rD  r6   r  r&   r  rM   r  r   r  r;  ra  r   r  r  r  r(   r   r   r*   rW   r   r  )r,  r  rX  venv_prefixroot_prefixr  ancrj  r  r$  r  r  r  rk  cmdline_rawcmdline_lowcwd_low	is_holderr   s                      r   _detect_venv_python_processesr	  "  s   & == 	   			 f$HD(**,,--3355<<RVDDrvM D D D(mm))++2226::RVCDH,..00117799@@HH26Q H H H,''--//66rv>>GH .//DHHRY[[>>##++-- 	# 	#CHHS\\""""	#    +-G''(P(P(PQQ		   			 !A !A	9DD 	 	 	H	hhuoohhuoo 	ckSXX%5%5	(499,,..//5577HH$ 	( 	( 	(3xx~~''HHH	(hhtxx	228b99!''))dhhuoo+,,2244;;BFCCbfL ''44	  	[K77I 	!.+==k))W-?-?-L-L) 	 	xx1499>C#d))[#->?@@@@Ns}    &&AB AC*)C*.A"E AF.-F.5AI 
III1 1J ?J 	J
JJ"A L##2MMc           
      V   dg}| dd         D ]M\  }}}d}|                                 }d|v sd|v rd}nd|v rd	}|                    d
| d| d| |            Nt          |           dk    r)|                    dt          |           dz
   d           |                    d           |                    d           |                    d           |                    d           |                    d           |                    d           d                    |          S )zDExplain which venv processes block the update and how to clear them.u@   ✗ Other Hermes processes are running from this install's venv:Nr  rR   r  r  u4     ← Hermes Desktop backend (close the desktop app)gatewayu     ← gatewayz  PID r  z
  ... and  morezD  On Windows these keep native extension files (.pyd) locked, so thezB  dependency update would fail partway and leave a broken install.zE  Close the Hermes desktop app / other Hermes terminals, then re-run:z    hermes updatezJ  (or use `hermes update --force-venv` to proceed anyway at your own risk)rc  )r6   r  r   r*   )rj  rz  r  r   r	  hintr3  s          r   #_format_venv_python_holders_messager	  "  ss    	KE &bqbk > >T7mmooc>>[C//IDD#"D<c<<T<<W<d<<====
7||a9#g,,"2999:::	LL	LLN   
LLL   
LLO   
LL$%%%	LL]^^^99UrH   c                  F   t                      sdS 	 ddlm}  ddlm}m}m}m} n3# t          $ r&}t          
                    d|           Y d}~dS d}~ww xY w	 t          t                               |d                              }n3# t          $ r&}t          
                    d|           Y d}~dS d}~ww xY w|sX	 dd	lm} |                                rdi g g dd
S n2# t          $ r%}t          
                    d|           Y d}~nd}~ww xY wdS i 	 d  |            D             n2# t          $ r%}t          
                    d|           Y d}~nd}~ww xY wi }g }	|D ]}
                    |
          }|t%          |
          |t'          |j                  <   |	                    t%          |
                     t-          t/          |j                  t%          |
                     t3          d           	 t5          t7           |                      d          }n# t          $ r d}Y nw xY wt9          |	|          }fd|D             }g }|D ]v}
d}	  |t%          |
                    }n3# t          $ r&}t          
                    d|
|           Y d}~nd}~ww xY w|                    t%          |
          |d           wg }t;          t=          |                              |                    D ]\}
	  | t%          |
          d           |                    t%          |
                     @# t@          tB          tD          f$ r Y Yw xY w|r2t3          dd#                    t;          |                                |r t3          dtI          |           d           |r[tK          d |D                       }t3          dtI          |           d           |tI          |          k     rt3          d           d|||dS )a  Stop running Windows gateways before mutating the checkout or venv.

    Windows scheduled/startup gateways run through pythonw.exe, so the generic
    hermes.exe concurrent-instance guard does not see them. They still import
    from the checkout and can keep files locked while ``git`` or ``uv`` updates
    the install. Stop only PIDs that the gateway discovery code identifies.
    Nr   terminate_pid)_capture_gateway_argv_get_restart_drain_timeoutfind_gateway_pidsfind_profile_gateway_processesz6Could not prepare Windows gateway pause for update: %sTall_profilesz9Could not discover Windows gateway PIDs before update: %sgateway_windows)resume_neededr   unmapped_pidsunmappedcold_start_if_installedzACould not check Windows gateway autostart state before update: %sc                     i | ]
}|j         |S rN   r  )r  r$  s     r   r  z6_pause_windows_gateways_for_update.<locals>.<dictcomp>A#  s)     
 
 
#DHd
 
 
rH   z2Could not map Windows gateway PIDs to profiles: %suB   → Stopping Windows gateway process(es) before updating Hermes...      ?      $@r(  c                     g | ]}|v|	S rN   rN   )r  r  profile_processess     r   r  z6_pause_windows_gateways_for_update.<locals>.<listcomp>Z#  s$    QQQSC?P4P4PS4P4P4PrH   z2Could not capture argv for unmapped gateway %s: %s)r  r@   r  u!     ✓ Paused gateway profile(s): r  u     → Force-stopped z gateway process(es)c              3   D   K   | ]}|                     d           dV  dS )r@   rB   Nr6  r  us     r   r  z5_pause_windows_gateways_for_update.<locals>.<genexpr>y#  s1      ??v?!??????rH        → Stopped z, gateway process(es) without profile mappingz5    Restart manually after update: hermes gateway run)r	  r   r	  r	  )&r8  rm  r	  r  r	  r	  r	  r	  r   r]  r^  r  r3   r>  rw   r	  is_installedr(   r   r4   profiler  rp	  r   r)   r|   r  floatrv	  r  r  unionrk  rl  rM   r*   r   sum)r	  r	  r	  r	  r	  r   running_pidsr	  r   mapped_pidsr  r$  drain_timeoutru	  r	  r	  r@   force_killedrespawnabler	  s                      @r   "_pause_windows_gateways_for_updater	  #  s    == t
000000	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
    MsSSStttttDMM*;*;*N*N*NOOPP   PRUVVVttttt  	222222++-- %) "%' "/3    	 	 	LLS       	
 tP
 
'E'E'G'G
 
 
  P P PI3OOOOOOOOP  "HK E E $$S))<&)#hhT\""#3s88$$$)$ty//3s88DDDD	
NOOOE"<"<">">??EE   5  I RQQQLQQQM H 9 9	Y((S22DD 	Y 	Y 	YLLMsTWXXXXXXXX	YC$778888Lc)nn**=99::  	M#c(($////C))))"OW= 	 	 	D	  QO$))F8<L<L2M2MOOPPP NLS%6%6LLLMMM K??X?????]S//]]]	
 	
 	
 ]++++ IJJJ &	  s   & 
AAA1B 
B<B77B<!C% %
D/DDD1 1
E ;EE 	%H/ /H>=H>)J
J2J--J2<MM&%M&c                  $   t                      sdS 	 ddlm}  ddlm} n3# t
          $ r&}t                              d|           Y d}~dS d}~ww xY w	 t           |d                    rdS n3# t
          $ r&}t                              d|           Y d}~dS d}~ww xY w	 | 	                                }n3# t
          $ r&}t                              d	|           Y d}~dS d}~ww xY w|r#t                       t          d
| d           dS dS )u  Start a fresh detached gateway after update when one is installed but down.

    Invoked from ``_resume_windows_gateways_after_update`` for the
    ``cold_start_if_installed`` case: no gateway was running when the update
    began, but an autostart entry (Scheduled Task / Startup-folder login item)
    is installed, signalling the user wants a gateway. Unlike the relaunch
    paths — which watch an old PID and respawn once it exits — this is a direct
    fresh spawn via the same windowless ``pythonw`` + breakaway path that
    ``hermes gateway start`` uses (``gateway_windows._spawn_detached``).

    Best-effort and idempotent: re-checks that nothing is running first so a
    concurrent start (e.g. the autostart entry firing) can't produce a
    duplicate gateway.
    Nr   r	  )r	  z5Could not load Windows gateway cold-start helpers: %sTr	  z9Could not re-check gateway liveness before cold-start: %sz5Could not cold-start Windows gateway after update: %su1     ✓ Starting Windows gateway after update (PID rr   )r8  rw   r	  r  r	  r   r]  r^  r  _spawn_detachedr|   )r	  r	  r   r  s       r   (_cold_start_windows_gateway_after_updater	  #  s    == ......8888888   LcRRR!!t44455 	F	   PRUVVV--//   LcRRR  JH#HHHIIIIIJ JsD    
AA

AA/ /
B9BB#B8 8
C(C##C(re  c                    | r|                      d          sdS d| d<   t                      sdS |                      d          pi }|                      d          pg }t          |                      d                    }|s+t          d |D                       s|rt	                       dS 	 dd	lm}m} n3# t          $ r&}t          
                    d
|           Y d}~dS d}~ww xY wg }t          |                                          D ]\  }}		  |t          |          t          |	                    r"|                    t          |                     O# t          $ r&}t          
                    d||           Y d}~zd}~ww xY wd}
|D ]}|                     d          }|                     d          }	|r|	s1	  |t          |	          t!          |                    r|
dz  }
^# t          $ r&}t          
                    d|	|           Y d}~d}~ww xY w|r3t#                       t#          dd                    |                      |
r%|st#                       t#          d|
 d           dS dS )z>Restart Windows profile gateways previously paused for update.r	  NFr   r	  r	  c              3   @   K   | ]}|                     d           V  dS )r@   Nr6  r	  s     r   r  z8_resume_windows_gateways_after_update.<locals>.<genexpr>#  s,      @@!f@@@@@@rH   r   )*launch_detached_gateway_restart_by_cmdline'launch_detached_profile_gateway_restartz1Could not load Windows gateway restart helper: %sz=Could not restart Windows gateway profile %s after update: %sr@   r  rB   zDCould not restart unmapped Windows gateway (pid %s) after update: %su-     ✓ Restarting Windows gateway profile(s): r  u     ✓ Restarting z% unmapped Windows gateway process(es))r(   r8  rV   r  r	  r  r	  r	  r   r]  r^  r  r  r4   r   r  r  r|   r*   )re  r   r	  
cold_startr	  r	  r   
relaunchedr	  old_pidunmapped_relaunchedr  r@   s                r   %_resume_windows_gateways_after_updater	  #  sU    		/22 "E/== yy$$*Hyy$$*Heii 9::;;J C@@x@@@@@  	74666	
 	
 	
 	
 	
 	
 	
 	
 	
    H#NNN J"8>>#3#344 	 		66s7||S\\RR 0!!#g,,/// 	 	 	LLO       	   yy  ))E"" 	7 		99#g,,T

SS )#q(# 	 	 	LLV       	  WUdii
>S>SUUVVV 
 	GGGZ 3ZZZ	
 	
 	
 	
 	

 
sI   -B6 6
C& C!!C&AE
F%FF+G00
H :HH c                    	 t          j        | ddgz   |dd          }|j        dk    rdS d |j                                        D             fd|j                                        D             }|sdS t          j        | d	d
g|z   |ddd           t          dt          |           d           dS # t          $ r Y dS w xY w)av  Restore tracked ``package-lock.json`` files that npm dirtied locally.

    npm rewrites lockfiles non-deterministically at install/build time. On a
    managed install those diffs are never intentional, so we discard them so
    ``hermes update`` sees a clean tree instead of autostashing every run.
    Best-effort; only ever touches files named ``package-lock.json``.
    r  r  Tr  r   Nc                     h | ]O}|                                                     d           )t          |                                           j        PS )r  r5   r  r   r   r  s     r   rc  z*_discard_lockfile_churn.<locals>.<setcomp>
$  sW     
 
 
zz||$$^44
%
 
 
rH   c                     g | ]e}|                                                     d           r<t          |                                           j        vQ|                                 fS )r  r	  )r  rj   dirty_package_dirss     r   r  z+_discard_lockfile_churn.<locals>.<listcomp>$  sn     
 
 
zz||$$%899
 TZZ\\"")1CCC JJLL DCCrH   checkoutr   Fr  u"   → Discarded npm lockfile churn (z	 file(s)))rA  rB  rY  r  r8  r|   r   r   )r  r<  r  dirtyr	  s       @r   _discard_lockfile_churnr	  #  s;    ~v}--	
 
 
 ?aF
 
..00
 
 


 
 
 
..00
 
 
  	Fz40%00	
 	
 	
 	
 	H3u::HHHIIIII   s   (B; A
B; 9A B; ;
C	C	c           
         ddl m}m}m}m}  |            r |d           dS  |t
                    dk    r+t           |                       t          j        d           t          | dd          r>t          |           }t          |t          t          | d	d                    
           dS t          | dd          }t          |          }	 t          | |           t          |           dS # t          |           w xY w)zUpdate Hermes Agent to the latest version.

    Thin wrapper around ``_cmd_update_impl``: installs hangup protection,
    runs the update, then restores stdio on the way out (even on
    ``sys.exit`` or unhandled exceptions).
    r   )r'	  r)	  
is_managedmanaged_errorzupdate Hermes AgentNr(	  rB   r.  Fr  )r  r$	  r	  )r	  )r  r'	  r)	  r	  r	  rQ  r|   rE   r   r0   r  r@	  rV   r	  _cmd_update_implr"	  )rx  r'	  r)	  r	  r	  r  r	  _update_io_states           r   
cmd_updater	  $$  sc               z|| +,,, \**h66**,,---tWe$$  (-- x!>!>??	
 	
 	
 	
 	4E22L
 2|LLL2L9999 011111 01111s   C6 6Dc                    ddl m} ddlm} t	          d|            t	          d           ddlm}m}  |              |            }t          j	        t          j
        k    }dt          j	                            t          j                  v }|rt          j        d          nd}d	}	 |            r;|s2t	          d
           t	          d           t          j        d           |dddg}
nA|r|r|ddg}
n7|r#|ddddg}
|rd}	n)|
                    dd           nt          j        dddddg}
t	          dd                    |
                      i }|	ri t          j        dt          j	        i|d<   t+          j        |
fi |}|j        dk    r#t	          d           t          j        d           t	          d           dS )z*Update Hermes via pip (for PyPI installs).r   )ra   )is_uv_tool_installu   → Current version: u    → Checking PyPI for updates...r  pipxNFu=   ✗ Detected a uv-tool install but managed uv install failed.zN  Install uv manually: https://docs.astral.sh/uv/getting-started/installation/rB   toolupgrader[  r  r  r  Tr  z--systemr   u   → Running: r7  r  r,  u   ✗ Update failedu;   ✓ Update complete! Restart hermes to use the new version.)rw   ra   r  r	  r|   r  r  r  rE   rY   base_prefixrf   r&   r  rW  rX  r   r  ra  r*   r'   rA  rB  rY  )rx  ra   r	  r  r  r  in_venvpipx_managedr	  export_virtualenvr9  
run_kwargsr  s                r   _cmd_update_pipr	  V$  s<   &&&&&&444444	
/+
/
/000	
,---BBBBBBBB 	BjCO+GSZ--bf555L#/96<TD  T 	QRRRbcccHQKKK69n5	 T$ T Y/	 T5)[.A 	& !% JJq*%%%%~tUI{NS	
)#((3--
)
)***J FErzE=#*EE
5^C..:..FA!"""	
GHHHHHrH   c                 O   |rd]dnd}t          t          | dd                    }|p>|p<t          j                                        ot          j                                         }d}|r	 ddlm}  |            pi                     di           }t          |t                    r;t          |                    d	d
                                                    }|dk    }n4# t          $ r'}	t                              d|	           d}Y d}	~	nd}	~	ww xY wt!          d           t!                       t#                      rct          | dd          sRt%                      }
|
Bt'          |
          }|r1t!          t)          ||
                     t          j        d           t-          |            t/                      }|rddl}|                    t4          |           t#                      r`t          | dd          sOt7                      }|r?t!          t9          |                     t5          |           t          j        d           d}t:          dz  }|                                srt          j        dk    rd}n_ddlm }  |t:                    }|dk    rtC          |            dS t!          d           t!          d           t          j        d           t          j        dk    r3|                                rtE          j#        g dt:          dd           dg}t          j        dk    rg d}tI          |t:                     tK          |t:                    }tM          |          }|r/t!          d           t!          d|            t!                       |r6	 tO          |            t5          |           n# t5          |           w xY wdS 	 tQ          |           }t!          d           tE          j#        |d d!|gz   t:          dd"          }|j)        dk    r|j*        +                                }d#|v sd$|v r>t!          d%           t!          |rd|,                                d          nd           nSd&|v sd'|v rt!          d(           n;t!          d)           |r*t!          d|,                                d                     t          j        d           tE          j#        |g d*z   t:          ddd+          }|j        +                                }||k    r5|d,k    rd-nd.| d/}t!          d0| d1| d2           t[          |t:                    }tE          j#        |d3|gz   t:          dd"          }|j)        dk    rtE          j#        |d3d4|d5| gz   t:          dd"          }|j)        dk    r|t]          |t:          |d|6           t!          d7| d8           |j*        +                                rAt!          d|j*        +                                ,                                d                     t          j        d           nt[          |t:                    }|duo@| o=|p;t          j                                        ot          j                                        }tE          j#        |d9d:| d;gz   t:          ddd+          }t_          |j        +                                          } | dk    rta                       |r|d<k    rtc          |t:                     |t]          |t:          |||6           ||d,hvr#tE          j#        |d3|gz   t:          ddd+           te                      \  }!}"|!srt!          d=           t!          d|"            t!          d>           tg                       dd?l4m5}#  |#            }$t:          d@z  t#                      rdAndBz  t#                      rdCndDz                                   }%|%r0|$r.t!          dE           tE          j#        |$d@d@gt:          dF           |$r<i tl          j7        dGt          t:          d@z            i}&tq          |$dg|&dHI           ntq          t          j9        dJdgdHK           tu                       te                      \  }'}(|'rt!          dL           n1t!          dM|(            t!          dN           nt!          dO           t5          |           dS t!          dP|  dQ           d})	 ddRl;m<}*  |*dSdT          })|)rt!          dU|)            n2# t          $ r%}	t                              dV|	           Y d}	~	nd}	~	ww xY wt!          dW           d}+t{          |t:                    },	 tE          j#        |dXdYd!|gz   t:          dd"          }-|-j)        dk    rt!          dZ           tE          j#        |d[d\d5| gz   t:          dd"          }.|.j)        dk    r{t!          d]| d^           |.j*        +                                r)t!          d|.j*        +                                            t!          d_|            t          j        d           t}          t:                    \  }/}0}1|/st!                       t!          d`           t!          d|0            |1r>t          |1          ,                                dda         D ]}2t!          db|2            |,rt!                       t!          dc|,ddd          de           tE          j#        |d[d\|,gz   t:          dd"          }3|3j)        dk    rt!          df           t!          dg           nt!          dh           t!          dit:           dj|,            |3j*        +                                rBt!          dk|3j*        +                                ,                                d          dl           n5t!                       t!          dm           t!          dit:           dn           t          j        d           d}+|W|+s#t!          do| dl           t!          dp           n|rt          |t:          |           nyt]          |t:          |||6           n_# |X|+s#t!          do| dl           t!          dp           w |rt          |t:          |           w t]          |t:          |||6           w w xY wta                       t          t:                    }4|4rt!          dq|4 dr|4dk    rdsndt            |r|d<k    rtc          |t:                     tg                       t!          du           ddvl4m5}#mA}5  |5              |#            }6t          j9        dJdg}7|6st          |7          }6dH}8|6ri tl          j7        dGt          t:          d@z            i}9t          |9          r=|9D                    dwd           |9D                    dxd           dy}8t!          dz           t          |9          r0t                      r"t!          d{           t          |6dg|9|           tq          |6dg|9|8I           nt          j9        dJdg}7	 tE          j#        |7d}gz   t:          dd           n@# tD          jG        $ r. tE          j#        t          j9        dJd~ddgt:          dF           Y nw xY wt                      rdy}8t!          d           t                      r,t                      rt!          d{           t          |7           tq          |7|8K           tu                       t                       t                       t          t:          dz             t:          dz  dz  }:t          |:          dupt          |:          };ddlMmN}< |:dz                                  r |<d          r|;rt!          d           t          j9        dJdddg}=t          |=t:                    }>|>j)        dk    rt          |=t:                    }>|>j)        dk    rt!          d           dP                    |>j        pd+                                ,                                dd                   }?|?rt!          |?           ddlMmQ}@ t!          d |@             d           nt!          d           t!                       t!          d           	 ddlRmS}A  |At:                    rt!          d           n2# t          $ r%}Bt                              d|B           Y d}B~Bnd}B~Bww xY w	 ddlT}CddlM}D|CU                    |D           n# t          $ r Y nw xY w	 ddlVmW}E t!                       t!          d            |Ed          }|d         rAt!          dt          |d                    ddP                    |d                               |                    d          rAt!          dt          |d                    ddP                    |d                               |                    d          r5t!          dt          |d                    d           t!          d           |                    d          r&t!          dt          |d                    d           |d         s$|                    d          st!          d           n2# t          $ r%}Bt                              d|B           Y d}B~Bnd}B~Bww xY w	 ddlYmZ}Fm[}G  |F            }H|Hrxt!                       t!          d           |HD ]W}I	  |G|Ij\        d          }J|Jr|J                    d          rd}Kn|Jrt          |J                    dg                     }Lt          |J                    dg                     }Mt          |J                    dg                     }Ng }O|Lr|O]                    d|L d           |Mr|O]                    d|M d           |Nr|O]                    d|N d           |OrdP                    |O          nd}Knd}Kt!          d|Ij^         d|K            &# t          $ r&}Pt!          d|Ij^         d|P dl           Y d}P~PQd}P~Pww xY wn# t          $ r Y nw xY w	 ddlYm_}Q  |Qd          }R|RrCt!                       t!          dt          |R           ddP                    |R                      n# t          $ r Y nw xY w	 ddl`ma}S  |S            }T|Trt!          d|T d           n# t          $ r Y nw xY wt!                       t!          d           ddlmb}Umc}Vmd}Wme}X  |Ud          }Y |V            }Z |W            \  }[}\t          |Yp|Z          }]|] o|[|\k     }^|]p|[|\k     }_|^r|t!                       t!          d|[ d|\ dÝ           	  |XddĦ           t!          dŦ           n;# t          $ r,}`t!          d|`            t!          dǦ           Y d}`~`n
d}`~`ww xY w|_rt!                       d^dȄ}a|Yr-t!          dt          |Y           dʝ            |a|Ydd̦           |Zr-t!          dt          |Z           dΝ            |a|ZddЦ           t!                       |rt!          dѦ           ds}bn|r5t          ddӦ          +                                                                }bnt          j                                        rt          j                                        st!          dԦ           d}bnG	 t          d֦          +                                                                }bn# t          $ r d}bY nw xY w|bdv rot!                       |p|p|bdk     }c |X|cdĦ          }d|dd         s|dd         rt!                       t!          dڦ           |s|s|bdk    r|Yrt!          dۦ           n-t!                       t!          dܦ           nt!          dݦ           	 ddl;mi}e  |e|)          }f|fr0t!                       t!          d|fd          d|fd          d^           n2# t          $ r%}	t                              d|	           Y d}	~	nd}	~	ww xY wt!                       t!          d           	 t                       n2# t          $ r%}Bt                              d|B           Y d}B~Bnd}B~Bww xY w	 t                       n2# t          $ r%}Bt                              d|B           Y d}B~Bnd}B~Bww xY w	 t                       n2# t          $ r%}Bt                              d|B           Y d}B~Bnd}B~Bww xY w	 d}g	 ddlm}  |            pi                     di           }t          |t                    r#t          |                    dd                    }gn2# t          $ r%}ht                              d|h           Y d}h~hnd}h~hww xY w|grQt          j        dv rCt          jn        d          r/ddlomp}i t!                       t!          d            |id           n2# t          $ r%}Bt                              d|B           Y d}B~Bnd}B~Bww xY w|r8t                      dz  }j	 |jr                    d           n# t          $ r Y nw xY w	 ddltmu}kmv}lmw}mmx}nmy}omz}pm{}qm|}rm}}s ddl~}t	 d_dt          dt          dt           dt           fd}u	 d`dt          dt          dt           dt           fd}vi dt          dt          dt          ffd}w	 ddlMm}x n# t          $ r d}xY nw xY wd}y	 ddlm}  |                                d           pi }z|z                    d          }yn# t          $ r Y nw xY w	 |yt          |y          nt          |x          }{n)# t          t          f$ r t          |x          }{Y nw xY wt	          |{d          dz   }{g }|t                      g }} |l            r	  |m             n# t          $ r Y nw xY wdddgfddgffD ]\  }~}	 tE          j#        |g dz   dddd	          }|j        +                                ,                                D ]}2|2                                }O|Os|Od         }|                    d
          s9|                    d
          }tE          j#        |d|gz   ddd	          }|j        +                                dk    r |w|~||          }d}	 tE          j#        |d|ddgz   ddd	          }t_          |j        pd+                                pd          }n&# t          tD          j        t          f$ r d}Y nw xY wd}|dk    r4t!          d| dt_          |{           d            |r||{          }|r|etE          j#        |d|gz   dddd	           tE          j#        |d|gz   ddd	            |u||d          r||]                    |            |v||d          }t	          d|dz             }|-|dk    r&t!          d| dt_          |           d            |u|||          r||]                    |           0t!          d| d           |t!          d| d| d            btE          j#        |d|gz   dddd	           tE          j#        |d!|gz   ddd	          }|j)        dk    r |u||d          r||]                    |           t!          d| d"           tE          j#        |d|gz   dddd	           tE          j#        |d!|gz   ddd	            |u||d          r,||]                    |           t!          d#| d$           b|~dk    rd%nd}|~dk    rd&nd}t!          d'| d(| d)| d*| d+| d,| d-| d.| d,| d/|            t!          d0| d|j*        +                                            5# t          $ r Y CtD          j        $ r3}	t!          d1|~ d2|	j        r|	j        nd3 d4           Y d}	~	d}	~	ww xY w |k            r	 dd5ltm}m}m}  |            }|                                rtE          j#        d6d7 |            gddd	          }|j)        dk    r~	  |             ||]                     |                       nU# tD          jG        $ rC}Bt          |Bd8d          pd+                                }t!          d9|            Y d}B~Bnd}B~Bww xY wn$# t          tD          j        t          f$ r Y nw xY w |q            } |n|d:          fd; |o|<          D             }|                                D ]\  }} |p|j        |          st!          d|j         d=| d>t_          |{           d            |r||{          }|s5	 tm          j        ||tj                   n# t(          t*          f$ r Y nw xY w |sdd?                               |           |}]                    |j                   ʉD ]P}||v r	 tm          j        ||tj                                       |           8# t(          t*          f$ r Y Mw xY w||srt!                       ||D ]}t!          d@|            |}r(dP                    |}          }t!          dA|            t                    t          |}          z
  }|r;t!          dB| dC           t!          dD           |dk    rt!          dE           ||ss	 	 t/          j        dF            |q            } |n|d:          }fdG|D             }|rt!                       t!          dt          |           dH           ddIlm} |D ]0}	  ||dJ           # t(          t*          t          f$ r Y -w xY wt/          j        dK           n3# t          $ r&}t                              dL|           Y d}~nd}~ww xY wn3# t          $ r&}Bt                              dM|B           Y d}B~Bnd}B~Bww xY wt5          |           	 ddNltm}m}mv}l  |l            r |            rt!                       t!          dO            |            D ]&\  }}}|rdnd}~t!          db| dP|~ dQ           't!                       t!          dR           t!          dS           t!          dT           t!                       t!          dU           t!                       t!          dV           n3# t          $ r&}Bt                              dW|B           Y d}B~Bnd}B~Bww xY wt;                       t!                       t!          dX           t!          dY           dS # tD          jG        $ r}Bt          j        dk    rAt!          dZ|B            t!          d[           t!                       tO          |            n-t!          d\|B            t          j        d           Y d}B~BdS Y d}B~BdS d}B~Bww xY w(a  uj   Body of ``cmd_update`` — kept separate so the wrapper can always
    restore stdio even on ``sys.exit``.rR   c                 "    t          | |          S r  )r  )rH  r   s     r   rj  z"_cmd_update_impl.<locals>.<lambda>$  s    OFG$D$D rH   Nr  Fr   rw  rO	  non_interactive_local_changesr  rs	  z8Could not read updates.non_interactive_local_changes: %su   ⚕ Updating Hermes Agent...r  r   
force_venvr>  r  Tr&	  r  u+   ✗ Not a git repository. Please reinstall:zD  curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bashrB   )r  r   r,	  r`  zwindows.appendAtomicallyr  r  r  r+	  u   ⚠ Updating from fork:r  u   → Fetching updates...r  r  r  r.	  r/	  r0	  r1	  r2	  r3	  u(   ✗ Failed to fetch updates from origin.)r  z--abbrev-refrB  r  rB  zdetached HEADzbranch 'r  u     ⚠ Currently on u    — switching to z for update...r	  z-Br-	  )r  r  r4	  z&' does not exist locally or on origin.r  zHEAD..origin/r  r  u3   ⚠ Checkout is current, but the venv is unhealthy:u$   → Repairing Python dependencies...r/  r  rW  r  r  r  u%   → Recreating virtual environment...r  r  r  r1  r   r2  u   ✓ Dependencies repaired!u'   ⚠ Venv still unhealthy after repair: z=  Close all Hermes windows/gateways and re-run: hermes updateu   ✓ Already up to date!u
   → Found z new commit(s))create_quick_snapshotz
pre-update)r,  rS	  u     ✓ Pre-update snapshot: zPre-update snapshot failed: %su   → Pulling updates...r  r  uP     ⚠ Fast-forward not possible (history diverged), resetting to match remote...r  r  u   ✗ Failed to reset to origin/r  z<  Try manually: git fetch origin && git reset --hard origin/u6   ✗ Pulled code has a syntax error in a critical file:r  r  u   → Rolling back to r  r#  u6     ✓ Rollback complete — your install is unchanged.z5  Try ``hermes update`` again later once a fix lands.u-     ✗ Rollback failed. Recover manually with:r3  z && git reset --hard z    (rr   u;     Could not capture pre-pull SHA — recover manually with:z- && git reflog && git reset --hard <prev-sha>u1     ℹ️  Local changes preserved in stash (ref: z(  Restore manually with: git stash applyr  r  r  r  r  r  r  r  r0  uH     → Termux detected: using uv + curated termux-all optional profile...uY     → Termux/Android detected: prebuilding psutil with Linux source path compatibility...r2  r\   r  r  r  uC     → Termux detected: using curated termux-all optional profile...rV  r  r  )r  r  r  u/   → Checking if desktop app needs rebuilding...r	  z--build-only)r  uE     ⚠ Desktop build failed (non-fatal; run `hermes desktop` to retry)rc  ira  z  Full build log: z/logs/update.logu     ✓ Desktop app up to dateu   ✓ Code updated!r  r  z+Model catalog seed during update failed: %srh  r  rj  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  z$Skills sync during update failed: %s)list_profilesseed_profile_skillsu-   → Syncing bundled skills to all profiles...skipped_opt_outzopted out (--no-skills)+z newu   ↑z updatedr   z user-modifiedz
up to datezsync failedr  z	: error ()backfill_profile_envsu   → Seeded .env for z# profile(s) (copied from default): )sync_honcho_profiles_quietz
-> Honcho: synced z profile(s)u-   → Checking configuration for new options...)get_missing_env_varsget_missing_config_fieldscheck_config_versionmigrate_config)required_onlyu     ℹ Updating config format (vu    → vu   )…)interactiverk  u:     ✓ Config format updated (no new settings to configure)u'     ⚠️  Config format update failed: z*     Run 'hermes config migrate' to retry.c                    | sd S t          d| d           | d d         }|D ]}t          |t                    rX|                    |          p|r|                    |          pd}|                    d          pd                                }nt          |          }d}|rt          d| d|            t          d|            t          |           t          |          z
  }|d	k    rt          d
| d           d S d S )Nr  r?  r  rr  r*  rR   u
         • r.  r   u         … and r	  )r|   r2   r3   r(   r5   r4   r   )	r  r,  rF  fallback_keyshownitr   r  r  s	            r   _print_itemsz&_cmd_update_impl.<locals>._print_items}'  s>    Fm5mmm$$$bqb	 3 3B!"d++ "!vvc{{\|/T|@T@T\Y\ "} 5 5 ;BBDD  #2ww! 3<4<<d<<====14112222E

SZZ/1997577788888 9rH   u
     ⚠️  z+ new required setting(s) need configurationzNew settingsr   u
     ℹ️  z new config option(s) availablezNew optionsrF  uG     ℹ --yes: auto-applying config migration (skipping API-key prompts).z2Would you like to configure new options now? [Y/n]r  uB     ℹ Non-interactive session — applying safe config migrations.r  z-Would you like to configure them now? [Y/n]: >   rR   r  r  r  	env_addedconfig_addedu   ✓ Configuration updated!u:     ℹ API keys require manual entry: hermes config migratez8Skipped. Run 'hermes config migrate' later to configure.u!     ✓ Configuration is up to date)restore_cron_jobs_if_emptieduC     ⚠️  cron/jobs.json lost jobs during this update — restored 	job_countz! job(s) from pre-update snapshot snapshot_idz'Cron jobs auto-restore check failed: %sr  r  r  zFHS PATH guard check failed: %srefresh_cua_driverz-Could not read updates.refresh_cua_driver: %s)r  r  r  z
cua-driverinstall_cua_driveru+   → Refreshing cua-driver (Computer Use)...r	  zcua-driver refresh failed: %sz.update_exit_coder  )	is_macossupports_systemd_services_ensure_user_systemd_envr	  r	  r	  _get_service_pids_graceful_restart_via_sigusr1_wait_for_gateway_exitr	  
scope_cmd_	svc_name_r?  r   c                 \   t          j                    t          |d          z   }	 	 t          j        | d|gz   ddd          }|j                                        dk    rdS n# t          t          j        f$ r Y nw xY wt          j                    |k    rdS t          j	        d           )az  Poll ``systemctl is-active`` until the unit reports active.

                systemd's Stopped -> Started transition after a graceful exit
                (or a hard restart) is not instantaneous; a one-shot check
                races that window and falsely reports the unit as down.
                Poll every 0.5s up to ``timeout`` seconds before giving up.
                r  T	is-activer  r=  r  F)
r{  r  r  rA  rB  r  r5   r   rC  r  )r	  r	  r?  r  _verifys        r   _wait_for_service_activez2_cmd_update_impl.<locals>._wait_for_service_activeG(  s     !?,,s7C/@/@@%
",.&+y)AA+/!%$%	# # # #>//11X==#'4 >-z/HI   ((H44$uK$$$%s   :A$ $A=<A=rr	  r   c           
         	 t          j        | d|ddgz   ddd          }n# t          t           j        f$ r |cY S w xY w|j        pd                                }|r|dk    r|S d	}d
}|                                D ]`}dD ][\  }}	|                    |          rA	 |t          |dt          |                              |	z  z  }d}n# t          $ r Y nw xY w n\a|r|n|S )a  Read the unit's ``RestartUSec`` (RestartSec) in seconds.

                After a graceful exit-75, systemd waits ``RestartSec`` before
                respawning the unit.  Callers that poll for ``is-active``
                must use a timeout >= ``RestartSec`` + transition slack, or
                they'll give up *during* the cooldown window and wrongly
                conclude the unit didn't relaunch.
                showz--property=RestartUSec--valueTr  r=  rR   infinityrr	  F))msgMbP?)usgư>)r        N@)r  r	  N)rA  rB  r   rC  r  r5   rf   r  r	  r   r   )
r	  r	  r   _showr<   r  matchedr  _suf_mults
             r   _service_restart_secz._cmd_update_impl.<locals>._service_restart_secd(  s^   #&N""%4%	 (,! !  EE *:+DE # # #"NNN#|)r0022  #cZ//"NIIKK " "D( " "e  ==.. "% %tLs4yyjL/A)B)BU)J J*.#- % % % $%!E" !(4uuW4s   " ==-C


CCscope_c                    | v r|          S |dgz   }| dk    rt          t          d          rt          j                    dk    rddg|z   dgz   }d}	 t          j        g dd	d
          }|j        dk    }|s't          j        |d|gz   d	d
          }|j        dk    }n# t          t          j        f$ r d}Y nw xY w|r|nd}|| <   |S )u  Resolve the command prefix for manage-units operations.

                Read-only systemctl calls (``is-active``, ``show``,
                ``list-units``) work unprivileged, but manage-units verbs
                (``reset-failed``, ``start``, ``restart``) on a *system*
                service trigger a polkit ``org.freedesktop.systemd1.manage-units``
                authentication prompt when run as a non-root user.  That
                interactive prompt runs inside our captured subprocess with a
                10-15s timeout — the user sees the prompt flash and "exit
                directly" before they can answer, and the resulting
                TimeoutExpired used to be swallowed silently.

                Strategy: if root, plain systemctl.  If not root, try
                non-interactive sudo (``sudo -n``) — first a blanket probe,
                then a targeted ``systemctl reset-failed`` probe so a
                least-privilege sudoers entry scoped to
                ``systemctl ... hermes-gateway*`` also qualifies
                (``reset-failed`` is an idempotent no-op we run before every
                privileged restart anyway).  If neither works, return None —
                the caller must SKIP the restart (without draining the
                gateway first!) and tell the user how to restart manually.
                ``--no-ask-password`` guarantees polkit can never hang a
                captured subprocess on this path.
                z--no-ask-passwordr   r   r   rM  rN  F)rM  rN  r  Tr  )r>  r?  reset-failedN)r   r&   r   rA  rB  rY  r   rC  )r
  r	  r	  r9  sudo_cmdsudo_ok_probe_manage_cmd_caches          r   _resolve_manage_cmdz-_cmd_update_impl.<locals>._resolve_manage_cmd(  s:   2 ...,V44 $7#88h&&I.. '
)) &~
:>Q=RRH#G(!+222+/$%" " "
 #)"3q"8& 	= &0^ (NI+F F/3()& & &F
 '-&71&<G-z/HI ( ( ("'(&-7((4C,/!&)
s   AB" "B=<B=)%DEFAULT_GATEWAY_RESTART_DRAIN_TIMEOUTr

  r)  restart_drain_timeoutg      >@g      .@user	systemctlz--userr   )z
list-unitszhermes-gateway*z--plainz--no-legendz
--no-pagerr=  z.servicer
  r  r  r
  z--property=MainPIDr
  u     → z: draining (up to zs)...)r	  r
  r   r   r(  r3  g      @z%: waiting for systemd auto-restart (~z's; no root for an immediate restart)...rx  u0    drained but didn't relaunch — forcing restartz is a system service and restarting it needs root.
    Restart it manually to load the new version:
      sudo systemctl restart zz
    To let `hermes update` restart it automatically, allow
    passwordless sudo for systemctl, or run updates with sudo.restartz  died after restart, retrying...r  z recovered on retryz--user r@  u     ✗ z7 failed to stay running after restart.
    Check logs: zjournalctl z-u z1 --since '2 min ago'
    Recover manually:
      z
systemctl zreset-failed z
      zrestart u     ⚠ Failed to restart u%     ⚠ systemctl timed out during the z-scope gateway restart (zunknown commandz0). Check the gateway with: hermes gateway status)launchd_restartget_launchd_labelget_launchd_plist_path	launchctlr  r   u     ⚠ Gateway restart failed: )r,  r	  c                 2    i | ]}|j         v |j         |S rN   r	  )r  r$  manual_pidss     r   r  z$_cmd_update_impl.<locals>.<dictcomp>;*  s4     ! ! !8{** $***rH   r+  z: draining gateway PID z (up to )r?  force_afteru     ✓ Restarted u,     ✓ Restarting manual gateway profile(s): r	  z manual gateway process(es)z(    Restart manually: hermes gateway runz;    (or: hermes -p <profile> gateway run  for each profile)r`  c                     g | ]}|v |	S rN   rN   )r  r  killed_pidss     r   r  z$_cmd_update_impl.<locals>.<listcomp>*  s#    JJJ#sk7I7I#7I7I7IrH   u6    gateway process(es) ignored SIGTERM — force-killingr	  r        ?z&Post-restart survivor sweep failed: %sz(Gateway restart during update failed: %s)has_legacy_hermes_units_find_legacy_hermes_unitsr	  u+   ⚠ Legacy Hermes gateway unit(s) detected:  (z scope)z;  These pre-rename units (hermes.service) fight the currentz<  hermes-gateway.service for the bot token and cause SIGTERMz  flap loops. Remove them with:z!    hermes gateway migrate-legacyz)  (add `sudo` if any are in system scope)z*Legacy unit check during update failed: %sz-Tip: You can now select a provider and model:z7  hermes model              # Select provider and modelu   ⚠ Git update failed: u#   → Falling back to ZIP download...u   ✗ Update failed: rR   r  )r	  )rr	  )rV   r0   rE   r   rK   r  r  r  r(   r2   r3   r4   r6   r   r]  r^  r|   r8  r9  rl  rq  r   rd	  r	  atexitregisterr	  r	  r	  rQ  r,   r   r'	  r	  rA  rB  r	  r  r  r  r  rY  r   r5   r8  r  r  r   r  r  r~	  r+  r  r  r&   r'   r  ra  r-  r  r	  r  r  r  r  r  r  r  rb  r  r  r  r  r  r_  r  rc  r   r  r 	  r*   rb  r  r  	importlibreloadrl  ri  r   r   r	  r	  r)   r  r   r	  plugins.memory.honcho.clir	  r	  r	  r	  r	  r  r)  r+  r	  rJ  rR  rL	  rW  rX  hermes_cli.tools_configr	  r   re  rM   r  r	  r	  r	  r	  r	  r	  r	  r	  r	  ri  r  r	  r
  rh  r   r  r  rf   r  removesuffixrC  r   r9  r
  r
  r
  r   r  r	  r3  rj  rk  rl  r   r{  r  rm  r	  r'
  r(
  rw  )rx  r	  gw_input_fn
assume_yes_non_interactive_updatediscard_local_changesr  _update_cfg_moder   r@  
concurrent_windows_gateway_resume_atexit_venv_holdersuse_zip_updaterE  r'	  r6	  r  r  is_forkr  r9	  r   r  current_branchr,  auto_stash_refcheckout_resulttrack_resultprompt_for_restorecommit_counthealthyr}	  r  	repair_uvvenv_python_missing
repair_envhealthy_afterdetail_afterpre_update_snapshot_idr	  update_succeededpre_pull_shapull_resultreset_result	syntax_okfailing_pathsyntax_errorrj   rollback_resultr  r  r  r  install_groupr  r`  has_desktop_appr  _desktop_build_cmdr$  tail_dhhr  r  r-
  _hcri  r	  r	  r	  r  r  r  r  r  modifiedr:  per	  
backfilledr	  syncedr	  r	  r	  r	  missing_envmissing_configcurrent_ver
latest_verhas_new_optionsversion_bump_onlyneeds_migration_mig_errr	  r  interactive_migrationr  r	  cron_restorer	  cfg_excr	  _exit_code_pathr	  r	  r	  r	  r	  r	  r	  r	  r	  rs  r
  r
  r
  _DEFAULT_DRAIN
_cfg_drain
_cfg_agent_drain_budgetrestarted_servicesrelaunched_profilesscope	scope_cmdrb	  svc_namer.  _manage_cmd	_main_pidr
  _graceful_ok_restart_sec_post_drain_timeoutr
  _scope_flag
_sudo_hintr
  r
  r
  
plist_pathservice_pidsr	  r  r$  drainedsvcr`  unmapped_count_service_pids_after
_surviving_stuck_terminate_pid
_sweep_excr'
  r(
  r   r)   is_sysr
  r%
  r"
  s                                                                                                                                                                     @@@r   r	  r	  $  s5    		D	D	D	D 
 gdE51122J 	 	<	<	  "":sz'8'8':':; 
 " *
	*555555&;==.B33IrBBK+t,, ;KOO,KWUUVV\\^^(-(:% 	* 	* 	*LLSUXYYY$)!!!!!!	*
 

()))	GGG }} WT7E:: '))"<[IIJ ::{SSTTT 4   @BB 
    1#	
 	
 	
 }} WT<?? 577 	5mDDEEE12IJJJHQKKK NV#G>> <7""!NN??????**<88F%%%?@@@V   HQKKK |w7>>#3#3   	
 	
 	
 	
 gG
|wAAA G\222 !,77Jz""G '(((:    	KD!!!12IJJJJ12IJJJJ (--'(((!~w&11	
 
 
 "a''!(..00F'6115G65Q5QQRRRvE36,,..q13332FFFF'6115NRX5X5XZ    @AAA 97v00221577888HQKKK ;;;;
 
 
  ,,.. V## "V++  1111 
 WWWWWWXXX;G\RRN(n:v.. #	  O )Q..
  *~z49K69K9KLL$#'	       *a// &10#(*(-%0    WWWWXXX#*0022 RP<#6#<#<#>#>#I#I#K#KA#NPPQQQHQKKK;G\RRN $& OOM#)"2"2"4"4"L9J9J9L9L 	 z#;6#;#;YGG
 
 
 6=..00111$&&&  E6V++-g|DDD )( " 2(    ff%555z>::$#'    9::OGV )1KLLLm6mm$$$<===/111;;;;;;%IKK	
 !$/MM<yyu> (3}}B||(D &(('# ' 9 ABBBN"FF3(#   
  !ZBJ!Zs<RXCX?Y?Y!Z!ZJG"E*
%     Hu5U    0111.H.J.J+|  [67777RLRRSSSYZZZZ/00012IJJJF7<777888 "&	@??????%:%:TU%V%V%V"% NL4JLLMMM 	@ 	@ 	@LL93????????	@ 	&'''  ),??`	$.6;&AA #	  K %** f    *~w2DF2D2DEE$#'	       *a//D6DDDEEE#*0022 B@<#6#<#<#>#>@@AAA_W]__   HQKKK 5T5 51I|\  NOOO)<))*** - !$L 1 1 < < > >rr B - -mTmm,,,, aGGGGcrc1BGGGHHH&0n7Hl"CC('+!	' ' 'O '1Q66VWWWUVVVVMNNNYYY<YYZZZ*17799 ]!"[/*@*F*F*H*H*S*S*U*UVW*X"["["[\\\GGGWXXX_L___```#) ( ]N]]]   DEEEE*  -$&    -$&$6!,   % ) ( ]N]]]   DEEEE*  -$&    -$&$6!,    % *4 	!"""
 (55 	eeeGWXLLSS^cee  
  	Av'')'<@@@ 	()))3444FFFFFFFF 	>4/ 	4*733F &	^N
NM3|f7L3M3MNNFf%% b

<...

<... ,`aaaf%% L*<*>*> Lqrrr.FKKKK?V=     ~tU3G{m+$#'	     0   ^T;_U$       ] ,[\\\ 8$6$8$8 8qrrr.w777?}]]]] 	()))%'''!###lU*+++ #V+i76{CC4OtSghsStSt999999.(0022 	67K7KE7R7R 	6Wf 	6CDDD"%.$8I9Vd!e 22D,WWWL&!++56Hl[[[&!++]^^^yy,"5";!B!B!D!D!O!O!Q!QRURVRV!WXX  $KKKHHHHHHC4466CCCDDDD4555!"""	KIIIIII''55 KIJJJ 	K 	K 	KLLFJJJJJJJJ	K	****S!!!! 	 	 	D		D555555GGG1222 [t,,,Fh YWS!122WW$))F8DT:U:UWWXXXzz)$$ ]S	!233]]tyyPYIZ?[?[]]   zz/** PS!899PPPQQQ5   zz)$$ OMs6)#455MMMNNN(# 5FJJy,A,A 53444 	D 	D 	DLL?CCCCCCCC	D!	       
 )=??L ;EFFF% ; ;A;//dCCC 3'8!9!9 3%>FF 3%(x)<)<%=%=F&)!%%	2*>*>&?&?G'*155"+E+E'F'FH$&E% ? %-=-=-=-= > > >& F %-D7-D-D-D E E E' K %-I-I-I-I J J J9>%PTYYu%5%5%5LFF%2F51655V556666$ ; ; ;91699B999::::::::; 	 	 	D		AAAAAA..T:::J F3z?? F F.2ii
.C.CF F    	 	 	D		LLLLLL//11F B@V@@@AAA 	 	 	D	 	=>>>	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 +*>>>2244"6"6"8"8Z{<n==<K*$< 	 *E[:-E b	7 GGGU+UUZUUU  D5====RSSSS D D DJJJKKKBCCCCCCCCD  R	7GGG9 9 9 9*  B^[!1!1^^^   [.&AAA CW3~#6#6WWWXXX^]EBBBGGG #]    ##Lc  UWWUWW  i&&(( #SZ->->-@-@ #Z[[[!#MNN H
   # # #"HHH# 333 !DJD(f2D)% ).5JRWXXX;' 87>+B 8GGG6777  XJ X(f2D2D+2DVWWWPQQQQ5666	IFFFFFF778NOOL J ,[ 9J J+7+FJ J J  
  	I 	I 	ILLBCHHHHHHHH	I 	$%%%	C+---- 	C 	C 	CLL>BBBBBBBB	C	D,.... 	D 	D 	DLL?CCCCCCCC	D
	?"$$$$ 	? 	? 	?LL:A>>>>>>>>	?	=!%	W999999*{}}277	2FFk400 )-#(<dCC* *&  W W WLgVVVVVVVVW #	1L$@@@L.. A GFFFFFCDDD""40000 	= 	= 	=LL8!<<<<<<<<	=&  	-//2EEO**3////   z		H
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 %$$$
 "&% % %% % 	% % % %@ "%15 15 1515 15 	15 15 15 15f ')8C 8T 8c 8 8 8 8 8 8@&        & & &!%&J999999([]]..w77=2
'^^,CDD

   6 "- *%%%~.. 
 z* 6 6 6 %n 5 56  t44t;M!#%%K"$ )(** ],,....    D k845}-) W W$E9S!+%   ,0!%$&" " " %+M$7$7$9$9$D$D$F$F z" z"D$(JJLLE#( ) (#( !$D $(==#<#< ) ('+'8'8'D'DH$.N )[(,C C/3%)()	% % %E  %|1133x?? ( +>*= %y(+ +K )*I.(2$-(.(0(<(1	'&%& 48)-,-)" )" )" -01C0J0J0L0L0QPQ,R,R		 * * 9 1$ . . .
 -.			. ,1L(1}} %$bX$b$b]I[I[$b$b$b!" !" !" 0M/L$-2?0" 0" 0"
  , T"8 $/#:$.N(3~x6P(P7;-102	%& %& %& %& %/N(3w6I(I7;-102	%& %& %& %& (@'?(1(004(& (& (& %1
 );(A(A((K(K(K(0 0D/C$-$,,/0" 0" 0"
 7:$($04$77" 7" 3 $/#6<#;M;M$))O )O )O:=l:K:K)O )O )O%& %& %&
 $<#;$-$,,?$" $" $" !-
 %7$=$=h$G$G$G$,
 !&$gX$g$g$g!" !" !"  +2 %%fX %f %fDL%f %f %f!" !" !" !)$ 'N +~x.H H/3%)(*	    '1n +y(.C C/3%)(*	' ' 'G  '1Q66 $<#;$-$,,0$" $" $" ,!*
 %7$=$=h$G$G$G$G %*([([([([%& %& %& %/N(3~x6P(P7;-102	%& %& %& %& %/N(3y(6K(K7;-102	%& %& %& %& (@'?(1(004(& (& (& %*
 );(A(A((K(K(K(-.Tx.T.T.T(U(U(U(UCHF??iiXZ@E@Q@QWWWY
(--kX -k -k?I-k -kVa-k -kfn-k -k 6@-k -k LW-k -k fn-k -k 6@	-k -k LW	-k -k ai	-k -k)* )* )* )* !&$cx$c$c7>K_K_KaKa$c$c!" !" !" !"qz"v -   %4    ME M M;>70YHYM M M       	 xzz           "8!7!9!9J!((** Q *(&2C2C2E2EF+/!%$%	! ! ! !+q00Q / 1 1 1 2 9 9:K:K:M:M N N N N#-#@ Q Q Q*1!Xr*B*B*Hb)O)O)Q)Q %&Ov&O&O P P P P P P P PQ *:+DkR   D -,..L++)  K! ! ! !::UUU! ! !
 /4466 )9 )9	T>>t|SQQ  8T\ 8 8# 8 8!-008 8 8   87"/    W_5555.@     '&sEEEE$$$#**4<8888"  +++GC111OOC((((*O<   D " [ - 4 4C2S223333& R II&9::EPPPQQQ!$[!1!1C8K4L4L!L! V>VVVWWWDEEE%))Y   & k SC   &7&7&9&9#..!4!%  
 KJJJJJJ %GGGdVddd   ONNNNN% ! !!
 +N3d;;;;; 2OWM ! ! ! D! K$$$ S S SEzRRRRRRRRS  	H 	H 	HLLCQGGGGGGGG	H 	..EFFF	J          )(** C/F/F/H/H CCDDD*C*C*E*E : :&D$(.:HHFE888%8889999STTTTUUU78889:::ABBB 	J 	J 	JLLEqIIIIIIII	J 	()))=>>>GHHHHH(   <7""/A//0007888GGGD!!!!+++,,,HQKKKKKKKKK "!!!!!s  1A6C( (
D2DD/O O%UB\7 2B\7 'e0 /B\7 0
f:fB\7 f)B\7 	J<s AB\7 At;;FB\7 !{2 1B\7 2:|/,B\7 .|//HB\7 E%AE4 E3B\7 E4
AF#E>AFFB\7 FAF#F#B\7 F'AG GB\7 G
AGGB\7 GAGGB\7 GE?AM MB\7 M
ANM AN M;B\7 N ANNB\7 N	7AT OD AS#S!AT S#
ATS-ATTAT TATTAT TB\7 T
AT$T!B\7 T#AT$T$B\7 T(AAV  U?B\7 V 
AVV
B\7 VAVVB\7 V%AV7 V6B\7 V7
AWWB\7 WAWWBB\7 YAY: Y8B\7 Y:
AZ0Z!AZ+Z%B\7 Z+AZ0Z0DB\7 _3A` `B\7 `A``B\7 `A``B#B\7 b6AAc: c9B\7 c:
Ad)dAd$dB\7 d$Ad)d) B\7 e
Ae eB\7 e
Afe#Afe>B\7 fAffB\7 fAf fB\7 f
Ag
f%Agg B\7 gAg
g
B\7 gAg gB\7 g
Ahg'AhhB\7 hAhhB\7 hAk8 hAAi2 i1Ak8 i2
Aj!i<AjjAk8 jAj!j!AAk8 k7B\7 k8
Al'lAl"lB\7 l"Al'l'B\7 l>Am mB\7 m
Am!mB\7 m Am!m!B\7 m%B BV o&Ao- o,BV o-Ao<o9BV o;Ao<o<BV p<Ap? p>BV p?
Aqq	BV qAqqBV q"Aq3 q2BV q3#ArrBV rArr7BV s
As sBV s
As)s&BV s(As)s)BV tC'BD1w+AAx:x9BD1x: AyyBD1yAyyKBD1D/BV D1BF D<BV D?BF E(BE;E5BV E;BF F BV FABI( G*'BH HBI( HBI$H!9BIIBI( IBI$I$BI( I'BV I(BJ	JBV JBJ	J	BBV L)BMMBV MBMMBV MBMMABV N&/BOOBV OBO,O)BV O+BO,O,B8BV R%A:BU% T BT/T.BU% T/BUUBU% U
BUUBU% U$BV U%
BVU/BVVBV VBVVBV VB\7 V
BW	V#BWV?B\7 WBW	W	B\7 WC(B[ [B\7 [
B[5[B[0[+B\7 [0B[5[5A B\7 \7B_]A8B_
_
B_c                 h   h d}h d}g }d}|t          |           k     r| |         }||v r|                    |           |dz  }g }|t          |           k     r}| |                             d          sb| |         |vrX|                    | |                    |dz  }|t          |           k     r%| |                             d          s
| |         |vX|r(|                    d                    |                     n|                    |           |dz  }|t          |           k     |S )a  Join unquoted multi-word session names after -c/--continue and -r/--resume.

    When a user types ``hermes -c Pokemon Agent Dev`` without quoting the
    session name, argparse sees three separate tokens.  This function merges
    them into a single argument so argparse receives
    ``['-c', 'Pokemon Agent Dev']`` instead.

    Tokens are collected after the flag until we hit another flag (``-*``)
    or a known top-level subcommand.
    >(   acpr  r   r  r  clawcrondumpr	  r^  loginr  r  setupr  r}  r`  doctorhonchoimportlogoutmemoryrP  r  r  r  r	  pairingpluginsr	  r]   webhookinsightsr  r  r  r  r  
completionr  >   r   r   r   r   r   rB   r   r7  )r   r  rW   r*   )r@   _SUBCOMMANDS_SESSION_FLAGSr  r   re  r:  s          r   _coalesce_session_name_argsr
  *  sY   ) ) )LT <;;NF	A
c$ii--QN""MM%   FAECIIQ**3// G<//T!W%%%Q CIIQ**3// G<//  /chhuoo...MM%   FA% c$ii--& MrH   c                 /   ddl m}m}m}m}m}m}m}m}m	}	m
}
m} ddlm} t          | dd          }| |            } |            }t          d|            t          d|             |            }|D ]}|j        |k    s|dk    r|j        r|j        r,t          d	|j         |j        rd
|j         dndz              t          d|j        rdnd            t          d|j         d           |j        r(|j        p|j        }t          d| d|j                     nt                       dS |dk    r% |            } |            }|st          d           dS t          dddddddddddddd 
           t          dd  d!d" d!d# d!d# d!d$ 
           |D ]}|j        |k    s|dk    r	|j        rd%nd&}|j        }|j        pd'dd(         }|j        rdnd}|j        r|j        p|j        nd'}|j        rd'}|j        r|j         d)|j        pd* }|dd+         }nd'}t          | |d,d|dd|dd|dd| 
           t                       dS |d-k    r| j        }	  ||           |dk    rt          d.           nt          d/|            dS dS # t6          t8          f$ r1}t          d0|            t;          j        d1           Y d}~dS d}~ww xY w|d2k    r| j        }t          | d3d4          }t          | d5d4          }t          | d6d4          }t          | d7d4          }	 t          | d8d          }|p|du}  ||||| ||t          | d9d          :          }!t          d;| d<|!            | s|rDt          | d8d          p	 |            }"|rt          d=|" d>           nt          d?|" d@           | s|r6	 ddAlm }#  |#|          rt          dB| d           n# tB          $ r Y nw xY w| s|s ||!          }$|$r%|$"                    dC          rt          dD           nZ|$r6tG          |$"                    dEg                     }%t          |% dF           n"t          dG$                    |                     |s ||          }&|&r<t          dH| dI|&            t          dJ| dK           t          dL| dM           nb ||          }'|'rUt          dN|'             |
            s9t          dO |             dP           t          dQ           t          dR           	 dStK          |!&                    tO          j(                                        z   }(n# t6          $ r tK          |!          }(Y nw xY wt          dT           t          d&| dU           t          d&| dV           t          d&| dW           |s|r't          dX|( dY           t          dZ|( d[           n5t          d\| d]           t          d^           t          dZ|( d_           t                       dS # t6          tR          t8          f$ r1}t          d0|            t;          j        d1           Y d}~dS d}~ww xY w|d`k    rm| j        }t          | dad4          })	  |||)b           dS # t6          t8          f$ r1}t          d0|            t;          j        d1           Y d}~dS d}~ww xY w|dck    rdddl*m+}* tY          t          | ded4                    }+tY          t          | dfd4                    },tY          t          | dgd4                    }-t          | dhd          }.t          | did          }|+r1|,s/t          djt:          j-        k           t;          j        dl           |+r3|.s|r/t          dmt:          j-        k           t;          j        dl           |+s1|s/t          dnt:          j-        k           t;          j        dl           |.r1|,r/t          dot:          j-        k           t;          j        dl           |rf|.sc|,s`	 |*.                    |          dk    rddplm/}/ tO           |/                      }!n|*0                    |          }!nI# tB          $ r<}0t          d0|0 t:          j-        k           t;          j        d1           Y d}0~0nd}0~0ww xY w|!1                                s3t          dq| drt:          j-        k           t;          j        d1           |*2                    |!          }1|1"                    d9          pd}2|2st          ds| dt           n,|1"                    du          rdvnd}3t          |3 |2            t;          j        d           |.r	 |*.                    |          dk    rddplm/}/ tO           |/                      }!n|*0                    |          }!|*3                    |!|.d4w           t          dx| dy           nI# tB          $ r<}0t          d0|0 t:          j-        k           t;          j        d1           Y d}0~0nd}0~0ww xY wt;          j        d           ddzl*m4}4 |+r<|45                    d{|          }5|5s#t          d}           t;          j        d           n|g}5d}6d}7|5D ]u}8|46                    |8|-~          }9|9j7        r%|6d1z  }6t          d|9j         d|9j8                    E|7d1z  }7t          d|9j         d|9j9         t:          j-        k           v|+st;          j        |6d1k    rdnd1           t;          j        |6dk    rdnd1           dS |dk    r| j        }ddl m0}:m:};m;}<m<}=m=}>m>}?m}m?}@  |;|          s't          d| d           t;          j        d1            |:|          }! |<|!          \  }}A |=|!          } |>|!          }B |?|!          \  }C}D}E |@|          }Ft          d|            t          d|!            |rt          d| |Ard
|A dndz              t          d|rdnd            t          d|B            t          d|!dz  @                                rdnd            t          d|!dz  @                                rdnd            |Cr>t          d|C d)|Dpd*            |Ert          d|E            t          d| d           |Fr=t:          jA        dk    }G |            |Gr|F dn|Fz  }Ht          d|F d| d|H d           t                       dS |dk    rk| j        }t          | dd4          }It          | dd          }Jddl m:};mB}K  |;|          s't          d| d           t;          j        d1           |Jp|}F	  |K|F           n=# t6          $ r0}0t          d0|0            t;          j        d1           Y d}0~0nd}0~0ww xY w|Ir5 |	|F          rt          d|F d           dS t          d|F d           dS  ||F          }&|&r&t          d0|&            t;          j        d1            ||F|Jr|nd          }'|'r9t          d|'             |
            st          d |             dP           dS dS dS |dk    rddl mC}L 	  |L| jD        | jE                  }Mt          d| jD         d| jE                    t          d|M d           dS # t6          tR          t8          f$ r1}t          d0|            t;          j        d1           Y d}~dS d}~ww xY w|dk    rddl mF}N | j        }| jG        p| d}O	  |N||O          }Pt          d| d|P            dS # t6          t8          f$ r1}t          d0|            t;          j        d1           Y d}~dS d}~ww xY w|dk    rddl mH}Q 	  |Q| jI        t          | dd                    }!|!j        }t          d| d|!             ||          }&|&s ||          }'|'rt          d|'            t                       dS # t6          tR          t8          f$ r1}t          d0|            t;          j        d1           Y d}~dS d}~ww xY w|dk    r#ddlJ}RddlKmL}SmM}TmN}U 	 |RO                    d          5 }V |S| jP        tO          |V          t          | dd                    }Wt          |W           t          | dad4          so	 t          d          S                                T                                }Xn# t          t          f$ r d}XY nw xY w|Xdvrt          d           	 ddd           dS ddd           n# 1 swxY w Y    |T| jP        t          | dd          t          | dd4          t          | dd4                    }Wt          d|WjW        j         d|WjW        jX                    t          d|WjY                    |WjW        jZ        rt          d|WjY         dƝ           |Wj[        rt          d|WjW        j         dȝ           t          d|WjW        j         dM           dS # |Ut6          f$ r1}t          d0|            t;          j        d1           Y d}~dS d}~ww xY w|dk    rddlKm\}Ym]}ZmN}U ddl m0}:m.}[ | j        }	  |[|          }\ |Z |:|\                    }]|]'t          d|\ d͝           t;          j        d1           t          | dd4          }^t          | dad4          st          d|\ d|]jP        pdћ            t          d|]jX                    |^rt          dӦ           nt          dԦ           t          dզ           	 t          d֦          S                                T                                }Xn# t          t          f$ r d}XY nw xY w|Xdvrt          dצ           dS  |Y|\|^ئ          }Wt          d|WjW        j         d|WjW        jX                    |Wj[        rt          d|WjW        j         dȝ           dS dS # |Ut6          f$ r1}t          d0|            t;          j        d1           Y d}~dS d}~ww xY w|dk    rddlKm^}_mN}U 	  |_| j                  }`n?# |Ut6          f$ r0}t          d0|            t;          j        d1           Y d}~nd}~ww xY w|`st          d| j         dߝ           dS t          d|`"                    d                      t          d|`"                    dd*                      |`"                    d9          rt          d|`d9                     |`"                    d          rt          d|`d                     |`"                    d          rt          d|`d                     |`"                    d          rt          d|`d                     |`"                    d          rt          d|`d                     |`"                    d          rt          d|`d                     |`"                    d          pg }a|art          d           |aD ]}b|b"                    dd{          rdnd}3d&|bd          d
|3 d}c|b"                    d9          r|cd|bd9          z  }ct          |c           |b"                    d          t          d|bd                     t                       dS dS )u;   Profile management — create, delete, list, switch, alias.r   )r	  create_profiledelete_profiler	  set_active_profileget_active_profile_namecheck_alias_collisioncreate_wrapper_scriptremove_wrapper_script_is_wrapper_dir_in_path_get_wrapper_dirra  profile_actionNz
Active profile: zPath:           r   zModel:          rq   rr   rR   zGateway:        runningr  zSkills:         z
 installedzAlias:          u    → hermes -p r  zNo profiles found.z
 Profilez<16r7  Modelz<28Gatewayz<12AliasDistributionu-   ───────────────r  uQ   ───────────────────────────u!   ───────────u<   ────────────────────u    ◆r     —   @rr  rK  z<15usez Switched to: default (~/.hermes)zSwitched to: r   rB   createcloneF	clone_allno_alias	no_skills
clone_fromr*  )r   r
  r
  clone_configr
  r
  r*  z

Profile 'z' created at zFull copy from z5 (excluding session history, backups, and snapshots).z.Cloned config, .env, SOUL.md, and skills from r  )clone_honcho_for_profilezHoncho config cloned (peer: r	  z`No bundled skills seeded (--no-skills). Delete .no-bundled-skills in the profile to opt back in.r  z bundled skills synced.u9   ⚠ Skills could not be seeded. Run `{} update` to retry.u   
⚠ Cannot create alias 'u   ' — z/  Choose a custom alias:  hermes profile alias z --name <custom>z$  Or access via flag:     hermes -p z chatzWrapper created: u   
⚠ z is not in your PATH.z3  Add to your shell config (~/.bashrc or ~/.zshrc):z(    export PATH="$HOME/.local/bin:$PATH"z~/z
Next steps:z0 setup              Configure API keys and modelz" chat               Start chattingz/ gateway start      Start the messaging gatewayz
  Edit z/.env for different API keysz  Edit z"/SOUL.md for different personalityu.   
  ⚠ This profile has no API keys yet. Run 'z setup' first,z8    or it will inherit keys from your shell environment.z!/SOUL.md to customize personalitydeleter  )r  describe)r   all_missingr  	overwriter9  r   z'profile describe: --all requires --autor   r   zJprofile describe: --all is mutually exclusive with a profile name / --textz<profile describe: profile name is required (or --all --auto)z:profile describe: --text is mutually exclusive with --autor   zError: profile 'z' not foundz(no description set for 'z')description_autoz[auto] )r*  r
  zDescription updated for 'r}  )r  T)missing_onlyz'All profiles already have descriptions.)r
  zDescribed '': zprofile describe r  r
  )get_profile_dirprofile_exists_read_config_model_check_gateway_running_count_skills_read_distribution_metar
  find_alias_for_profilezError: Profile 'z' does not exist.z

Profile: z	Path:    z	Model:   z	Gateway: z	Skills:  z	.env:    r   r,   znot configuredz	SOUL.md: zSOUL.mdzDistribution: zInstalled from: z  (run `hermes profile info z` for full manifest)r  z.batz	Alias:   r)
  aliasremove
alias_name)r
  validate_alias_nameu   ✓ Removed alias 'r  z
No alias 'z' found to remove.)r~  u   ✓ Alias created: u   ⚠ r{  )rename_profilez
Profile renamed: r  zPath: rc  export)export_profilez.tar.gzu   ✓ Exported 'z' to r
  )import_profileimport_name)r   u   ✓ Imported profile 'z' at z  Wrapper created: r  )plan_installinstall_distributionDistributionErrorhermes_dist_preview_rX   install_name)override_namez
Proceed with install? [y/N] >   r  r  zInstall cancelled.r  )r   r  create_aliasu   
✓ Installed 'z' vz  Profile path: zA  Next: copy .env.EXAMPLE to .env and fill in required keys:
    z/.env.EXAMPLEz^  Cron jobs were included but are NOT scheduled automatically.
  Review them with:  hermes -p z
 cron listz
  Use with:      hermes -p r  )update_distributionread_manifestr
  )r
  normalize_profile_namezt' is not a distribution (no distribution.yaml). Only profiles installed via `hermes profile install` can be updated.force_configz	
Update 'z' from: z(no source)z  Currently at version z6  --force-config set: config.yaml WILL be overwritten.zC  config.yaml will be preserved (pass --force-config to overwrite).zA  User data (memories, sessions, auth, .env) will NOT be touched.z
Proceed? [y/N] zUpdate cancelled.)r
  u   
✓ Updated 'u   ' → vz6  Cron files were refreshed.  Review with:  hermes -p r  )describe_distributionr
  z	Profile 'z/' is not a distribution (no distribution.yaml).
Distribution: r   zVersion:      r]   zDescription:  authorzAuthor:       licensezLicense:      hermes_requireszRequires:     Hermes r  zSource:       installed_atzInstalled:    env_requiresz
Environment variables:requiredr  r.  z      default: )_r   r	  r
  r
  r	  r
  r
  r
  r
  r
  r
  r
  r   rb  r0   r|   r   
is_defaultr  r  gateway_runningskill_count
alias_pathr
  distribution_namedistribution_versionr   r   r   rE   r   r/
  r
  r   r(   r   formatr4   r  r   r8   r7  rw   r   rV   r   r
  r   r
  r   read_profile_metawrite_profile_metar  list_describable_profilesdescribe_profilerL  r*  r\  r
  r
  r
  r
  r
  r
  r,   r   r
  r
  old_namenew_namer
  outputr
  r  r[  hermes_cli.profile_distributionr
  r
  r
  r  r  _render_distribution_planr)  r5   r6   r+  r*  r  r]   
target_dirr
  has_cronr
  r
  r
  )drx  r	  r
  r
  r	  r
  r
  r
  r
  r
  r
  r
  rb  actionr   dhhr   r  alias_displayr  r  r   r  gwr
  r  r  r
  r
  r
  r
  r
  r
  profile_dirsource_labelr
  r  r  	collisionwrapper_pathprofile_dir_displayr  _profiles_modall_flag	auto_flagoverwrite_flag
text_value_hhr   r  r  tag_pdtargetsok_count
fail_counttgtoutcomer
  r
  r
  r
  r
  r
  r
  r  rP  	dist_namedist_versiondist_sourcer
  
is_windowsr(  r
  custom_namer
  r
  new_dirr
  r
  result_pathr
  r[  r
  r
  r
  r  planr  r
  r
  r
  canonr  r
  r
  rq  env_reqserrj   sd                                                                                                       r   cmd_profiler  9+  sD!                             544444T+T22F~..00!!##1<11222&&&''' =?? 	 	Av%%,)*C*C*C7 4174412C-
----E   VA4E'Tyy9VV   BBBBCCC< U$%L$:AFMS]SS16SSTTT =??((** 	&'''F 	.)@ . .'@ . .	@ . .-. .+. .	
 	
 	
 	,
 , ,
 , ,
 , ,, ,), ,	
 	
 	

  	Q 	QA Ff$$9)<)<)<  
 6DW%ss+E/>YB01GQ\+QV%E| " -OO0F0M#OOCRCyVOTOOOOOOBOOOUOOOOOPPPP	5 	t$$$y  89999,d,,----- :9 -. 	 	 	-A--   HQKKKKKKKKK	 
8		 gu--D+u55	4U33D+u55	g	 |T::J :Jd$:L(.%#)!##D->>  K @@@;@@AAA y D,55R9P9P9R9R   O, O O O   
 XXXX  
  y RRRRRR//55 FDTDDDEEE    D ! I ,,[99 fjj):;; S      Hb!9!9::FV<<<====SZZ      N11$77	 NOOOIOOPPP`$```   LLLLMMMM#8#8#>#>L# N@,@@AAA6688 N!"T+;+;+=+="T"T"TUUU! U   ""LMMM7&*S1H1H1U1U-V-V&V## 7 7 7&)+&6&6###7 /"""MtMMMNNN?t???@@@LtLLLMMM X	 XS"5SSSTTTW 3WWWXXXXZdZZZ   PQQQV 3VVVWWWGGGGGO->? 	 	 	-A--   HQKKKKKKKKK	 
8		 dE5))	N4S))))))-. 	 	 	-A--   HQKKKKKKKKK	 
:		 	988888mU;;<<vu5566	gdK??@@T6400
t^T22 	I 	;#*MMMMHQKKK 	 	t 	\Z    HQKKK 	 	PWZWabbbbHQKKK 	) 	LZ    HQKKK  	
 	9 	 77==JJGGGGGG"&ssuu++KK"/"?"?"E"EK   oooCJ7777 %%'' ::::LLLL 22;??D88M**0bD &:$:::;;;;#'88,>#?#?GiiRndnn%%%HQKKK  	 77==JJGGGGGG"&ssuu++KK"/"?"?"E"EK00 *%* 1   
 :$:::;;;;   oooCJ7777 HQKKK 	877777 	333FFG ?@@@fG
 
	 
	C**3.*IIGz ARG$8RRW=PRRSSSSa
P(<PPPP      	0H(a--QQQ///hll*****	6		 		
 		
 		
 		
 		
 		
 		
 		
 		
 		
 		
 		
 		
 		
 		
 		
 		
 		
 		
 		
 ~d## 	<T<<<===HQKKK%od++,,[99x##K00{++/F/F{/S/S,	<++D11
"D""###'+''((( 	P%e%%X)M)9h)9)9)9)92NOOO:r8))y::;;;"&""###[[6%9$A$A$C$CYIY[[	
 	
 	
 	^[9%<$D$D$F$F\L\^^	
 	
 	
  	MD9DD|/BsDDEEE 8666777KKKKLLL 	N0J&&((:,]z,?,?,?,?S]^GLjLLLL'LLLMMM	7		 x//dL$77KKKKKKKK~d## 	<T<<<===HQKKK (D
	
++++ 	 	 	/C//"""HQKKKKKKKK	  	L$$Z00 C9J999:::::A:AAABBBBB--j99I +	++,,,00;#@44D  L  L:L::;;;..00 LJ!1!1!3!3JJJKKKKKL LL L 
8		666666	$nT]DMBBGKKKDMKKLLL&7&&&'''''O->? 	 	 	-A--   HQKKKKKKKKK	 
8		666666 04 0 0 0	(.v66K;4;;k;;<<<<<-. 	 	 	-A--   HQKKKKKKKKK	 
8		666666	(.74#E#E  K #DC4CCkCCDDD .-d33I @44T:: @>>>???GGGGGO->? 	 	 	-A--   HQKKKKKKKKK	 
9			
 	
 	
 	
 	
 	
 	
 	
 	
 	
*	 ,,4J,KK s#|KII")$"E"E  
 *$///tUE22 $!&'G!H!H!N!N!P!P!V!V!X!X$&78 $ $ $!#$\112333                      " ('T>488dGU33$T7E::	  D Tdm&8TTT]=RTTUUU6T_66777}) :?: : :   } U6:m6HU U U   K$-2DKKKLLLLL!:. 	 	 	-A--   HQKKKKKKKKK	 
8			
 	
 	
 	
 	
 	
 	
 	
 	
 	

 	POOOOOOO $	**400E#mOOE$:$:;;G[u [ [ [   "4??L4.. S5SS'.2QMSSTTTAAABBB aRSSSS_```YZZZ "#677==??EEGGFF "34      FFF ---...F&&u<HHHDVDM$6VVt}?TVVWWW} @!%!3@ @ @     
 ":. 	 	 	-A--   HQKKKKKKKKK	 
6		\\\\\\\\	(():;;DD!:. 	 	 	-A--   HQKKKKKKKK	  	*D- * * *   F3&!1!1334449txx	37799:::88M"" 	:84#68899988H 	534>3344488I 	644	?4455588%&& 	EC$/@*ACCDDD88H 	534>3344488N## 	;94#799:::88N++1r 		=,--- = =$&FF:t$<$<Ljj*0BvJ00#00066-(( 87B}$5777Dd66)$$0;ByM;;<<<O 
	s8  53J, ,K.=&K))K.BY $P Y 
PY PE Y 6V Y V$!Y #V$$B9Y Z'6&Z""Z'	[ \)&\\>Ac 
d2dd A7i 
j"2jjx 
y&&yy A}* *~2&~--~2!8 8A@:@	&A@5@5A@:A
A7AC CADC&ADDADD$AK8 D:AAHF3AF?F>AHF?AGGAHGAGGAHG,AK8 G9AK8 HAH	H	AK8 HAH	HC)AK8 K8AL5L&AL0L0AL5MCAS' P'3AQ QAS' QAQ1Q.AS' Q0AQ1Q1AS' R	AAS' S'AT$S3&ATTAT$T7AU UAVU&AU?U?AVc                    ddl m} | j        }t          d|j         d|j                    |j        rt          d|j                    |j        rt          d|j                    |j        rt          d|j                    t          d| j	                    t          d	| j
                    | j        r=| j
        |z                                  }|rt          d
           nt          d           |j        rlt          d           |j        D ]T}|j        rdnd}t          j                            |j                  du}|s| j
                                        r| j
        dz  }|                                r	 |                                                                D ]k}|                                }	|	r|	                    d          r.|	                    dd          d                                         }
|
|j        k    rd} nln# t0          $ r Y nw xY w|rdn
|j        rdnd}d|j         d| d| d}	|j        r|	d|j         z  }	t          |	           V| j        rt          d           dS dS )zAPrint a human-readable summary of a pending distribution install.r   )MANIFEST_FILENAMEr
  z vr  z  Author:   z  Requires: Hermes z  Source:   z  Target:   uC     (profile exists — will overwrite distribution-owned files only)u    ⚠ Profile exists but is NOT a distribution.  Installing here will
    overwrite its SOUL.md, skills/, cron/, and mcp.json.
    Your memories, sessions, auth.json, and .env will be preserved,
    but any hand-edits to distribution-owned files will be lost.z
  Env vars:r
  r  Nr   rc   rb   rB   Tu   ✓ setzneeds settingr
  u       • rq   r  rr   r.  uj   
  ⚠ This distribution ships cron jobs.  They will NOT run automatically — review and enable manually.)r
  r  r  r|   r   r]   r*  r
  r
  
provenancer
  r  rC  r
  r
  r&   r'   r(   r   r   r8  r5   rW   rf   rM   r
  )r  r  mfexisting_is_distributionr  r   alreadyenv_pathr<   rj   rF  r  s               r   r
  r
  -  s   AAAAAA	B	
4RW
4
4

4
4555	~ %#2>##$$$	y *(RY(()))	 :8B$688999	
*
*
*+++	
*
*
*+++}  %)O6G$G#P#P#R#R # 	WXXXXS   
 o/ 	 	B ";**C jnnRW--T9G t5577 ?V3##%% 
#+#5#5#7#7#B#B#D#D & &C#&99;;D#' )4??3+?+? ) ("&**S!"4"4Q"7"="="?"?C"bg~~*. %  . #   ")ZYY2;0YTYF9bg9999999D~ 10000$KKKK} 
<	
 	
 	
 	
 	

 
s   BH
H%$H%c                     t                      } | st          d           dS t          t          |            d           | D ]}d}	 t          j        dk    rd| d}t
          j                            |          rwt          |d          5 }|	                                
                    d	d
                              dd                                          }ddd           n# 1 swxY w Y   n# t          t          f$ r Y nw xY w|rt          d| d|            t          d|            t          |           S )a:  Print ``hermes dashboard`` PIDs and return the count.

    Uses the same detection logic as ``_find_stale_dashboard_pids`` (the
    current process is excluded, but since ``hermes dashboard --status``
    runs in a short-lived CLI process that never matches the pattern,
    the exclusion is irrelevant here).
    &No hermes dashboard processes running.r   z& hermes dashboard process(es) running:rR   r  z/proc/z/cmdlinerg  rf      r   r3  r  Nrn  r  )r>  r|   r   rE   r   r&   r)   r,   r.   r  r3  r  r5   rM   r   )rr  r  r	  cmdline_pathr  s        r   _report_dashboard_statusr  -  s    &''D 6777q	SYY
>
>
>??? $ $	|w&&55557>>,// lD11 QFFHH$WWd33#VGIV>>"UWW	                 $ 	 	 	D	 	$-S--G--...."S""####t99s8   AC5AC)C5)C-	-C50C-	1C55D	D	hostportc                     ddl }	 |                    | pd|fd          5  	 ddd           dS # 1 swxY w Y   dS # t          $ r Y dS w xY w)u   True when something is accepting TCP connections at host:port.

    Any listener counts — even a 401 response proves a dashboard is up.
    Used by the unified profile-launch routing to decide attach-vs-start.
    r   Nr  r&
  r(  TF)socketcreate_connectionrM   )r  r   r"  s      r   _dashboard_listeningr$  -  s     MMM%%t':{D&A3%OO 	 		 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	   uus'   = 0= 4= 4= 
A
Ac                 (	   t          | dd          pd}	 ddlm}  ||          sdS n# t          $ r Y dS w xY w	 ddlm}  |            rdS n# t          $ r Y dS w xY wt          j                                        rt          j	                                        sdS t                       t          d| d           t          d	           t                       t          d
           t          d           t          d           t          d           t                       	 t          d                                          pd}n:# t          t          f$ r& t          d           t          j        d           Y nw xY w|dk    r1t                       t          d           t          j        d           |dvr#t          d           t          j        d           ddl}ddl}t                       	 t          d                                          pd}|                    d          }|                    d          }	n:# t          t          f$ r& t          d           t          j        d           Y nw xY w|s#t          d           t          j        d           ||	k    r#t          d           t          j        d           	 ddlm}
 n=# t          $ r0}t          d|            t          j        d           Y d}~nd}~ww xY w |
|          }|                    d          }	 ddlm}m}  |            }|                    d i           }|                    d!i           }||d"<   ||d#<   d$|d%<   t3          |                    d&d$          pd$                                          s||d&<    ||           n=# t          $ r0}t          d'|            t          j        d           Y d}~nd}~ww xY w	 dd(lm}  |d)*           n*# t          $ r}t          d+| d,           Y d}~nd}~ww xY wt                       t          d-| d.           t          d/           t          d0           t                       dS )1u/  Offer to configure dashboard auth when a non-loopback bind has none.

    Called from ``cmd_dashboard`` just before ``start_server``. The auth
    gate engages on every non-loopback bind (``--insecure`` is a no-op since
    the June 2026 hardening), and ``start_server`` fails closed when no
    ``DashboardAuthProvider`` is registered. Rather than greet an interactive
    operator with that hard error, prompt them to set up the bundled
    username/password provider on the spot — or point them at
    ``hermes dashboard register`` for OAuth.

    No-ops (so the existing fail-closed ``SystemExit`` remains the backstop)
    when:
      * the bind is loopback (gate never engages), or
      * a provider is already registered, or
      * stdin/stdout isn't a TTY (Docker/s6, CI, piped ``--no-open`` runs).
    r  r  r   )should_require_authN)list_providersu8   ⚠ The dashboard is binding to a non-loopback address (z) and needs an auth provider.zX  Non-loopback binds always require authentication (--insecure no longer bypasses this).z0  How do you want to authenticate the dashboard?z?    [1] Username & password (quickest; for a trusted LAN / VPN)z?    [2] OAuth via Nous Portal (run `hermes dashboard register`)z    [3] Cancelz  Choice [1]: rD   z
  Cancelled.rB   r  aR    Run this on the host where the dashboard lives, then start the dashboard again:
    hermes dashboard register
  It provisions a Nous Portal OAuth client and writes HERMES_DASHBOARD_OAUTH_CLIENT_ID into ~/.hermes/.env for you.
  Docs: https://hermes-agent.nousresearch.com/docs/user-guide/features/web-dashboard#authentication-gated-mode)rD   z  Cancelled.z  Username [admin]: adminz  Password: z  Confirm password: u"     ✗ Empty password — aborting.u)     ✗ Passwords don't match — aborting.)hash_passwordu,     ✗ Could not load the password provider: r  r  r  
basic_authusernamepassword_hashrR   passwordsecretu#     ✗ Failed to write config.yaml: discover_pluginsTr  u"     ⚠ Plugin re-discovery failed (zS); the gate may still fail closed. Set the password again or restart the dashboard.u/     ✓ Username/password auth configured (user: r5	  z4    Saved to config.yaml under dashboard.basic_auth.z4    Sign in at the dashboard with these credentials.)r0   hermes_cli.web_serverr&  r   hermes_cli.dashboard_authr'  rE   r   rK   r  r|   r)  r5   r+  r*  r   getpasssecretsplugins.dashboard_auth.basicr)  token_urlsafer  r  r  r`  r4   r(   r  r0  )rx  r  r&  r'  r  r3  r4  r+  r-  confirmr)  r   r,  r.  r  r  r  dashbasicr0  s                       r   )_maybe_setup_dashboard_auth_interactivelyr:  -  s   " 4--<D======""4(( 	F	   <<<<<<> 	F	   
 I 3:#4#4#6#6 	GGG		#4 	# 	# 	#   
	0   
GGG	
<===	
KLLL	
KLLL	
	GGG'((..007C'(    }}J	
 	
 	
 	Vn NNNNNN	GGG/006688CG??>22//"899'(     233379:::>>>>>>>   BSBBCCC "M(++M""2&&F>>>>>>>>kmm~~k2..b11$j!.oj599Xr**0b117799 	%$E(OC   9C99:::O777777t$$$$$ O O O N3 N N N 	O 	O 	O 	O 	O 	O 	O 	OO 
GGG	
HH
H
H
HIII	
@AAA	
@AAA	GGGGGs   ) 
77A 
AA&#E
 
4F F9AI 4I>=I>K 
L!&LL5BO
 

P&O??PP 
Q%P==Qc                    t          | dd          r*t                      }t          j        |dk    rdnd           t          | dd          rit	                      }|s#t          d           t          j        d           t          d           t	                      }t          j        |rdnd           	 dd	lm}  |            }n# t          $ r d
}Y nw xY w|dvrit          | dd          sWt          | dd          sEt          j                            d          dk    r!d| j        pd d| j         d| }t          | j        | j                  rst          d| j         d           t          d| d|            | j        s+	 ddl}|                    |           n# t          $ r Y nw xY wt          j        d           t          d| d           t          j        dddd
dd t)          | j                  d!| j        d"|g}| j        r|                    d#           t          | d$d          r|                    d%           t          | d&d          r|                    d'           t          j                                        }		 dd(lm}
 t)           |
                      |	d)<   n&# t          $ r |	                    d)d           Y nw xY wt          j        d*k    r=t7          j        ||	+          }t          j        |                                           n t          j        t          j        ||	           	 dd,lm }  |d-.           n# t          $ r Y nw xY w	 ddl!}ddl"}nq# tF          $ rd}t          d/           t          d0tH           d1t          j         d2           t          d3|            t          j        d           Y d}~nd}~ww xY wtK                       d4t          j        vr?t          | d&d          s.tM          tH          d5z  d67          st          j        d           nt          | d&d          rd4t          j        v rtO          t          j        d4                   ntH          d8z  d9z  }|d:z  (                                sDt          d;|            t          d<           t          d=           t          j        d           t          d>|            	 dd?l)m*}  |             n5# t          $ r(}t          d@| t          j+        A           Y d}~nd}~ww xY w	 ddBl,m-}  |t\          dCD           n,# t          $ r t\          /                    dEd6F           Y nw xY wddGl0m1} te          |             || j        | j        | j         t          | d$d          t          | dd          pdH           dS )IzGStart the web UI server, or (with --stop/--status) manage running ones.r  Fr   stopr  zrequested via --stop)r\  rB   )r
  r   )r   r  isolatedopen_profilerR   HERMES_DESKTOPrD   r-  r  r?  z
/?profile=z*Machine dashboard already running on port r  z  Managing profile 'r
  Nz+Routing to the machine dashboard (profile 'zB' preselected). Use --isolated for a dedicated per-profile server.r   r	  r   r  z--portz--hostz--open-profilez	--no-openinsecurez
--insecurer  z--skip-buildr   r   r  r2  r  r  r  z;Web UI dependencies not installed (need fastapi + uvicorn).zMRe-install the package into this interpreter so metadata updates apply:
  cd rW  zP -m pip install -e .
If `pip` is missing in this venv, use:  uv pip install -e .zImport error: HERMES_WEB_DISTrV  TrL  rw   r   r  u6   ✗ --skip-build was passed but no web dist found at: zG  Pre-build first:  npm install --workspace web && npm run build -w webz.  Or drop --skip-build to build automatically.u8   → Skipping web UI build (--skip-build); using dist at r/  u   ⚠ Plugin discovery failed: r   start_background_mcp_discoveryzdashboard-mcp-discoveryr]  thread_namez9Background MCP tool discovery failed at dashboard startupr3  )start_server)r  r   open_browserallow_publicinitial_profile)3r0   r  rE   r   r>  r|   rw  r   r
  r   r&   r'   r(   r  r   r$  no_open
webbrowserr.   ra  r4   r  r\  r   r   rb  r   rA  r-  r1  execvper  r  fastapiuvicornr   rQ  ru  r_  r   r,   r  r0  r   hermes_cli.mcp_startuprC  r]  r^  r1  rF  r:  )rx  r  rr  rt	  r
  _launch_profiler  rK  reexec_argvr,  r   r$  r  rM  rN  r  
_dist_rootr0  r   rC  rF  s                        r   cmd_dashboardrS  .  sg    tXu%% )(**eqjja((( tVU## 
()++ 	:;;;HQKKK'/EFFFF /00	i&Q'''$??????1133 $ $ $#$ 	444j%00 	5nb11 	5 JNN+,,33Y	0[YY49YYYY	4955 		KtyKKKLLLBBBSBBCCC< %%%%OOC((((    DHQKKKP/ P P P	
 	
 	

 ND"3)c$)nndio
 < 	,{+++4U++ 	-|,,,4u-- 	/~...joo	)@@@@@@!$%<%<%>%>!?!?C 	) 	) 	) GGM4(((((	) <7""#KS999DHTYY[[!!!!Js~{C888FFFFFF&&&&&    	 	 	KLLLJ J JJ J J	
 	
 	
 	"q""###	 !"""
**74u3U3U*\E1>>> 	HQKKK	|U	+	+ W !BJ.. -.///,z9 	
 \)1133 	W:WWXXX[\\\BCCCHQKKKUUUVVVF777777 F F F 	3c33#*EEEEEEEEE	F
IIIIII&&1	
 	
 	
 	
 	
  
 
 
G 	 	
 	
 	
 	
 	

 322222 .d333
 LYY%T:u55nb99?R     s   7C CCF' '
F43F4 J5 5 KK	M 
M)(M)-M6 6
O$ AOO$;T 
T>T99T>U &VVc                 (    ddl m}  ||            dS )z?Register a self-hosted dashboard OAuth client with Nous Portal.r   )cmd_dashboard_registerN)hermes_cli.dashboard_registerrU  rx  _impls     r   rU  rU  S/  s%    MMMMMM	E$KKKKKrH   c                 (    ddl m}  ||            dS )z4Enroll a self-hosted gateway with a relay connector.r   )cmd_gateway_enrollN)hermes_cli.gateway_enrollrZ  rW  s     r   rZ  rZ  Z/  s%    EEEEEE	E$KKKKKrH   c                     ddl m}m}m} t	          | dd          }|dk    rt           ||                     dS |dk    rt           ||                     dS t           ||                     dS )zPrint shell completion script.r   )generate_bashgenerate_zshgenerate_fishshellr  zshfishN)hermes_cli.completionr]  r^  r_  r0   r|   )rx  parserr]  r^  r_  r`  s         r   cmd_completionre  a/  s    PPPPPPPPPPD'6**E~~ll6""#####	&mmF##$$$$$mmF##$$$$$rH   c                 (    ddl m}  ||            dS )z?Show a byte/char breakdown of the system prompt + tool schemas.r   cmd_prompt_sizeN)hermes_cli.prompt_sizerh  rW  s     r   rh  rh  n/  s%    ??????	E$KKKKKrH   c                 8   ddl m}m} t          | dd          pd}|dk    r |             dS  ||t          | dd          t          | d	d
          t          | dd          t          | dd          t          | dd          t          | dd                     dS )z!View and filter Hermes log files.r   )tail_log	list_logslog_namer)  r  Nrz  r!  followFlevelrl  since	component)	num_linesrn  ro  rl  rp  rq  )hermes_cli.logsrk  rl  r0   )rx  rk  rl  rm  s       r   cmd_logsrt  u/  s    33333333tZ11<WH6	H$,,tXu--dGT**i..dGT**$T22     rH   c                 "    ddl m}  |            S )z%Open the safe Hermes command console.r   )run_console_repl)hermes_cli.console_enginerv  )rx  rv  s     r   cmd_consolerx  /  s#    ::::::rH   c                  X    	 ddl m}  dgd | D             z   S # t          $ r g dcY S w xY w)zDBuild the --provider choices list from CANONICAL_PROVIDERS + 'auto'.r   )r  r  c                     g | ]	}|j         
S rN   r'  r%  s     r   r  z+_build_provider_choices.<locals>.<listcomp>/  s    ///a16///rH   )r  r  r;  r<  r=  r?  r@  rA  rM  rE  rJ  rD  rF  rX  rU  rK  rB  rZ  rC  rQ  rV  rS  novitarO  rL  rN  rR  rP  r>  rY  rW  )r  r  r   )_cps    r   _build_provider_choicesr}  /  sn    
@@@@@@x//3///// 
 
 

 
 
 	
 	
 	

s    ))>?   prompt-sizer
  r  lspr   r:  r  r  r
  r
  r
  helpr	  petssendr^  hooksr
  r  proxyr  r
  slackr  r}  r`  r
  r
  kanbanr
  r
  portalrP  r  r  bundlesconsoler  r  r	  journeymigrater
  r
  r	  r!  r4  r]   r
  fallbackr
  learningr  r  r  r  r  r
  r,  postinstallcomputer-usememory-graphr  >   r   r   r   r   r   r   r   r   r   r   r   r   r   c                  R   t           j        dd         } d}|t          |           k     r}| |         }|dk    r#|dz   t          |           k     r| |dz            S dS |                    d          r5d|v r|dz  }c|t          v r|dz   t          |           k     r|dz  }|dz  }|S dS )u  Return the first non-flag, non-flag-value token in ``sys.argv[1:]``.

    Used by ``main()`` to decide whether plugin discovery has to run at
    argparse-setup time. Handles common invocations like
    ``hermes -m gpt5 --provider openai chat "msg"`` by skipping the
    values attached to known top-level flags.

    Does NOT fully simulate argparse — unknown ``--foo=bar`` / ``--foo
    bar`` flags degrade gracefully (``bar`` may be wrongly classified as
    a positional, which at worst forces a one-time plugin discovery).
    rB   Nr   r   r   rb   r   )rE   r@   r   rW   _TOP_LEVEL_VALUE_FLAGS)r@   r   toks      r   _first_positional_argvr  /  s     8ABB<D	A
c$ii--1g$;;1us4yy  AE{"4>># 		czzQ,,,QT1B1BQFA
4rH   c                  @    t                      } | dS | t          v rdS dS )a  True when the CLI might be invoking a plugin-registered subcommand.

    Returning False lets ``main()`` skip plugin discovery entirely during
    argparse setup, saving ~500-650ms per invocation for users whose
    enabled plugins don't contribute any CLI command.
    NFT)r  _BUILTIN_SUBCOMMANDS)r  s    r   _plugin_cli_discovery_neededr  /  s1     #$$E}u$$$u 4rH   >   Nrlr
  r  rw  rB  tickr  
mcp_actionr  )r
  r	  r   c                     t          t          | dd          p"t          j                            d          dk              S )Nr%   FrC   rD   )rV   r0   r&   r'   r(   r  s    r   _is_tui_chat_launchr  0  s4    eU++Rrz~~l/K/Ks/RSSSrH   c                     | j         dk    rdS | j         dk    rt          | dd           dk    rdS | j         dk    rt          | dd           dv rdS d	S )
Nr
  Tr	  r  rB  r
  rw  >   rB  r  F)r  r0   r  s    r   "_command_has_dedicated_mcp_startupr  0  sj    |ut|y  WT3Dd%K%Ku%T%Tt|v'$"E"E"X"Xt5rH   c                 6    t          |           rdS | j        dv S )NF>   Nr  r  )r  r  r  s    r   _should_background_mcp_startupr  '0  s%    4   u<///rH   c                 f   t                               | j        d          \  }}| j        t          v s|rt	          | |d          |v sdS t          t	          | dd                    }	 ddlm}  |             n,# t          $ r t          
                    dd	           Y nw xY wd}t          |           rd}nit          |           rd}nWt          |           rH	 dd
lm}  |t          d           n,# t          $ r t                              dd	           Y nw xY wd}|r>	 ddlm}  |             n,# t          $ r t                              dd	           Y nw xY w	 ddlm} ddlm}	  |	 |            |           dS # t          $ r  t                              dd	           Y dS w xY w)zCDiscover plugins/MCP/hooks for commands that can run an agent turn.)NNNr/  Fr   r/  z&plugin discovery failed at CLI startupTr3  rB  zcli-mcp-discoveryrD  z3Background MCP tool discovery failed at CLI startup)discover_mcp_toolsz(MCP tool discovery failed at CLI startuprw  )register_from_config)r/  z-shell-hook registration failed at CLI startup)_AGENT_SUBCOMMANDSr(   r  _AGENT_COMMANDSr0   rV   r  r0  r   r]  re  r  r  r  rO  rC  r^  tools.mcp_toolr  r  r  agent.shell_hooksr  )
rx  	_sub_attr_sub_set_accept_hooksr0  _run_inline_mcp_discoveryrC  r  r  r  s
             r   _prepare_agent_startupr  -0  s   ,00|LLIx'' 	(!$	488HDD~u==>>M
777777 
 
 
4 	 	
 	
 	
 	
 	


 !%4   * %*!!	+D	1	1 * %*!!	'	-	- *	MMMMMM**/      	 	 	LLE      	
 %*!  
	 :99999     	 	 	LL:      	
	
111111::::::[[]]GGGGGG 
 
 
; 	 	
 	
 	
 	
 	
 	

sH   (A9 9&B"!B"C4 4&DD%D6 6&EE#!F &F0/F0c                 X    dD ]&\  }}t          | |          st          | ||           'd S )N)r  Nr  Nr  Nr  N)r)  Fr|  Nr{  Nr+  F)r   setattr)rx  r  r   s      r   _set_chat_arg_defaultsr  p0  sH    	 ) )g tT"" 	)D$((() )rH   c                     t                      sdS t          j                            d          dk    rdS t          j        dd         } d| v sd| v rdS t          |           rdS t          |           rt          d           d	S t                      }t          d
 | D                       }|s|dvrdS ddlm}  |            \  }}}|                    t                     |                    t!          |                     }t#          |dd          rt          d           d	S t#          |dd          rht%          |           ddlm} t	          j         ||j        t#          |dd          t#          |dd          t#          |dd                               |j        s|j        r|j        d|_        |j        dv rt5          |           t#          |dd           ot#          |dd           }	|	rPt7          |dd	           dt          j        d<   dt          j        d<   t#          |dd          rdt          j        d<   nt%          |           t          |           d	S dS )zHRun obvious Termux non-TUI chat/oneshot/version paths on a light parser.Fr   rD   rB   N-h--helpr  Tc              3   X   K   | ]%}|d k    p|dk    p|                     d          V  &dS )r   r   z
--oneshot=Nr
  r  s     r   r  z._try_termux_fast_cli_launch.<locals>.<genexpr>0  sU         	tIsk)IS^^L-I-I     rH      Nr  r   build_top_level_parserfuncr]   oneshotrun_oneshotr  r  r  r  r  r  r  r  r*  r  HERMES_DEFER_AGENT_STARTUPHERMES_FAST_STARTUP_BANNERr/  rO  )r/  r&   r'   r(   rE   r@   rG   r^   r  r  r  hermes_cli._parserr  set_defaultsr  
parse_argsr
  r0   r  r  r  r   r  r|  r{  r  r  r  )
r@   r  has_oneshotr  rd  _subparserschat_parserrx  r  interactive_prompts
             r   _try_termux_fast_cli_launchr  0  s   )++ u	z~~6773>>u8ABB<Dt||x4''u  u#D)) %0000t"$$E      K  566u999999'='='?'?$FK(+++8>>??DtY&& %0000ttY%% 
t$$$222222KdGT22 z488 z488	  	
 	
 	
 	 t) t|/C|~%%t$$$!(w!=!==bgdT[]aFbFbBb 		) D)T***7:BJ347:BJ34t^U33 847
01"4(((t5rH   c                  l   t                      sdS dt          j        dd         v sdt          j        dd         v rdS t          t          j        dd                   } | sdS t	                      }|dvrdS ddlm}  |            \  }}}|                    t          	           |	                    t          t          j        dd                             }t          |d
d          st          |dd          rdS t          |dd          dvrdS t          |          sdS t          |           dS )a  Launch obvious Termux TUI invocations before building every subparser.

    `hermes --tui` is the hot path on phones. The full parser setup imports
    command modules for model, fallback, migrate, kanban, bundles, plugins,
    etc. even though the TUI immediately execs Node. On Termux only, parse the
    lightweight top-level/chat parser and hand off to ``cmd_chat`` when the
    invocation is unambiguously the built-in TUI/chat path.
    Fr  rB   Nr  r  r   r  r  r]   r  r  T)r/  rE   r@   rG   r  r  r  r  r  r  r
  r0   ry  )	wants_tuir  r  rd  r  r  rx  s          r   _try_termux_fast_tui_launchr  0  sW    *++ usx|x38ABB<77u !""..I u"$$EN""u999999'='='?'?$FK(+++8!""FFGGD tY&& '$	4*H*H utY%%^;;uD!! uTNNN4rH   c           
         t          | dd           }|dk    ruddlm}m}  |            }t	          |                    d          t                    si |d<   d|d         d<    ||           t          d           t          d	           d S |d
k    rddlm	}m
}  |            dz  t          | dd          }g }|dv r|                    d           |dv r|                    d           fd|D             }	|	st          d |             d           d S t          d           |	D ]=\  }
}|
z  }|                                j        }t          d|
 d| d|dd           >t          | dd          ss	 t          d                                                                          }n'# t"          t$          f$ r t          d           Y d S w xY w|dk    rt          d            d S |	D ]2\  }
}|
z                                   t          d!|
 d| d"           3t          d#           t          d$ |             d           d S dd%lm}  ||            d S )&Nmemory_commandrN  r   r  r
  rR   r  u%   
  ✓ Memory provider: built-in onlyz  Saved to config.yaml
r  rX	  memoriesr~  r  >   r  r
  )z	MEMORY.mdzagent notes>   r  r
  )zUSER.mdzuser profilec                 L    g | ] \  }}|z                                   ||f!S rN   rb  )r  r  r  mem_dirs      r   r  zcmd_memory.<locals>.<listcomp>	1  sF     
 
 
!!T!7K7K7M7M
I
 
 
rH   u1   
  Nothing to reset — no memory files found in z/memories/
z:
  This will permanently erase the following memory files:u       ◆ rq   u   ) — r  z bytesr  Fz
  Type 'yes' to confirm: z
  Cancelled.
z  Cancelled.
u     ✓ Deleted rr   zE
  Memory reset complete. New sessions will start with a blank slate.z  Files were in: )r  )r0   r  r  r  r2   r(   r3   r|   r   r   rb  r  rR  rT  r)  r5   r6   r+  r*  rd  hermes_cli.memory_setupr  )rx  r  r  r  r`  r   rb  r~  files_to_resetr  r  r  r)   sizer  r  r  s                   @r   
cmd_memoryr  0  sR   
$($
/
/C
e||>>>>>>>>&**X..55 	"!F8')x$F6777()))))	IIIIIIII!/##j0x//&&&!!">???_$$!!"=>>>
 
 
 
%3
 
 
  	hEXEXEZEZhhh   FKLLL 	> 	>GAtQ;D99;;&D<Q<<$<<d<<<<====tUE** 	<==CCEEKKMM/0   ())) &''' 	1 	1GAtq[  """/1/////0000T	
 	
 	
 	E"5"5"7"7EEEFFFFF::::::ts   =3F1 1 GGc                 `   	 ddl m} g }t          | dd          r|                    d           t          | dd          r|                    d           t          | dd          r|                    d	           t          | d
d          r|                    d           t          | dd          r|                    d            ||           dS # t          $ rN t          dt          j                   t          dt          j                   t          j        d           Y dS w xY w)z%Launch Hermes Agent as an ACP server.r   r  acp_versionFr\   r.  z--checkr
  z--setupsetup_browserz--setup-browserr3
  --yeszACP dependencies not installed.r   z+Install them with:  pip install -e '.[acp]'rB   N)	acp_adapter.entryr  r0   r  r   r|   rE   r   r   )rx  acp_mainacp_argvs      r   cmd_acpr  01  sM   6666664.. 	)OOK(((4%(( 	'OOI&&&4%(( 	'OOI&&&4%00 	/OO-...4u-- 	%OOG$$$   /cjAAAA;#*MMMMs   CC AD-,D-c                     t          | dd           }|dv rddlm}  ||            d S |dk    r%ddlm} t	          j         ||                      d S t          d           ddlm}  ||            d S )	Ntools_action>   r  enabledisabler   )tools_disable_enable_commandz
post-setup)run_post_setup_commandr  )tools_command)r0   r0
  r  r  rE   r   r   r  )rx  r
  r  r  r  s        r   	cmd_toolsr  G1  s    T>400F...HHHHHH$$T*****	<		BBBBBB''--.....W999999drH   c                 L   	 ddl m} ddlm}  |            } ||          }|                    | j        | j                  }t          |                    |                     |	                                 d S # t          $ r}t          d|            Y d }~d S d }~ww xY w)Nr   r1  )InsightsEngine)rI  r  zError generating insights: )r4  r2  agent.insightsr  generaterI  r  r|   format_terminalr6  r   )rx  r2  r  r7  enginereportr  s          r   cmd_insightsr  X1  s    
1******111111Y[[##diDDf$$V,,---





 1 1 1/A//0000000001s   A8A< <
B#BB#c                     t          | dd           dk    r"t          d           ddlm}  ||            d S ddlm}  ||            d S )Nskills_actionr`  zskills configr   )skills_command)r0   r   hermes_cli.skills_configr  hermes_cli.skills_hub)rx  skills_config_commandr  s      r   
cmd_skillsr  f1  sx    t_d++x77_%%%TTTTTTd#####888888trH   c                 (    ddl m}  ||            d S )Nr   )pairing_command)hermes_cli.pairingr  )rx  r  s     r   cmd_pairingr  s1  s*    222222ODrH   c                 (    ddl m}  ||            d S )Nr   )plugins_command)hermes_cli.plugins_cmdr  )rx  r  s     r   cmd_pluginsr   y1  s*    666666ODrH   c                 (    ddl m}  ||            d S )Nr   )mcp_command)hermes_cli.mcp_configr  )rx  r  s     r   cmd_mcpr  1  s*    111111KrH   c                 (    ddl m}  ||            d S )Nr   )claw_command)hermes_cli.clawr  )rx  r  s     r   cmd_clawr  1  s*    ,,,,,,LrH   c                  %  KLMNOPQ t                       	 ddlm}   |              n# t          $ r Y nw xY w	 t	                       n# t          $ r Y nw xY w	 dt
          j        dd         vrt                       n# t          $ r Y nw xY wt                      rdS t                      rdS ddl
m}  |            \  O}}|                    t                     t          |t                     dd	lm} |                    d
dd          }|                    d          }|                    ddgd           |                    ddgd          }|                    ddd           |                    ddgd          }|                    dd           |                    |           dd lm}	 |                    d!d"d#          }
|
                    d$          }|                    ddgd%           |                    d&d'           |                    d(dgd)           |                    d*d+           |
                    |	           |                    d,d-d.          PP                    d/          }|                    d0d1gd2          }dd3lm} |                    |           Pfd4}P                    |           dd5lm}m} |                    d6d7d8          }|                    d9          }|                    d:d;d<          }|                    d=d>d?@           |                    dAd>dB@           |                    |           |                    |           t;          |t<          t>          t@          C           	 ddDl!m"}  ||           n2# t          $ r%}tF          $                    dE|           Y d}~nd}~ww xY wtK          |tL          F           tO          |tP          G           tS          |tT          H           |                    dIdJdK          }|                    tV                     tY          |tZ          L           ddMl.m/}  ||           ta          |tb          N           te          |tf          O           ti          |tj          P           tm          |tn          Q           tq          |tr          R           tu          |tv          S           ddTl<m}  ||           ddUl=m>}  ||          }|                    t~                     ddUl@m>}  ||          }|                    t                     t          |t          V           t          |t          W           t          |t          X           t          |t          Y           t          |t          Z           t          |t          [           |                    d\d]d^          }dd_lNm}  ||           t          |t          `           t          |t          a           t          |t          b           t          |t          c           t          |t          d           |                    dedfdg          } ddhlYm}!mZ}"  |!|            |                     |"           t          |t          i           t                      r	 ddjl^m_}# ddkl`ma}$mb}% t                      }& |#            D ]}'|                    |'d         |'dl         |'d                    dmdn          t          do          jf        p          }( |'dq         |(           |'d                    dr          |(                    |'dr                    |&g                    |'d                     |$              |%            jh        i                                D ]}'|'d         |&v r|                    |'d         |'dl         |'d                    dmdn          t          do          jf        p          }( |'dq         |(           |'d                    dr          |(                    |'dr                    nD# t          $ r7})t          jk        t                    $                    ds|)           Y d})~)nd})~)ww xY w|                    dtdudv          }*	 dd_lmm}+  |+|*           nD# t          $ r7})t          jk        t                    $                    dw|)           Y d})~)nd})~)ww xY w|                    dxdydz          },	 dd_lnm}-  |-|,           nD# t          $ r7})t          jk        t                    $                    d{|)           Y d})~)nd})~)ww xY w|                    d|d}d~gdd          }.	 dd_lom}/  |/|.           nD# t          $ r7})t          jk        t                    $                    d|)           Y d})~)nd})~)ww xY wt          |t                     t          |t                     |                    ddd          MM                    d          }0|0                    dd          }1|1                    dd>d@           |0                    dd           |0                    ddd          }2|2                    ddg dd           |2                    ddg dd           |2                    dd>d@           |0                    ddd          NN                    d          }3|3                    dd          }4|4                    dd>d@           |3                    dd           MNfd}5M                    |5           t          |t                     |                    ddd          QQ                    d          }6|6                    dd          }7|7                    dd           |7                    dt          dd           |6                    dd          }8|8                    dd           |8                    dd           |8                    dd           |6                    dd          }9|9                    dd           |9                    ddd>d@           |6                    dd          }:|:                    dt          dd           |:                    dd¬           |:                    ddd>d@           |6                    ddĬ           |6                    dddǬ          };|;                    dd>dɬ@           |;                    dAd>dʬ@           |6                    dd̬           |6                    ddά          }<|<                    ddϬ           |<                    dddҬ           |6                    ddԬ          }=|=                    dd           |=                    dt          dd֬           dt          dt          fdلKKQfdڄ}>Q                    |>           t          |t          ۦ           t          |t          ܦ           t          |t          ݦ           t          |t           ަ           t          |t          ߦ           t          |t                     t          |t                     |                    dd          }?|?                    dddg dd           |?                    Ofd           t          |t          t                     t          |t                     t          |t                     t          |t                     ddlm}@  |@            }A|Ar7t%          |At
          j        dd                    t          j        d           t)          t
          j        dd                   }Bddl}Ct-          |d          r&t          |j                                                  nt                      Lt3          Lfd|BD                       }D|Drd|_        t
          j        }E	 |C                                t
          _        O                    |B          }F|Et
          _        nh# t<          $ r>}G|Et
          _        |Gj        dk    r d|_        O                    |B          }FY d}G~Gn$d}G~Gww xY wd|_        O                    |B          }F|Fj        rt          |F           dS tC          |F           tE          |Fdd          r\ddlm}H t          j         |H|Fj        tE          |Fdd          tE          |Fdd          tE          |Fdd                               |Fj        s|Fj        rJ|Fj        Cd|F_        dD ](\  }I}Jt-          |F|I          stS          |F|I|J           )t          |F           dS |Fj        <dD ](\  }I}Jt-          |F|I          stS          |F|I|J           )t          |F           dS t-          |Fd          r|F                    |F           dS O                                 dS )z Main entry point for hermes CLI.r   )configure_windows_stdior  rB   Nr  r  )r  )cmd_moar:  z0Configure Mixture of Agents provider/model slotsz7Configure the provider/model set used by /moa <prompt>.)r  r*  moa_command)destr  lszShow current MoA model slots)aliasesr  	configurer`  zInteractively pick MoA modelsr   rr  zPreset name to create or update)nargsr  r
  rmzDelete a MoA presetzPreset name to delete)r  )cmd_fallbackr  z>Manage fallback providers (tried when the primary model fails)zManage the fallback provider chain.  Fallback providers are tried in order when the primary model fails with rate-limit, overload, or connection errors.  See: https://hermes-agent.nousresearch.com/docs/user-guide/features/fallback-providersfallback_commandz<Show the current fallback chain (default when no subcommand)r   zOPick a provider + model (same picker as `hermes model`) and append to the chainr
  z&Pick an entry to delete from the chainr  zRemove all fallback entriesr4  z:Manage external secret sources (Bitwarden Secrets Manager)zPull API keys from an external secret manager at process startup instead of storing them in ~/.hermes/.env.  Currently supports Bitwarden Secrets Manager.  See: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/bitwardensecrets_command	bitwardenbwz%Bitwarden Secrets Manager integration)secrets_clic                     t          | dd           }t          | dd           }|dv r||                     |           S                                  dS )Nr  secrets_bw_command)r  r  r   )r0   r  
print_help)rx  r  bw_subsecrets_parsers      r   _dispatch_secretszmain.<locals>._dispatch_secrets2  s_    d-t443T::%%%&*<99T??"!!###qrH   )cmd_migratecmd_migrate_xair  z?Migrate configuration for retired models or deprecated settingszxDiagnose and (optionally) rewrite the active config.yaml to replace references to retired models or deprecated settings.migrate_typerJ  z;Migrate xAI models scheduled for retirement on May 15, 2026zScan config.yaml for references to xAI models retiring on May 15, 2026 and, with --apply, rewrite them in-place to the official replacements per the xAI migration guide. The original config.yaml is backed up before any rewrite.z--apply
store_truez:Rewrite config.yaml in-place (default: dry-run, no writes))r
  r  z--no-backupz8Skip the timestamped backup of config.yaml when applying)r  r  rZ  )register_subparserzLSP CLI registration failed: %s)r  )r  )r  r  z.Set up WhatsApp Business Cloud API integrationzConfigure the official Meta WhatsApp Business Cloud API adapter (Business account required, public webhook URL required). Distinct from `hermes whatsapp` which sets up the Baileys bridge for personal accounts.)r  )register_send_subparser)rj  )rm  )rq  )ru  )ry  )r}  )
add_parser)build_parser)r  )r  )r  )r  )r  )r  r,  z.Inspect / prune / clear ~/.hermes/checkpoints/u   Manage the filesystem checkpoint store — the shadow git repo hermes uses to snapshot working directories before write_file/patch/terminal calls. Lets you see how much space checkpoints occupy, force a prune, or wipe the base.)register_cli)r  )r  )rx  )r  )r  r  zDCreate, list, and manage skill bundles (aliases for multiple skills)zSkill bundles let you load several skills under one slash command. `/<bundle>` from the CLI or gateway loads every referenced skill at once.)r'  bundles_command)r   )discover_plugin_cli_commands)r0  get_plugin_managerr  r*  rR   argparse)r  r*  formatter_classsetup_fn
handler_fnzPlugin CLI discovery failed: %sr  uB   Background skill maintenance (curator) — status, run, pause, pina  The curator is an auxiliary-model background task that periodically reviews agent-created skills, prunes stale ones, consolidates overlaps, and archives obsolete skills. Bundled and hub-installed skills are never touched. Archives are recoverable; auto-deletion never happens.zcurator CLI wiring failed: %sr  z0Browse, install, and select petdex animated petszPetdex (https://github.com/crafter-station/petdex) is a public gallery of animated sprite pets for coding agents. Install one and Hermes shows it reacting to agent activity across the CLI, TUI, and desktop app.zpets CLI wiring failed: %sr  r  r  z/Timeline of learned skills + memories over timezA terminal rendition of the desktop Star Map / Memory Graph: a timeline bar chart of learned skills and memories over time (oldest at top, newest at bottom) plus a playable constellation scrubber. Mirrors the TUI `/journey` overlay and the desktop panel.)r  r  r*  zjourney CLI wiring failed: %s)r  )r  r  zBManage the Computer Use (cua-driver) backend (macOS/Windows/Linux)aw  Install or check the cua-driver binary used by the
`computer_use` toolset. Supported on macOS, Windows, and
Linux.

Use `hermes computer-use install` to fetch and run the
upstream cua-driver installer. This is equivalent to the
post-setup hook that `hermes tools` runs when you first
enable the Computer Use toolset, and is a stable target
for re-running the install if it didn't fire (e.g. when
toggling the toolset on a returning-user setup).

Use `hermes computer-use doctor` to run cua-driver's
`health_report` MCP tool and surface its check matrix
(TCC, bundle identity, version, platform support, ...)
in human-readable form.computer_use_actionr  z=Install or repair the cua-driver binary (macOS/Windows/Linux)r  zRe-run the upstream installer even if cua-driver is already on PATH. The upstream install.sh always pulls the latest release, so this performs an in-place upgrade.r  z1Print whether cua-driver is installed and on PATHr
  z;Run cua-driver `health_report` and surface the check matrixu  Drive cua-driver's stable `health_report` MCP tool and render
its check matrix (TCC permissions, bundle identity, version,
platform support, screenshot probe, …) as human-readable
output. cua-driver owns the health model; this command stays
thin so new checks added upstream surface here without code
changes. Exits 0 when overall=ok, 1 when degraded/failed, 2
when the binary is missing or unreachable.z	--includer  CHECKzRun only the listed checks. Repeat for multiple (e.g. --include tcc_accessibility --include bundle_identity). Unknown names are reported by cua-driver.)r
  r   metavarr  z--skipzASkip the listed checks. Repeat for multiple. Wins over --include.z--jsonzEEmit the raw structured payload as JSON (same shape as `tools/call`).permissionsz=Check or grant macOS Accessibility + Screen Recording (macOS)uI  Computer Use drives the Mac through cua-driver, whose TCC grants
attach to cua-driver's own identity (com.trycua.driver) — not the
terminal or the Hermes app. `status` reports the driver's grant
state; `grant` launches CuaDriver via LaunchServices so the macOS
permission dialog is attributed to the process that does the work.computer_use_perms_actionz?Report Accessibility + Screen Recording grant state (read-only)z/Emit the normalized permission payload as JSON.grantz=Request the grants (opens the dialog attributed to CuaDriver)c                 j	   t          | dd           }|dk    r0ddlm}  |t          t          | dd                               d S |dk    rkdd l}dd l}dd	lm}  |            } |j        |          }|r d
}	 ddlm}	  |j	        |dgddd |	                      j
                                        }n# t          $ r Y nw xY w|rt          d| d| d           nt          d|            	 ddlm}
  |
            }|rO|                    d          r:|                    d          pd}t          d| d           t          d           n!|rt          d           nt          d           n# t          $ r t          d           Y nw xY wd S t          d           t          d           d S |dk    rddlm}  |t'          t          | d g           pg           t'          t          | d!g           pg           t          t          | d"d                    #          }t)          j        |           |d$k    rWt          | d%d           }|d&k    r"dd'lm} t)          j         |                       |dk    rdd l}dd(lm}  |            }t          t          | d"d                    rCt          |                    |d)d*                     t)          j        |d+         rdnd,           |d-         s-t          d.|d/          d           t)          j        d,           |d0         s#t          d1           t)          j        d,           d2 }t          d3|d4         pd0 d|d/          d           |d5         r\t          d6 ||d7                    d8           t          d6 ||d9                    d:           |d+         st          d;           n+t          d6 ||d+                    d<|d/          d           |d=         D ]/}|d         d>k    r!t          d?|d@          dA|dB                     0|dC         rt          d?|dC                     t)          j        |d+         rdnd,                                            d S                                  d S )DNr/  r  r   r	  r	  Fr	  r  )_cua_driver_cmdrR   )_cua_driver_envr\   Tr  )r>  r9  r?  r,  zcua-driver: installed at rq   rr   )cua_driver_update_checkupdate_availablelatest_versionrr  u#     ⬆ Update available: cua-driver r  z.    Run: hermes computer-use install --upgradeu     ✓ Up to date.z:  Refresh to latest: hermes computer-use install --upgradezcua-driver: not installedz"  Run: hermes computer-use installr
  r  includer  r  )r;  r  json_outputr2  r3  r4  )request_permissions_grant)computer_use_statusr   )r  	sort_keysreadyrB   platform_supportedz!Computer Use is not supported on r   r  z;cua-driver: not installed. Run: hermes computer-use installc                     | du rdn| du rdndS )NTu   ✅Fu   ❌u   •rN   )r  s    r   rj  z0main.<locals>.cmd_computer_use.<locals>.<lambda>14  s    199%%AJJ55TY rH   zcua-driver: r]   	can_grantr  accessibilityz Accessibilityscreen_recordingz Screen Recordingz.  Grant: hermes computer-use permissions grantz) driver health (no permission toggles on checksrL  rx  r,  r  messager  )r0   r0
  r	  rV   rW  rA  r6  rX  r7  rB  r  r5   r   r|   tools.computer_use.cua_backendr8  r(   tools.computer_use.doctorr  r  rE   r   tools.computer_use.permissionsr=  r  r>  r  r  )rx  r
  r	  rW  rA  r6  
driver_cmdr)   r]   r7  r8  stlatestr  rl  perms_actionr=  r  r>  glyphr  computer_use_parsercomputer_use_permss                        r   cmd_computer_usezmain.<locals>.cmd_computer_use3  s   4d;;YBBBBBBtGD)U,K,K'L'LMMMMFXMMM?????? )**J6<
++D GGGGGG,jn{+'+$+O--   UUWW	 G
 !   D >HdHHgHHHIIII<d<<===XVVVVVV0022B \bff%788 \!#(8!9!9!@SMFMMMNNNNOOOO \12222 Z[[[  X X XVWWWWWX-...6777FX<<<<<<:WT9b99?R@@'$339r:: vu!=!=>>  D
 HTNNN]"""4)DdKKLw&&TTTTTT2244555x''$$$$NNNNNN((**fe4455 6%++bd+CCDDDH"W+4QQ1555./  ObnOOOPPPHQKKK+  WXXXHQKKKZZVR	]%AkVVR
^VVVWWWk? oIuuR%899IIIJJJOuuR(:%;<<OOOPPPg; PNOOOmuuR[11mm\^_i\jmmmnnnH E EA{d**CqzCCQy\CCDDDg; 202g;00111bk0q111))+++F&&(((((s%   ><B; ;
CC7BE: :FF)r  r  z<Manage session history (list, rename, export, prune, delete)z(View and manage the SQLite session storesessions_actionzList recent sessionsz--sourcez/Filter by source (cli, telegram, discord, etc.)z--limitr  zMax sessions to show)typer   r  r
  zExport sessions to a JSONL filer
  z)Output JSONL file path (use - for stdout)zFilter by sourcez--session-idzExport a specific sessionzDelete a specific sessionrp  zSession ID to deleter  z-yzSkip confirmationprunezDelete old sessionsz--older-thanZ   z/Delete sessions older than N days (default: 90)z$Only prune sessions from this sourceoptimizezAReclaim disk space: merge FTS5 segments + VACUUM (no data change)repairz>Repair a malformed state.db schema so hidden sessions reappearzRecover a state.db whose schema is malformed (e.g. 'table messages_fts already exists'), which makes Desktop/Dashboard show no sessions. A backup is made first; sessions and messages are preserved and the FTS search index is rebuilt if needed.z--check-onlyz@Only report whether the database opens cleanly; do not modify itz2Skip the timestamped backup copy (not recommended)statszShow session store statisticsr{  zSet or change a session's titlezSession ID to renamer  r	  zNew title for the sessionbrowseuB   Interactive session picker — browse, search, and resume sessionsrw  z#Max sessions to load (default: 500)rH  r   c                     	 t          |                                                                           dv S # t          t          f$ r Y dS w xY w)z?Prompt for y/N confirmation, safe against non-TTY environments.>   r  r  F)r)  r5   r6   r+  r*  r  s    r   _confirm_promptzmain.<locals>._confirm_prompt4  sX    	==&&((..00L@@+, 	 	 	55	s   47 AAc           
         dd l }| j        }|dk    rddlm}m}m} |}|                                st          d| d           d S  ||          }|t          d| d           d S t          d| d	|            t          | d
d          rd S t          d            ||t          | dd                     }|	                    d          r|	                    d          rt          d|d                     t          d|	                    d                      	 ddlm
}	  |	            j                            d                                          d         }
t          d|
 d           n# t          $ r t          d           Y new xY wt          d|	                    d                      |	                    d          rt          d|d                     t          d           d S 	 ddlm
}	  |	            }n*# t          $ r}t          d|            Y d }~d S d }~ww xY wt          | dd           }|rd ndg}|d k    r|                    | j        || j        !          }|st          d"           d S t%          d# |D                       }|r-t          d$d%d&d'd(d&d)d*d&d+            t          d,           n,t          d'd-d&d)d*d&d.d/d&d+            t          d0           |D ]}t'          |	                    d1                    }|r|	                    d2d3          d d4         n|	                    d2d3          d d5         }|rE|	                    d6          pd7d d8         }|d9         }t          |d%d&|d(d&|d*d&|            |d9         }t          |d-d&|d*d&|d         d/d&|            Րn|d:k    r| j        r
|                    | j                  }|st          d;| j         d<           d S |                    |          }|st          d;| j         d<           d S |                    |d=          d>z   }| j        d?k    r!t2          j                            |           nt9          | j        d@dAB          5 }|                    |           d d d            n# 1 swxY w Y   t          dC| j                    n|                    | j        D          }| j        d?k    r>|D ]9}t2          j                            |                    |d=          d>z              :nXt9          | j        d@dAB          5 }|D ]/}|                    |                    |d=          d>z              0	 d d d            n# 1 swxY w Y   t          dEt=          |           dF| j                    n|dGk    r|                    | j                  }|st          d;| j         d<           d S | j        s  +dH| dI          st          dJ           d S tA                      dKz  }|!                    ||L          rt          dM| dN           n-t          d;| j         d<           n|dOk    r| j"        }| j        rdP| j         dQnd3}| j        s# +dR| dS| dT          st          dJ           d S tA                      dKz  }|#                    || j        |U          }t          dV| dW           n|dXk    r|                    | j                  }|st          d;| j         d<           d S d&$                    | j%                  }	 |&                    ||          rt          d;| dY|            nt          d;| j         d<           n# tN          $ r}t          dZ|            Y d }~nd }~ww xY w|d[k    rt          | d\d]          pd]}t          | dd           }|rd ndg} |                    || |!          }|(                                 |st          d"           d S tS          |          }!|!st          dJ           d S t          d^|!            dd_l*m+}"  |"d`|!g           d S |dak    r|j,        }|                                r"tZ          j.        /                    |          dbz  ndc}#t          dd           	 |0                                }
n># t          $ r1}t          de|            |(                                 Y d }~d S d }~ww xY w|                                r"tZ          j.        /                    |          dbz  ndc}$|#|$z
  }%t          df|
 dg           t          dh|#didj|$didk|%didl           n|dmk    r|1                                }&|2                                }'t          dn|&            t          do|'            dpD ]4}(|1                    |(D          })|)dk    rt          dq|( dr|) ds           5|j,        }|                                r6tZ          j.        /                    |          dbz  }*t          dh|*didt           n,3                                 |(                                 d S )uNr   rX  )DEFAULT_DB_PATH_db_opens_cleanlyrepair_state_db_schemazNo session database at z (nothing to repair).u   ✓ u$    opens cleanly — no repair needed.u   ✗ z does not open cleanly: 
check_onlyFu*   Repairing (a backup copy is made first)…rN	  )r}  r"  backup_pathz
  backup: z  strategy: strategyr1  zSELECT COUNT(*) FROM sessionsu   ✓ Repaired — z sessions recovered.u   ✓ Repaired.u   ✗ Repair failed: r  z  A backup is preserved at: z3  Keep state.db and the backup; do not delete them.z(Error: Could not open session database: r  r	  r  )r  exclude_sourcesr3  r  c              3   @   K   | ]}|                     d           V  dS )r  Nr6  )r  r  s     r   r  z-main.<locals>.cmd_sessions.<locals>.<genexpr>4  s,      >>QUU7^^>>>>>>rH   Titlez<32r7  Previewz<40zLast Activez<13r  uJ  ──────────────────────────────────────────────────────────────────────────────────────────────────────────────r%  r  z<6u  ───────────────────────────────────────────────────────────────────────────────────────────────r  r  rR   &   0   r  r
  rK  r  r
  z	Session 'z' not found.)ensure_asciirc  r   wr   r   zExported 1 session to ru  z	Exported z sessions to r
  zDelete session 'z' and all its messages? [y/N] rK  r  )sessions_dirzDeleted session 'r}  rU  z from 'r  z%Delete all ended sessions older than z daysz? [y/N] )older_than_daysr  rl  zPruned z session(s).r{  z' renamed to: r   rZ  r3  rw  zResuming session: rX  r   rW  r$  rr	  u0   Optimizing session store (FTS merge + VACUUM)…zError: optimization failed: z
Optimized z FTS index(es).zDatabase size: rW	  z MB -> z MB (reclaimed z MB)rY  zTotal sessions: zTotal messages: )r   telegramdiscordr  r  r  r  r  z MB)4r  rS  r4  r^  r_  r`  r,   r|   r0   r(   r2  _connexecutefetchoner   list_sessions_richr  r3  r  r  rp  resolve_session_idexport_sessionr  r
  rE   r  rL   r.   
export_allr   r  r   delete_session
older_thanprune_sessionsr*   r  set_session_titler   r6  r/  re  rY  db_pathr&   r)   getsizevacuumsession_countrv  r  )-rx  r  r
  r^  r_  r`  r{  r\  r  r2  r  r7  r  _source_excluder  
has_titlesr  r  r  r  r  resolved_session_idrq  rj   r  rl  rI  
source_msgr  r3  r  _browse_excludeselected_idrY  	before_mbafter_mbsavedr  msgsr-  r  size_mbr\  sessions_parsers-                                              r   cmd_sessionszmain.<locals>.cmd_sessions4  sC   %
 X          &G>>## NNNNOOO&&w//F~JWJJJKKKBBB&BBCCCt\511 >???++GD+u$E$E E  F zz*%% M::m,, @>vm'<>>???=VZZ
%;%;==>>>+666666!	)117 hjj$A EaEEEFFFF  + + +/*****+ AFJJw,?,?AABBB::m,, RP9NPPQQQKLLLF	......BB 	 	 	@Q@@AAAFFFFF	
 $$//"044V,,{HDJ -  H  *+++>>X>>>>>J "QQQyQQQ}QQQ4QQRRRk""""NNNNNNUNNNNNOOOj!!! U U,QUU=-A-ABB "3AEE)R(("--y"--crc2 
  UUU7^^4ucrc:ED'CUNNNNNN{NNNNNOOOOD'CWSSS;SSSQx[SSScSSTTTTU x Q&(&;&;DO&L&L#* CdoCCCDDDF(()<== CdoCCCDDDF{{4e{<<tC;#%%J$$T****dk3AAA &Q& & & & & & & & & & & & & & &@4;@@AAAA===<<;#%%% T T
((QU)K)Kd)RSSSST dk3AAA OQ!) O OAGGEKKK$F$F$MNNNNOO O O O O O O O O O O O O O O Oc(mmOO$+OOPPPPx"$"7"7"H"H& ?$/???@@@8 &Z':ZZZ   ,'''F*,,z9L  !4< PP AA*=AAABBBB?$/???@@@@w?D59[H14;1111bJ8 &[D[[z[[[   ,'''F*,,z9L%% $T[| &  E /E///0000x"$"7"7"H"H& ?$/???@@@HHTZ((E%''(;UCC EP&9PPPPQQQQCdoCCCDDD % % %mmm$$$$$$$$% xD'3//63ET8T22F&,:dd6(O,,e -  H HHJJJ *+++0::K l### 4{44555444444Hj+.///Fz!!jG >>##((K88 
 DEEE IIKK   8Q88999


 >>##((K88 
 (E1q111222.)J . .J . .#-. . .   
 w$$&&E##%%D,U,,---+T++,,,J 4 4$$C$00q552s22a222333jG~~ :'//'22kB88888999 &&(((





s   'AE; ;FF>H 
H6H11H6-SSS.3V..V25V2A_ 
_._))_. d 
e&ee)r  )r  )r  )r	  )r  )r  )r  r
  z2Print shell completion script (bash, zsh, or fish)r`  r  )r  ra  rb  zShell type (default: bash))r  r   r  r  c                 $    t          |           S r  )re  )rx  rd  s    r   rj  zmain.<locals>.<lambda>5  s    ^D&5Q5Q rH   )rS  rU  )r*  )rt  rg  )get_container_exec_infor  c              3   J   K   | ]}|                     d           |v V  dS r	  r
  )r  rS  _known_cmdss     r   r  zmain.<locals>.<genexpr>6  sM        c9J9J	[     rH   TFr  r  r  r  r  r  r  )r  r  r  r  r)  Nr  )r  r  r  r  r  r  r  r  r  )r   hermes_cli.stdior
  r   r  rE   r@   rJ  r  r  r  r  r  r  r   r  hermes_cli.moa_cmdr  r%  add_subparsersadd_argumenthermes_cli.fallback_cmdr  rw   r  r'  hermes_cli.migrater  r   r   r  r  rZ  agent.lsp.clir#  r]  r^  r   r  r   r  r   r  r  r   r  hermes_cli.send_cmdr$  r   rj  r   rm  r   rq  r   ru  r   ry  r   r}  hermes_cli.portal_clir  r&  r  r  r  r   r  r   r  r   r  r   r  r   r  r   r  hermes_cli.checkpointsr   r  r   r  r   rx  r   r  r   r  hermes_cli.bundlesr(  r   r   r  plugins.memoryr)  r  r0  r*  r  r(   
__import__RawDescriptionHelpFormatterr   _cli_commandsr  rY	  rZ	  r&  hermes_cli.curatorhermes_cli.petshermes_cli.journeyr   r  r   r  r   r  r   r4   rV   r   r  r   r  r   r  r   r	  r   r  r   r  r   r  r   rS  rU  r   r*  r   rt  r   rh  r  r  rf  r   r
  ior   r  keysr  r
  r   StringIOr  r  rl  r]   r  r0   r  r  r  r|  r{  r  r  r  r  )Rr
  r  
subparsersr  r  
moa_parsermoa_subparsersmoa_configure
moa_deleter  fallback_parserfallback_subparserssecrets_subparsers
secrets_bw_secrets_clir  r  r   migrate_parsermigrate_subparsersmigrate_xai_lsp_register_lsp_errwhatsapp_cloud_parserr$  _add_portal_parser_build_kanban_parserkanban_parser_build_project_parserproject_parsercheckpoints_parser_register_checkpoints_clibundles_parser_bundles_registerr(  r)  r0  r*  seen_plugin_commandscmd_infoplugin_parser_exccurator_parser_register_curator_clipets_parser_register_pets_clijourney_parser_register_journey_clicomputer_use_subcomputer_use_installcomputer_use_doctorcomputer_use_perms_subcomputer_use_perms_statusrR  sessions_subparserssessions_listsessions_exportsessions_deletesessions_prunesessions_repairsessions_renamesessions_browser  completion_parserr  rE  _processed_argv_io_has_cmd_token_saved_stderrrx  r   r  r  r   r\  r  rP  rQ  rd  r  r  sR                                                                              @@@@@@@r   r  r  1  s    <<<<<<!!!!   !####   38ABB<''-///    #$$ "$$ 999999&<&<&>&>#FJ(+++
 zY7777******&&?M '  J
  ..M.BBNftf;YZZZ"--kH:Ts-ttMvS7XYYY**8dVJ_*``JF)@AAA)))
 544444 ++M`	 , 	 	O *88>P8QQ""K #   
 ""^ #    ""5 #   
 ""* #      l 333
  **IV	 + 	 	N (66<M6NN#..4 /  J 766666j)))     %6777
 @???????**NK	 +  N (66N6KK$//J;	 0 	 	K I    
 G    
 /222[111
 yUg   BEEEEEEj!!!! B B B 	6AAAAAAAAB zY7777
 ZIIII
 *<@@@@
 '11=8	 2 	 	 &&,>&???
 zY7777
 <;;;;;J'''
 zY7777
 
z::::
 j84444
 
z::::
 j84444
 ====
 GFFFFFz"""
 GFFFFF((44MJ///
 NMMMMM**:66N[111 zY7777
 
z:::: *<@@@@
 j84444
 zY7777
 
z::::
 $..=E /   QPPPPP0111
 J:>>>>
 
z::::
 ====
 ====
 
z::::
  **S(	 +  N VUUUUUUUn%%%_555
 ==== $%%  W	WCCCCCCOOOOOOOO#&55 88:: 
; 
; * 5 5V$!&) (]B ? ?$.z$:$:$V	 !6 ! ! %$]333<<--9!..H\4J.KKK$((&)9::::..00>EEGG L LF#';;; * 5 5V$!&) (]B ? ?$.z$:$:$V	 !6 ! ! %$]333<<--9!..H\4J.KKKL  	W 	W 	Wh''--.OQUVVVVVVVV	W  **QE	 + 
 
NQLLLLLLn---- Q Q Q(##))*I4PPPPPPPPQ ''?$	 ( 	 	KNFFFFFF;'''' N N N(##))*FMMMMMMMMN  **^,>R + 
 
NQLLLLLLn---- Q Q Q(##))*I4PPPPPPPPQ 
z::::
 zY7777
 %//Q&	 0  & +99?T9UU+66L 7   %%4	 &    @      +55J9	 6   $$8 % 
 
 
 $$P %    $$T %   
 *44LQ	 5 
 
 0>>( ?   !7 A AN !B ! ! **> +   
 %%L &   
\) \) \) \) \) \)| $$*:$;;; Z1111
 !++K> ,  O
 *88>O8PP'226@V2WWMJ     R.D     *448 5  O   B !      2D EEE  6Q RRR)442 5  O   4J KKK  l1D !    )33GBW3XXN>	      
1WXXXl1D      ""P #   
 *44MG	 5 	 	O   O !   
   A !    ""71P"QQQ)448 5  O   4J KKK  :U VVV)44Q 5  O   J !      S/T !        l l l l l l\   l 333
 *<@@@@
 j84444
 ====
 
z::::
 :]CCCC
 Z1111
 ====
 #--A .   ""''') #    ""(Q(Q(Q(Q"RRR
 #5   $ Z1111
 j84444
 ZIIII :99999,,..N >38ABB<888 	1#(122,??O  +2*i*H*HSJ##%%&&&cee      "1    N  2"

	6CJ$$_55D&CJJ 
	6 
	6 
	6&CJ x1}} #(J$$_55DDDDDD
	6 $
  11 | D 4    tY%% 

222222KdGT22 z488 z488	  	
 	
 	
 	 t) t|/C
 		- 		-MD' 4&& -dG,,, |	
 	- 	-MD' 4&& -dG,,, tV 		$s   ( 
55A 
AA$A> >
B
B+N= =
O,O''O,:Fb 
c-cc/d 
e-d==ee0 0
f1:-f,,f1g" "
h#,-hh#C?AD DAED4AEEAE__main__r*  r  )r   )F)r!  )NFNNNNNFNNFFFNFr*
  )rR   rR   NNN)rR   r  )r[  )FN)r  r  (  r	  hermes_bootstrapModuleNotFoundErrorr&   rE   r   r   __annotations__r4   r?   rV   rG   rO   rZ   r  r^   rn   r   r   r  r+  ru  r  r  rW  rR  rA  pathlibr   typingr   hermes_cli.subcommands._sharedr   _add_accept_hooks_flaghermes_cli.subcommands.cronr   hermes_cli.subcommands.gatewayr   hermes_cli.subcommands.profiler   hermes_cli.subcommands.modelr   hermes_cli.subcommands.setupr   "hermes_cli.subcommands.postinstallr   hermes_cli.subcommands.whatsappr   hermes_cli.subcommands.slackr   hermes_cli.subcommands.loginr   hermes_cli.subcommands.logoutr   hermes_cli.subcommands.authr   hermes_cli.subcommands.statusr   hermes_cli.subcommands.webhookr   hermes_cli.subcommands.hooksr   hermes_cli.subcommands.doctorr   hermes_cli.subcommands.securityr   hermes_cli.subcommands.dumpr   hermes_cli.subcommands.debugr   hermes_cli.subcommands.backupr   !hermes_cli.subcommands.import_cmdr   hermes_cli.subcommands.configr   hermes_cli.subcommands.consoler   hermes_cli.subcommands.versionr   hermes_cli.subcommands.updater    hermes_cli.subcommands.uninstallr    hermes_cli.subcommands.dashboardr   hermes_cli.subcommands.guir   hermes_cli.subcommands.logsr   "hermes_cli.subcommands.prompt_sizer   hermes_cli.subcommands.memoryr   hermes_cli.subcommands.acpr   hermes_cli.subcommands.toolsr   hermes_cli.subcommands.insightsr   hermes_cli.subcommands.skillsr   hermes_cli.subcommands.pairingr   hermes_cli.subcommands.pluginsr   hermes_cli.subcommands.mcpr   hermes_cli.subcommands.clawr   r   rz   r   rD  rQ  r)   r  r   r  r   rM  r   _FORCE_IPV4_EARLYr-   _yaml_early	_cfg_pathr,   r.   r;   r/   r0   r1   _early_cfg_rawrw   r   apply_managed_overlayr   r'   r(   _early_sec_cfgr2   r3   _early_redactr6   _early_net_cfgr  r  _setup_loggingr  r@   r   r  _apply_ipv4rY	  r+  r|  r{  r  ra   rp   hermes_cli.model_setup_flowsr  r  r  r  r  r  r  r  r   r!  r"  r#  r$  r%  r&  r'  r(  r)  r*  r+  rZ	  r&  r]  r/  r;  rN  rW  rY  r^  rf  rm  rp  r  r  r/  r8  rD  rf  rn  rr  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  objectr  r   r   r'  rm  rs  ru  ry  r  r  r  r  r  r  r  r  r  r  r_  r  r  r  r  r  r]  r  r  r  r\  _DEFAULT_QWEN_PORTAL_MODELSr  r	  r  r  r  r^  r$  r'  r,  r/  rB  rP  rT  rY  rg  rj  rm  rq  ru  ry  r}  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r	  r  r  r/  r7  rA  rK  r_  rc  r|  r~  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r
  	Namespacer*  r  r>  rJ  rR  rO  rw  _warn_stale_dashboard_processesr  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r	  r  r  r  r  r&  r(  r+  r-  rJ  rS  r8  r9  r:  rl  rq  r  ry  r  r  r  r  r  r_  r  r  r  r  r  r  r  r  r  r	  r	  r 	  r"	  r  r@	  rL	  rd	  rp	  rv	  r~	  r	  r	  r	  r	  r	  r	  r	  r	  r	  r
  r  r
  r  r$  r:  rS  rU  rZ  re  rh  rt  rx  r}  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r   r  r  r  rN   rH   r   <module>r     s+  * * *r	 	 	 	D	 
			 



% % % %Z )-  , , ,    >6 6- 6 6 6 6 6.   ,     T    8tCy 8T 8 8 8 83:    (	^ 	^ 	^ 	^
t 
 
 
 
 ! "" 
*Q--                       [ Z Z Z Z Z 9 9 9 9 9 9 ? ? ? ? ? ? ? ? ? ? ? ? ; ; ; ; ; ; ; ; ; ; ; ; G G G G G G A A A A A A ; ; ; ; ; ; ; ; ; ; ; ; = = = = = = 9 9 9 9 9 9 = = = = = = ? ? ? ? ? ? ; ; ; ; ; ; = = = = = = A A A A A A 9 9 9 9 9 9 ; ; ; ; ; ; = = = = = = E E E E E E = = = = = = ? ? ? ? ? ? ? ? ? ? ? ? = = = = = = C C C C C C C C C C C C 7 7 7 7 7 7 9 9 9 9 9 9 G G G G G G = = = = = = 7 7 7 7 7 7 ; ; ; ; ; ; A A A A A A = = = = = = ? ? ? ? ? ? ? ? ? ? ? ? 7 7 7 7 7 7 9 9 9 9 9 9s t    $ tH~~$+3355 33|$$ % % %iF iF iF iFX      . - - - - - 4 4 4 4 4 4  |f4 5 5 5 5  	!!M1I T)g... 	"-[-77;tDD^H^    	 	 	 	 	 	 	 	 	 	 	 	 	 	 		000000@]@PPNN 	 	 	D	""*44+//
B??Nz.$// U . 2 23C D D ,:=#m:L:L:R:R:T:TBJ67'++Ir:::nd++ 	%0B0B<0P0P 	% $	 	 	 	D		>>>>>>N tLLCHQRRLLLLbQQ78 8 E       	 	 	D	  IIIIII$                   4 4 4 4 4 4 4 4
                                           , 
	8	$	$ S#X(=      C C$J    &*d *sTz * * * *Z
BC 
B 
B 
B 
BG4 G G G G	D 	 	 	 	   $    "Cd C C C C;# ; ; ; ;$qd q q q qhnT nhsm n n n nb # (3-    ( $      $Z%t Z%t Z% Z% Z% Z%z%s %x} % % % %P (3-     EI9 994<SM9	9 9 9 9x #NF#;<< 	 $    8 49* * *	*,0*
4sCx !* * * *4N N N N N Nb 

  &I;;;  
     D T    680 80 80 80v2 2dTk 2TD[ 2 2 2 2D T    64 D    >fND fN4 fNE$s)T/4J fN fN fN fNR5f 5c 5 5 5 50,8C= , , , ,^: :S :C : : : :2 (,""!#b b}bb C=b sm	b
 b b d^b b C=b C=b b b b }b b b b bJ   *   (d    2B B BJ    fY fY fYR& & &&  , , ,*
) 
) 
)c d    [' [' [' ['|
 
 
X* * *
DsC}%&    U3S=12    *$ 3    *   
  	
   
   @C    D9" 9" 9" 9"xL@s L@t L@ L@ L@ L@f 	2K 2K
2K2K 2K 	2K
 
2K 2K 2K 2Kj3WC 3W4 3WD 3W 3W 3W 3Wl 129K $ $ $ $ $`  C C Cs CT\]`Ta C C C CL# #    *
/ 
/ 
/ 
/0 0 0 0 NR=Q =Q =Q =QD7B 7B 7BL , K K K    +# +$ + + + +M M M MhP P3 PS P% P P P PjC C    
 
 
 
 
 
*Z Z Z~              >     " " "    
 
 
      	 	 	   26 ( ( ($ ($ ( ( ( (V, , ,
  <     @	 sTz    'U4tS4Z3O-P ' ' ' 'T/ / /s /% /TW / / / /d($ (4 ( ( ( (^ !$!%aL aL aL	caL	aL 	aL
 aL 
c3h$	aL  aL aL aL aLH4$sCx.4/ 4 4 4 4r #%!%1 1 1	1	1 c3h	1
 1 
c3h$	1  1 1 1 1h 38 z z z4 z4 zD z z z zz:d :t : : : :49 9 9 9 9 9x:T : : : :!&t !&4 !&QU !&Z^ !& !& !& !&HET E4 ED E E E E$:d :x~ : : : :2"tDz " " " "J8T 8d4j 8 8 8 8z F 6 6 6 6 6 6$      D T    D T     !	"+ "+ "+"+	"+ SM	"+
 
"+ "+ "+ "+JZ Z4 ZD Z Z Z ZFt FS	 F F F FR"C4 "CD "C "C "C "CJD $ 4    8    6	/t 	/PT 	/ 	/ 	/ 	/#d #t # # # #L#tCy#~!6 # # # #LY'($ Y' Y' Y' Y'| %)t t tc(T/t 
#Yt t t tn& & & &R? ? ? ?DS S    * Ot4 t4t4	t4 t4 t4 t4r #B "2S "2s "2t "2 "2 "2 "2JD& D& D&N+DI +D +Xc] + + + +\#Y!.1c]   $ 59
 

$,SM
	
 
 
 
& i i#Yi	i i 	i 
i i i iX-#Y-	- - 
	- - - -h    G 3 T#Y T hsm     # 4    "$s) $ 4    $s) $ 4    DI D  3 SV     Dd D D D D  d3i d t    "qX49 qX4 qXD qX qX qX qXh  8 S T#Y    H/T / / / /J J J JJ J J Jp p p pl "&&(	$ $ $	c$ 
c3h$	$ !$	$
 
$ $ $ $N#T # # # #14$; 1 1 1 1C4 CDJ C C C C$ 59r r rr'*Tzr	%S/r r r rj%S/"15   6 /0d d dd(+d	%d

d d d dN d  t          FT%d
*;%<      "&#	  	c 
c3h$	 	
 
   D 4$; $    .AH AH AH AHN "&	?C ?C ?CS	?C 
c3h$	?C 	?C
 
?C ?C ?C ?CDDI    0 "&B9 B9 B9S	B9 
c3h$	B9 
	B9 B9 B9 B9P "&	tD tD tDS	tD 
c3h$	tD 	tD
 
tD tD tD tDnS	(,S#X(=	D[   8/ /S#X- / / / / /%D % % % % "&!
 !
 !
S	!
 
c3h$	!
 
	!
 !
 !
 !
H".49 ".t ". ". ". ".J;4 ;4 ;4 ;4|F- F- F- F- F- F- F- F-RN NT N N N Nb# $    . (,     *  ,GC G G G GeEu eE eE eEc eE eE eE eE eEPVQ VQ VQ VQrb b b bJD s t    2
s)!&X   >DE$)$4 D D D DP (,S S SX_S	%S#
S S S SleCcM6J1K PS    <@D4K @ @ @ @F*J *J *J *JZ?
 ?
 ?
 ?
 ?
 ?
D( ( (V/2 /2 /2d:I :I :IzT T T T Tn2Ld Lt L L L L^F	 F	 F	R;
 ;
 ;
 ;
|"# " " " "Js # $    J J J JZO O Od    
% 
% 
% 
%    *  
c 
 
 
 
2 !y    < #    " d
        Fd    * .--eV_-!E7+7)$  T T T T T    0D 0 0 0 0@
 @
 @
 @
F) ) ) )FT F F F FR&T & & & &R< < <~  .  "1 1 1
 
 
        e e eP' zDFFFFF s   
 3M 
'I=1M =JM JM 	J  M  J(%M 'J((B%M MM9N NN"N5 5N=<N=