
    epj#                    P%   U d Z 	 ddlZn# e$ r Y nw xY wddlmZ  e             ddlZddlZej                            ej        	                    ej        
                    e          ej                            Zeej        vrej                            de           ddlmZ ddlmZ 	  ej                     n# e$ r Y nw xY wdeddfdZd	add
ZddddddedededededdfdZdefdZdd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%e         de!fdZ'de!fdZ(d ede!fd!Z)de!fd"Z*dedz  fd#Z+dd$Z,de!fd%Z-de!fd&Z. e              e-            r e/d          ddl0Z0ddl1Z1ddl2Z2ddl3Z3ddl4Z4ddl5Z5ddl6Z6ddl7Z7dd'l8m9Z9 dd(l:m;Z; ddl<Z=dd)l>m?Z? dd*l@mAZB dd+lCmDZD dd,lEmFZF dd-lGmHZH dd.lImJZJ dd/lKmLZL dd0lMmNZN dd1lOmPZP dd2lQmRZR dd3lSmTZT dd4lUmVZV dd5lWmXZX dd6lYmZZZ dd7l[m\Z\ dd8l]m^Z^ dd9l_m`Z` dd:lambZb dd;lcmdZd dd<lemfZf dd=lgmhZh dd>limjZj dd?lkmlZl dd@lmmnZn ddAlompZp ddBlqmrZr ddClsmtZt ddDlumvZv ddElwmxZx ddFlymzZz ddGl{m|Z| ddHl}m~Z~ ddIlmZ ddJlmZ ddKlmZ ddLlmZ ddMlmZ ddNlmZ ddOlmZ ddPlmZ ddQlmZ ddRlmZ ddSlmZ ddTlmZ dUeddfdVZ e9 e                      Z e             ddWZ e             ddXlmZ ddYlmZ  eedZz  [           d	Z	 dd\lmZ  e            d]z  Ze                                r e            pi Z	 dd^lmZ  ej        e          Zn# e$ r Y nw xY wd_ej        vr^e                    d`i           Z eee          r<e                    da          Ze% ee                                          ej        d_<   e                    dbi           Z eee          re                    dc          rddZ[[n# e$ r Y nw xY w	 ddelmZ  e edf ej        dgd         D             dh          div rdjndkl           n# e$ r Y nw xY wer	 ddmlmZ  eddn           n# e$ r Y nw xY wddlZddlZddlZddolmZ ddplmZmZ ddqlmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZ  ej        eԦ          ZՐddreeef         dz  de!fdsZdte9duededz  fdvZdwe9dedz  fdxZdefdyZde9fdzZde!fd{Zېdd|Zde!fd}Zde!fd~ZdefdZde!fdZde%de;e         fdZde;e         fdZddede;e         fdZdde%dede!fdZdede%fdZdede;e         fdZde;e         de;e         fdZ	 dde;e         de;e         ddfdZ eddh          Z	 de9de9fdZd	dde9de!dee9eedf         f         fdZde9de!fdZdZdZ eh d          Zde9fdZde9de!fdZddZdde9dz  de9dz  fdZde9de!fdZde9ddfdZde9de!dee%e         e9f         fdZdede%e         fdZde;e         fdZddedefdZddre;e         defdZdreddfdZ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dde;e         de!de;e         de;e         dedede;e!         de!de;e         de;e         de!de!de!de;e         de!fdZddZ ddZde!fdZd Zd ZdÄ ZdĄ Zdń ZdƄ ZdǄ Z	dede!fdɄZ
ddʄZd˄ Zg d̢Ze%eeeef                  ed<   de%eeeef                  fd΄ZdedefdЄZdhdhdhdќdedededededdfdՄZdefdքZddׄZdeddfd؄Z	 ddedede%deddf
d܄Zdededdfd݄ZdddߜdZddgZddedede;e         fdZdedefdZddZddZ	 	 ddZd ZdZd ZdefdZ deddfdZ!ddZ"	 	 ddede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  Z0d Z1d Z2d Z3d Z4d Z5d Z6d Z7d Z8d	 Z9d
 Z:dddde!ddfdZ;d Z<d Z=de9defdZ>ddl?m@Z@mAZAmBZBmCZCmDZDmEZEmFZFmGZGmHZHmIZImJZJmKZKmLZLmMZMmNZNmOZOmPZPmQZQmRZRmSZSmTZTmUZUmVZVmWZWmXZXmYZYmZZZm[Z[m\Z\m]Z]m^Z^m_Z_m`Z`maZambZbmcZcmdZdmeZemfZfmgZgmhZhmiZimjZjmkZkmlZlmmZmmnZnmoZompZpmqZqmrZrmsZsmtZtmuZumvZvmwZwmxZxmyZymzZzm{Z{m|Z|m}Z}m~Z~mZmZmZmZmZmZmZmZmZmZmZmZmZ dZddZddZde9de!fdZde9de9defdZde9fdZde9de9ddfdZddddde%e         de9ded edreeef         dz  de7j        fd!Zdeeef         dz  fd"Zd#dddd$d%ede9d&eedf         d'e!dreeef         dz  de7j        fd(Zde%e         de9dreeef         d'e!de7j        f
d)Zd*ededz  fd+Zd	d,de9d-e!de!fd.Zd	d,de9d-e!de!fd/Zd0e9de!fd1Zde9defd2Zde9fd3Zd0e9de9d4e!de!fd5Zde9d4e!ddfd6Zd0e9de;e9         fd7Zd8Zd9Zd:Zed;ed<ed=iZed=ed>ed?iZdgZde;e         fd@Zde;e         fdAZdefdBZdefdCZde9defdDZde9de;e         fdEZde9de;e         fdFZdGe9de9fdHZdGe9de;e9         fdIZd0e9dGe;e9         defdJZde%e9         fdKZd0e9de%e9         fdLZdMZde9de9fdNZde9de9fdOZde9de!fdPZde9de!fdQZddRde9dredSe;e         de!fdTZde9drede!fdUZd0e9de%e         fdVZdWe9de;e         fdXZde;e         fdYZdZe9de!fd[Zd\d]dZe9d0e9d^ede!fd_Zdd`d0e9dae;e!         de!fdbZdred4e!de!fdcZde!fddZdGe9de!fdeZÐdGe9de!fdfZdee%e         ef         fdgZŐdhe0j        fdiZddjlȐmɐZɐmʐZʐmːZ ddkdlee         dz  de%e         fdmZ̐dnedeeeef         dz  fdoZ͐dpedz  defdqZΐdrZϐefdsedtede!fduZАdvededz  fdwZѐdxededz  fdyZҐdvededz  fdzZӐdd{ed|edz  de!fd}ZԐdvede%e         dz  fd~ZՐde%e%e                  de%e%e                  fdZ֐eʐZאddede%e         fdZde9fdZde9fdZde9de!fdZde9deddfdZܐddZݐddZސddZߐddZddZde!fdZdee%e         eeef         dz  f         fdZdddde%e         dreeef         dz  deddfdZde!fdZde9dz  fdZde9de%e9         fdZddde9dede%ee9e9f                  fdZde9de9de!fdZde%ee9e9f                  ddfdZdddde%e         dreeef         dz  de9dz  ddfdZdde9dz  ddfdZej        Zeeeef         df         ed<   ej        Zeeef         ed<   ddde%e         dreeef         dz  ddfdZde%e         de%e         fdZddde%e         dreeef         dz  de%e         dz  fdZddde%e         de%e         dreeef         dz  de!fdZddde%e         dreeef         dz  defdZdddde%e         dreeef         dz  deddfdZde%e         fdZddde%e         dreeef         dz  ddfdZdddde%e         dreeef         dz  deddfdZde%e         dreeef         dz  de9dz  fdZddreeef         dz  de!fdZdede!fdZdedz  fdZ G d d          Zdde!fdZd Z defdZdedefdZd Zde%de%fdZd ZddÄZdefdĄZdpedede!fdƄZ	ddǄZ
dedefdȄZdede!fdɄZdʄ Zd˄ Zd̄ Zdd̈́Zd΄ Zdτ ZdЄ Zde%e         fdфZ eh dң          Z eh dӣ          Zdedz  fdԄZde!fdՄZdUedz  ddfdքZh dףZdؐdِdhfdېdhfdܐdhfdޜZde!fd߄Zde!fdZde!fdZddZddZ ddZ!de!fdZ"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.eԐ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)suppress_platform_ver_console)_startup_fast)_early_recoveryrcreturnc                 6   t           j        t           j        fD ]'}	 |                                 # t          $ r Y $w xY w	 t          j                     n# t          $ r Y nw xY w| d}nt          | t                    r| }nd}t          j
        |           dS )u   Exit one-shot mode without letting late native finalizers change rc.

    The SIGABRT this guards against (#30387, #43055) fires in a
    native-extension finalizer during CPython's ``Py_FinalizeEx``, *after*
    the response has printed. Flush streams, shut down file logging, then
    ``os._exit`` past interpreter finalization. The ``atexit`` chain is
    deliberately skipped — several handlers re-enter native code that may
    be the abort source. Stateful cleanup is handled in ``_run_agent`` and
    ``_cleanup_oneshot_runtime``.
    Nr      )sysstdoutstderrflush	Exceptionloggingshutdown
isinstanceintos_exit)r   stream	exit_codes      5/home/thesage/.hermes/hermes-agent/hermes_cli/main.py_exit_after_oneshotr   i   s     :sz*  	LLNNNN 	 	 	D	   	z			B		 			HYs   1
>>A 
A#"A#Fc                  t   t           rdS da 	 ddlm}   |              n# t          $ r Y nw xY w	 ddlm}  |d           n# t          $ r Y nw xY w	 ddlm}  |             n# t          $ r Y nw xY w	 dd	lm	}  |             n# t          $ r Y nw xY w	 dd
lm}  |             dS # t          $ r Y dS w xY w)uS  Best-effort process-global cleanup before one-shot hard exit.

    ``run_oneshot`` owns the agent-local cleanup (memory provider, agent.close,
    session_db.close — all in ``_run_agent``'s finally block). This mirrors the
    process-global pieces from ``cli.py:_run_cleanup()`` that would otherwise
    be skipped by ``os._exit``.
    NTr   )cleanup_all_environments)interrupt_allzoneshot shutdownreason)_emergency_cleanup_all_sessions)shutdown_mcp_servers)shutdown_cached_clients)_oneshot_cleanup_donetools.terminal_toolr   r   tools.async_delegationr   tools.browser_toolr   tools.mcp_toolr   BaseExceptionagent.auxiliary_clientr    )r   r   r   r   r    s        r   _cleanup_oneshot_runtimer(      s      @@@@@@  """"   888888/00000   FFFFFF''))))   777777   BBBBBB!!!!!   sS    
++A 
AAA$ $
A10A15B 
BBB) )
B76B7modelprovidertoolsets
usage_filepromptr*   r+   r,   r-   c                   	 ddl m}  || ||||          }n# t          $ r d}Y nt          $ rU}|j        =t          |j        t                    s#t          |j        t          j	                   d}n|j        }Y d }~n?d }~wt          $ r/ dd l}	 |                                 n# t          $ r Y nw xY wd}Y nw xY w	 t                       t          |           d S # t          |           w xY w)Nr   )run_oneshotr)      filer	   )hermes_cli.oneshotr0   KeyboardInterrupt
SystemExitcoder   r   printr
   r   r&   	traceback	print_excr   r(   r   )	r.   r*   r+   r,   r-   r0   r   excr9   s	            r   _run_and_exit_oneshotr<      s^   222222[!
 
 
       8
38S(A(A#(,,,,BBB    		!!!! 	 	 	D	  """
 	BBsP    B<	B<AB  B<B'&B<'
B41B<3B44B<;B< C C0c                  (    t          j                    S N)r   project_root_str     r   _project_root_str_fastrB      s    )+++rA   c                  ,    t          j                     d S r>   )r   ensure_project_root_on_pathr@   rA   r   !_ensure_project_root_on_path_fastrE      s    -/////rA   c                     	 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_npr   )rL   rN   rO   rP   libcs        r   _set_process_titlerU      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)rV   r   environgetpathjoin
expanduserexistsyamlopenloadgetattr
SafeLoaderr   dictstrstriplowerr   )valuehomecfg_path_yaml_iface_frawdispifaces           r   _config_default_interface_earlyr|     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                 R   | t           j        dd         } d| v rdS t          j                            d          dk    sd| v rdS 	 t           j                                        rt           j                                        sdS n# t          $ r Y dS w xY wt                      d	k    S )
u1  Earliest TUI decision, usable before argparse/config imports.

    Precedence: explicit ``--cli`` wins (forces classic REPL), then
    explicit ``--tui``/``HERMES_TUI=1``, then a real-TTY gate (a
    non-interactive stdio can't host the Ink UI, so ambient config never
    boots it there), then ``display.interface`` in config.

    The TTY gate is load-bearing for headless spawners — kanban workers,
    cron jobs, pipes run ``hermes … chat -q`` with stdio on a pipe. This
    is the earliest launch decision (it runs before ``cmd_chat`` /
    ``_resolve_use_tui``), so a ``display.interface: tui`` default used to
    boot the TUI here — whose no-TTY bail-out exits 0 without doing the
    task → "protocol violation" on every attempt. An explicit ``--tui``
    still reaches the informative bail-out.
    Nr	   z--cliF
HERMES_TUI1z--tuiTrd   )
r
   r}   r   re   rf   stdinisattyr   r   r|   r}   s    r   _wants_tui_earlyr   7  s      |x|$u	z~~l##s**goot	  "" 	sz'8'8':': 	5	   uu*,,55s   <B 
BB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_DISABLEr   r	   sM   [?1003l[?1002l[?1001l[?1000l[?9l[?1006l[?1005l[?1015l[?1016l[?2029l)r   re   rf   r   r   writeOSErrorr@   rA   r   _suppress_mouse_residue_earlyr   ]  s    	z~~344;;  y|| 	F 	G	
 	
 	
 	
 	

    s   A$ A$ $
A21A2c                  (    t          j                    S )z3Tiny Termux check for pre-import startup shortcuts.)r   is_termux_envr@   rA   r   #_is_termux_startup_environment_fastr   v  s    &(((rA   c                 *    t          j        |           S r>   )r   is_termux_fast_version_argvr   s    r   _is_termux_fast_version_argvr   {      4T:::rA   c                 *    t          j        |           S r>   )r   is_global_fast_version_argvr   s    r   _is_global_fast_version_argvr     r   rA   c                  (    t          j                    S r>   )r    is_container_startup_environmentr@   rA   r   &_is_container_startup_environment_fastr     s    9;;;rA   hermes_rootc                 *    t          j        |           S r>   )r    active_profile_may_override_home)r   s    r   &_active_profile_may_override_home_fastr     s    9+FFFrA   c                  (    t          j                    S r>   )r   container_mode_may_be_activer@   rA   r   "_container_mode_may_be_active_fastr     s    5777rA   c                  (    t          j                    S )zARead OpenAI SDK version without importing ``importlib.metadata``.)r   read_openai_versionr@   rA   r   _read_openai_version_fastr     s    ,...rA   c                  ,    t          j                     d S r>   )r   print_fast_version_infor@   rA   r   _print_fast_version_infor     s    )+++++rA   c                  (    t          j                    S )z:Handle ``hermes --version`` before config/logging imports.)r   try_fast_versionr@   rA   r   _try_ultrafast_versionr     s    )+++rA   c                  >    t                      sdS t                      S )z?Backward-compatible test hook for the Termux startup fast path.F)r   r   r@   rA   r   _try_termux_ultrafast_versionr     s!    .00 u!###rA   Path)Optional)cmd_sessions)add_accept_hooks_flag)build_cron_parser)build_sync_parser)build_gateway_parser)build_profile_parser)build_model_parser)build_setup_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_approvals_parser)build_dump_parser)build_debug_parser)build_backup_parser)build_import_cmd_parser)build_import_agent_parser)build_config_parser)build_skin_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_monitoring_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.r2   r	   N)r
   r   r   r8   r   exit)r   s    r   _require_ttyr     sk     9 9l 9 9 9 		
 	
 	
 	
 	 rA   c                  :   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          st	 ddlm}  |            dz  }|                                r4|                    d                                          }|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.r	   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   addr	   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_USER rootr\   profiles)hasattrr   r   re   rf   rr   pwdr   getpwnampw_dirr   is_dirrq   r   )r   	sudo_userr   ru   	candidates        r   _resolve_sudo_user_profile_envz?_apply_profile_override.<locals>._resolve_sudo_user_profile_env  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--usage-file-c
--continue--z--args>   	--profile-p   z
--profile==-z^[a-z0-9][a-z0-9_-]{0,63}$rY   r   r   HERMES_S6_SUPERVISED_CHILDget_default_hermes_rootactive_profiler]   r^   r   )resolve_profile_envError: r2   z"Warning: profile override failed (z), using default)r
   r}   r   boolrq   len
startswithsplitrematchr   re   rf   r   parentr   hermes_constantsr
  rj   	read_textrr   UnicodeDecodeErrorr   hermes_cli.profilesr  FileNotFoundErrorr8   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_homer;   startr}   s                     @r   _apply_profile_overrider&    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!!##  ",,g,>>DDFF  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%I7 7
M ?KM 2L		M L;;M get_hermes_home)load_hermes_dotenv.env)project_envread_raw_configrZ   )managed_scopeHERMES_REDACT_SECRETSsecurityredact_secretsnetwork
force_ipv4Tsetup_loggingc              #   D   K   | ]}|                     d           |V  dS r  Nr  .0r   s     r   	<genexpr>r;    s3      LLSs8K8KLSLLLLLLrA   r	   r   >   guiservedesktop	dashboardr<  rX   mode)apply_ipv4_preferenceforce)datetime)__version____release_date__)_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_model_flow_ai_gateway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.PREFIXr   TERMUX_VERSIONzcom.termux/files/usrz/data/data/com.termux/)r   re   rq   rf   r  r  )r]  checkprefixs      r   _is_termux_startup_environmentrc  (  so    2:E8R(())F		"## 	7!V+	7566  rA   
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_   errorsN#^ r	   r  r   )r  r   
splitlinesr  r  r  rr   )rd  re  textlinepartss        r   _read_packed_refrq  3  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]   rg  rh  :gitdir	commondirHEADzref:r	   zgit:z:unresolvedz	git:HEAD:N)is_filer  rm  	partitionrr   resolverj   r   r  r  rq  )rr  git_dirro  key_rt   rd  commondir_filerel	head_fileheadre  r   ref_file
packed_shas                  r   _read_git_revision_fingerprintr  F  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:ru  z:missing)r  PROJECT_ROOTstatrF  rG  st_mtime_nsst_sizer   )git_fp
skills_dirr  s      r   "_termux_bundled_skills_fingerprintr  s  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 )Nr  z.termux_bundled_sync_stampr'  r@   rA   r   !_termux_bundled_skills_stamp_pathr    s    x'*FFFrA   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_SYNCr   r]   r^   )	rc  r   re   rf   r  r  rr   r  r   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^   )rc  r  r  mkdir
write_textr  r   r  s    r   "_mark_termux_bundled_skills_syncedr    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)rc  r  tools.skills_syncr  r  r  s    r    _sync_bundled_skills_for_startupr    s[     &'' 0R0T0T u------Kd&(((4rA   c                  h    t                      sdS t          j                            d          dk    S )NTHERMES_TERMUX_PREFETCH_UPDATESr   )rc  r   re   rf   r@   rA   r   $_termux_should_prefetch_update_checkr    s/    )++ t:>>:;;sBBrA   c                 $    ddl m}  ||           S )zFormat a timestamp as relative time (e.g., '2h ago', 'yesterday').

    Thin wrapper kept for backward compatibility; the implementation lives
    in :mod:`hermes_cli.timefmt` so lightweight consumers don't have to
    import the whole CLI surface.
    r   )relative_time)hermes_cli.timefmtr  )tsr  s     r   _relative_timer    s&     100000=rA   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  }|                                rs	 ddl} |j        |                    d                    }|                    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_CONFIGr*   r   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 r>   )r   getenv)r:  vs     r   r;  z/_has_any_provider_configured.<locals>.<genexpr>  s*      
3
3A29Q<<
3
3
3
3
3
3rA   Tr]   r^   rj  r  zexport    Nz'"	logged_inz	auth.jsonactive_providerr+   base_url)read_claude_code_credentialsis_claude_code_token_validrefreshTokenF)hermes_cli.configr  r(  r  hermes_cli.authr  r  rf   r   rp   rr   rq   r  values	auth_typeupdateapi_key_env_varsanyrj   r  rm  r  rz  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_filero  r}  r~  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171CCDDD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$ $
O10O1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.
    z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.titler   previewsourceN   last_activeid   -      <  <10<5rl  )rf   rr   r  max)
smax_xr  r  r  r  sid
fixed_cols
name_widthr   s
             r   _format_rowz+_session_browse_picker.<locals>._format_rowC  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PrA   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  r   r  r  r  )rs   rf   )r  queryqs      r   _matchz&_session_browse_picker.<locals>._matchY  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	rA   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   r	   r           r   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  rl  IDz  No sessions match the filter.    → /z	 sessionsz (filtered from )z  0/
      r        c                 ,    g | ]} |          |S r@   r@   r:  r  r  search_texts     r   
<listcomp>zB_session_browse_picker.<locals>._curses_browse.<locals>.<listcomp>  s*    'V'V'Vavva?U?U'V'V'V'VrA   r      ~   c                 ,    g | ]} |          |S r@   r@   r  s     r   r  zB_session_browse_picker.<locals>._curses_browse.<locals>.<listcomp>  s*    NNNavva7M7MNNNNrA   )!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footerr}  r  r  r  cursesresult_holderr  s                           @r   _curses_browsez._session_browse_picker.<locals>._curses_browsec  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  r   r  r  2   /   ...r  r  r  r  r	   z>3. z<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.)r8   rE  wrapperr   r)  rf   rr   r  r  inputrs   r   r   r5   EOFError)r  rG  r  r  r  r  labelr  srcr  idxr  r  rE  rF  s   `          @@@@r   _session_browse_pickerrT  2  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	c                  \   	 ddl }  | j        g dddddd          }|j        dk    rO|j                                        r6t
          j                            |j                                                  S n# t          $ r Y nw xY w	 t          j	                    S # t          $ r Y dS w xY w)	u  The current workspace identity for cwd-scoped resume.

    Git repo root when CWD is inside a repo (so all sessions across its
    subdirs/worktrees group together), else the CWD itself. Returns None when
    neither can be determined — callers fall back to the global MRU then.
    r   N)gitz	rev-parsez--show-toplevelTr]   rg  r  capture_outputrn  r_   ri  timeout)

subprocessrun
returncoder   rr   r   rg   abspathr   getcwd)rZ  results     r   _resolve_workspace_keyr`  #  s    
333dWYXY
 
 
 !!fm&9&9&;&;!7??6=#6#6#8#8999   y{{   tts$   A5A9 9
BB
B 
B+*B+r  c                 ^   d}	 ddl m}  |            }t                      }|rP|                    | d|          }|r6|d         d         |&	 |                                 S # t
          $ r Y S w xY wS |                    | 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 )ah  Look up the most recently-used session ID for a source.

    Scoped to the current workspace first (git repo root, else cwd) so
    ``hermes -c`` from repo A continues repo A's last session rather than the
    global MRU. Falls back to the unscoped MRU when no session matches the
    current workspace, preserving the old behaviour for fresh directories.
    Nr   	SessionDBr	   )r  limitworkspace_keyr  )r  rd  )hermes_staterc  r`  search_sessionscloser   )r  dbrc  ws_keyr  s        r   _resolve_last_sessionrk  ;  s    
B******Y[[')) 	)))qPV)WWH ){4( >



     %%V1%==$,6x{4  $6 >



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



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



    
 4s   AC A##
A0/A04(C  B55
CC
CD  CD  C/ /
C<;C< D*DD*
D&#D*%D&&D*cmdbackendvia_sudoc                     	 t          j        | dd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]   rg  rJ   rW  zsudo zError: timed out waiting for z to respond.
The z+ daemon may be unresponsive or starting up.r2   r	   N)rZ  r[  TimeoutExpiredr8   r
   r   r   )rl  rm  rn  rQ  s       r   _probe_containerrq  [  s    	~c$TG\eoqrrrr$   %-:!!!!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')
    rm  container_name	exec_user
hermes_binr  z. not found on PATH. Cannot route to container.r2   r	   Ninspect--formatokr   sudo-nT)rn  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 rl  z<.
The container may be running under root. Try: sudo hermes z-itz-i)TERM	COLORTERMLANGLC_ALL-er  execz-u)shutilwhichr8   r
   r   r   rq  r\  r   r  rh   r   r   re   rf   extendexecvp)rr  rs  rm  ru  rv  rw  runtime	sudo_pathprobeprobe2is_tty	tty_flags	env_flagsvarr  
cmd_prefixexec_cmds                    r   _exec_in_containerr  m  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$$$$$rA   
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   rb  Nr  )rf  rc  get_sessionresolve_session_by_titleget_compression_tipr   rh  )r  rc  ri  sessionresolved_ids        r   _resolve_session_by_name_or_idr    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rg   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_idr   )r  r  r   r  rq   rf   rr   r   )rg   datar  s      r   _read_tui_active_session_filer    s     tz$t**...@@AA$((<((.B//5577{d   tts   A.A5 5
BBr  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.rd   r  Nr   rb  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 r  )r  rk  rf  rc  r  rh  get_session_titler   rf   r   r8   )r  r  targetri  rc  r  r  r  r  r  r  r  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)ry  r  )r  s    r   _workspace_rootr  F  sd    & 
~	&&((**3355 Z--6688
 zJrA   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.r@   --workspacepackagesr  z--include-workspace-root=false)r  relative_toas_posixr   r   sortediterdirry  r  appendtuple)r  r  ws_root	workspaceworkspace_argspackages_dirchilds          r   !_termux_workspace_install_contextr  a  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 r@   )_NPM_LOCK_RUNTIME_KEYSr:  kr  s      r   
<dictcomp>z=_tui_need_npm_install.<locals>.comparable.<locals>.<dictcomp>  s)    PPPA9O0O0O10O0O0OrA   )r  )r  s    r   
comparablez)_tui_need_npm_install.<locals>.comparable  s    PPPPPPrA   optionalr  )r  ry  r  r  r  rf   r   r  JSONDecodeErrorr  st_mtimerp   r  r   )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.)rR  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>   .js.ts.cjs.jsx.mjs.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_FILESry  _TUI_BUILD_INPUT_DIRSr   rglobsuffix_TUI_BUILD_INPUT_SUFFIXES)r   r  rg   bases       r   _iter_tui_build_inputsr    s      %  cz<<>> 	JJJ$  cz{{}} 	JJsOO 	 	D||~~ $+1J"J"J


		 rA   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_BUILDr   >   r   onyestrueTr  r  F)	r   re   rf   rr   rs   r  r  r   r  )r   rD  r  output_mtimerg   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            	      L   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 dd	lm} t           |                      }	 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 noderY   Tr]   rg  F)r]  rX  rn  r_   ri  ra  PATHr   binz.local)r  r  r   re   rf   r  ry  r  r(  rq   rZ  r[  r   SubprocessErrorr  pathsepr   rr   r  r   r{  r  r  ru   r   insertrh   )	helperr(  r$  r_  rp  extrasresolvedextrar  s	            r   _ensure_tui_noder    s8    |F U 3 3 	z~~233 I%-0CCF>> 000000oo''((K 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   2C CC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   __file__r  ry  )r  bundleds     r   _find_bundled_tuir  J  s>    h.z)J6Goo''177T1rA   tui_dirc           	      $   t          j        d          }|r| j        dz                                  sdS 	 t	          j        |dd| j        gt          | j                  dd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.
    rV  rt  Frestorer  Tr]   rg  )cwdrX  rn  r_   ri  ra  )
r  r  r  rj   rZ  r[  r   rq   r   r   )r	  rV  s     r   _restore_tui_workspacer  R  s     ,u

C w~.6688 u	)T7<0GN##		
 	
 	
 	
 	
    uu>>s   6A- -
A;:A;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`.r2   r	   )	r   r  r   re   rf   r8   r
   r   r   )r	  s    r   _ensure_tui_workspacer  m  s     ~~ g&& z~~n-- 	@>W>>???		N&	N 	N 	N Z
 
 
 
 HQKKKKKrA   tui_devc                 	   t                       dt          dt          fd}t          j                            d          }|r5|r3t          d| dt          j                   t          j        d           |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 |st          |            d}t                      }d}	|r|st          |           }	|o| o|	 }
|
st!          |           rq |d          }t          j                            d          st          d           t#          |           | k    rdnd}|rt%          | d          \  }|dg|ddddddt&          j        ffd} |            }|j        dk    r:ddlm} |j        pd d |j        pd } |||          }|r|}|d<    |            }|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 ddlm}  ||           }|s4| dk    r.	 ddl	m
}  |d          r |d          }n# t          $ r Y nw xY w|s&t          |  d           t          j        d           |S )Nr  HERMES_NODEr   find_node_executable)ensure_dependencyu.    not found — install Node.js to use the TUI.r	   )r   re   rf   rg   isfileaccessX_OKr  r  hermes_cli.dep_ensurer  r   r8   r
   r   )r  env_noder  rg   r  s        r   	_node_binz!_make_tui_argv.<locals>._node_bin  s   &==z~~m44H  BGNN844  8RW9U9U  
 	:99999##C(( 	vCCCCCC$$V,, 8//77D    	SHHHIIIHQKKKs   B 
B,+B,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.r2   r	   r  r  r  z--expose-gcNFr  r  u   Installing TUI dependencies…r@   )r  ui-tuiTr  install--include=dev--silent	--no-fund
--no-audit--progress=falsec                      ddl m}  t          j        t	                    t          j        t          j        dddi  |             ddi          S )	Nr   with_hermes_node_pathTr]   rg  CIr   r  r   r   rn  r_   ri  r]  )r  r(  rZ  r[  rq   PIPE)r(  npm_cwdnpm_install_cmds    r   _run_tui_installz(_make_tui_argv.<locals>._run_tui_install   sk    >>>>>>
 >LL!!  :,,..:c::	 	 	 	rA   r   maybe_repair_npm_enginer   r  znpm install failed.r  z
hermes-inkr[  buildr]   rg  )r  rX  rn  r_   ri  zTUI dev prebuild failed.r  z.bintsxzsrc/entry.tsxr%  zTUI build failed.)r  rq   r   re   rf   r8   r
   r   r   r   ry  r  r  r  rc  r  r  r  r  rZ  CompletedProcessr\  hermes_cli.npm_enginer0  r   rr   rh   rm  r[  rj   )r	  r  r  ext_dirpr  r  did_installtermux_startuptermux_need_rebuild$skip_install_for_fresh_termux_bundler  npm_workspace_argsr.  r_  r0  combined_outputrepaired_npmcombinedr  ink_dirr3  should_buildr,  r-  s                          @@r   _make_tui_argvrB    sC   s s    2 jnn-..G 7 ` ` ` ` 		
 	
 	
 	
 	  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  'g&&& K355N 9g 9/88 	Bw;B/B+B ) 1J!'**J iz~~n-- 	42333!'** 5<w4F4FbbLe 	*K)-+ + +'G'
 
  
 
 
 
 
 
 	*"= 	 	 	 	 	 	 	" "!##!!
 FEEEEE!'!4"MM8KMMO223HHL ,"%1"))++!! --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MrA   c                    	 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>   rr   r:  parts     r   r;  z*_normalize_tui_toolsets.<locals>.<genexpr>s  s*      !K!K4$**,,!K!K!K!K!K!KrA   ,c                     g | ]}||S r@   r@   )r:  items     r   r  z+_normalize_tui_toolsets.<locals>.<listcomp>w  s    444t4444rA   )r4   rD  AttributeErrorrM   r   rq   r  r  r  r  r  rr   )r,   rD  	raw_items
normalizedrK  s        r   _normalize_tui_toolsetsrO  b  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           )rl   readrr   r   r   r   )
candidatesrg   fry   rd  s        r   _read_cgroup_memory_limitrU  z  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``.
    i   g      ?i   i   )rU  r   r  )rW  rd  limit_mbsizeds       r   _resolve_tui_heap_mbr[    sj     &''E %H 4  E
  ($3tUE9rA   c                 T   	 t          j                    S # t          $ r | pi                     d          p t           j                            d          pd                                }|r%t          |                                          r|cY S t          t                    cY S w xY w)z=Return a stable cwd value for the Node TUI child environment.PWDr   )
r   r^  r  rf   re   rr   r   r   rq   r  )r]  r   s     r   _safe_tui_cwdr^    s    !y{{ ! ! !iR__U++Jrz~~e/D/DJQQSS	 	i//11 	<     	!s    A9B'B'&B'c                    t          |                     d          pd                                          }|r!t          |                                          st          t
                    | d<   t          |                     d          pd                                          }|r!t          |                                          st          |           | d<   t          |                     d          pd                                          }t          j        	                    |          rot          |          }|
                                st          | d                   |z  }|                                ot          j        |t          j                  }n6t          t          j        ||                     d                              }|st"          j        | d<   dS dS )zMSeed/repair Python-related env vars shared by CLI and dashboard TUI launches.HERMES_PYTHON_SRC_ROOTr   
HERMES_CWDHERMES_PYTHONr  rg   N)rq   rf   rr   r   r   r  r^  r   rg   dirnameis_absolutery  r  r  r  r  r  r
   
executable)r]  src_rootr  pythonpython_pathpython_is_executables         r   _apply_tui_python_envrk    s   377344:;;AACCH :4>>0022 :(+L(9(9$%
cggl##)r
*
*
0
0
2
2C /d3ii&&(( /)#..L))/R006688F	wv P6ll&&(( 	@s<011K?K*2244X;PRPW9X9X#FLcggfoo$N$N$NOO ."~O. .rA   resume_session_idr  verboser  r  imageworktreecheckpointspass_session_id	max_turnsaccept_hooksc                    t           dz  }ddl}ddlm}  |dd          }	 ddlm}  ||	           n,# t          $ r t                              d
d           Y nw xY w|	                    dd          \  }}t          j        |           ||d<   |                    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<   t-          |           |r
||d<   ||d<   |r
||d<   ||d<   t/          |          }|rd                    |          |d<   |rt3          |t4          t6          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-                                          }!tC          d. |!D                       s$|!"                    d/tG                                  d0                    |!          |d,<   |$                    d1d           | r| |d1<   tK          ||          \  }"}#d}$	 	 tM          j'        |"t;          |#          |2          }$n# tP          $ r d3}$Y nw xY w|$d4v rtS          | |           	 t          j*        |           n# tV          $ r Y nw xY w|r	  ||           n[# t          $ r Y nOw xY wnJ# 	 t          j*        |           n# tV          $ r Y nw xY w|r	  ||           w # t          $ r Y w w xY ww xY w|$d5k    r?dd6l,m-}% t%                       t%          d7           t%                        |%d8gd9           t'          j        |$           dS ):z%Replace current process with the TUI.r  r   Nbuild_subprocess_envFTscrub_secretsinherit_profile_homeapply_terminal_config_to_envr]  z5Failed to apply terminal config bridge for TUI launchexc_infozhermes-tui-active-session-r  )rb  r  HERMES_TUI_ACTIVE_SESSION_FILENODE_ENVdevelopment
production)_cleanup_worktree_git_repo_root_prune_stale_worktrees_setup_worktreeu#   ✗ Failed to create TUI worktree: r2   r	   rg   ra  TERMINAL_CWDHERMES_MODELHERMES_INFERENCE_MODELHERMES_TUI_PROVIDERHERMES_INFERENCE_PROVIDERrI  HERMES_TUI_TOOLSETSc              3   f   K   | ],}|                                 |                                 V  -d S r>   rF  rG  s     r   r;  z_launch_tui.<locals>.<genexpr>-	  sK       ! !%)TZZ\\!JJLL! ! ! ! ! !rA   HERMES_TUI_SKILLSHERMES_TUI_QUERYHERMES_TUI_IMAGEr   HERMES_TUI_CHECKPOINTSHERMES_TUI_PASS_SESSION_IDHERMES_TUI_MAX_TURNSrm  HERMES_TUI_TOOL_PROGRESSoffHERMES_ACCEPT_HOOKSNODE_OPTIONSr   c              3   @   K   | ]}|                     d           V  dS )--max-old-space-size=Nr8  )r:  ts     r   r;  z_launch_tui.<locals>.<genexpr>U	  s/      FFq||344FFFFFFrA   r  rl  HERMES_TUI_RESUMEr  r]  r1   >   r   r1   *   )relaunchu   ⚕ Launching update...r  )preserve_inherited).r  tempfiletools.environments.localrv  r  r{  r   loggerdebugmkstempr   rh  
setdefaultrX   r  r  r  r  r8   r
   r   r   rk  rO  rh   r   r  r  r  rq   r  rr   rf   r  r  r[  poprB  rZ  callr5   r  unlinkr   hermes_cli.relaunchr  )&rl  r  r*   r+   r,   r  rm  r  r  rn  ro  rp  rq  rr  rs  r	  r  rv  r]  r{  active_session_fdr  wt_infor  r  r  r  repor;   tui_toolsets	flattenedrK  rt   _tokensr}   r  r7   r  s&                                         r   _launch_tuir    s|   $ X%GOOO >=====

U
N
N
NC]BBBBBB$$----- ] ] ]LW[\\\\\]-5-=-=+G .> . .** H,?C()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   6 &AA4-C" "
D, DD3$M O M'$O &M''O ?N 
N! N!'N3 3
O ?O POP
O(%P'O((P.O:9P:
PPPP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   re   rf   hermes_cli.kanban_dbr  r   r  s    r   _pin_kanban_board_envr  	  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   r  Tr  N)r  r  r   r  s    r   _sync_bundled_skills_quietlyr  	  sT    111111$   s    
$$c                 P   t          | dd          rdS t          | dd          rdS 	 t          j                                        rt          j                                        sdS n# t
          $ r Y dS w xY w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         → always TUI (explicit ask)
      3. no TTY                 → always classic (ambient prefs don't apply)
      4. ``HERMES_TUI=1`` env   → TUI
      5. ``display.interface`` config value ("cli" | "tui")
      6. default → classic REPL

    Explicit flags always win over config so muscle memory and scripts keep
    working regardless of the configured default.

    The TTY gate (3) is load-bearing: ambient TUI preferences (env var or
    config default) must never hijack a NON-interactive invocation. Kanban
    workers, cron jobs, and pipelines run ``hermes … chat -q`` with stdout
    on a pipe; booting the Ink TUI there hits its no-TTY bail-out, which
    prints a resume hint and exits 0 — a kanban worker then dies with
    "exited cleanly without calling kanban_complete — protocol violation"
    on every attempt (found dogfooding the desktop kanban board). A user
    who *explicitly* passes ``--tui`` still gets the informative bail-out.
    rX   Frd   Tr   r   r   r  rb   rc   )rn   r
   r   r   r   r   r   re   rf   r  r  r   rq   rr   rs   )argsr  r{   s      r   _resolve_use_tuir  	  sG   . tUE"" utUE"" t	  "" 	sz'8'8':': 	5	   uu	z~~l##s**t111111""9b117R<<[%PP%%%H%++--*=*=*?*?5*HH   uus$   <A' '
A54A5A8D 
D%$D%c                 ,   t          |           }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|| _        t          | dd          rHt          | dd          s6t          | dd          s$	 ddlm}  |                                | j                  pi                     d          pd                                }	|	rFt           j                            |	          s't          d|	 dt!          j                                ny|	rwt           j                            |	          t           j                            t!          j                              k    r&t!          j        |	           t          d|	            n# t,          $ r Y nw xY w	 ddlm}
m}m}m} ddlm}  | |                      }|rt          j                            dtA          |           d| d           |D ].}t          j                            d ||           d           /t          j                            d |
 d!           t          j                            d"           n# t,          $ r Y nw xY wtC                      st                       t          d#           t                       t          d$           t                       dd%l"m#}m$}  |            s |d&           t          j	        d           	 tK          d'                                          &                                }n# tN          tP          f$ r d(}Y nw xY w|d)v rtS          |            dS t                       t          d*           t          j	        d           tU                      r"	 dd+l+m,}  |             n# t,          $ r Y nw xY w	 t[                       n# t,          $ r Y nw xY wt          | d,d          rd-t           j.        d.<   t          | d/d          rd-t           j.        d0<   t          | d1d          rd-t           j.        d2<   t          | d3d          r| j/        t           j.        d4<   ta                       |rtc          t          | dd          t          | d5d          t          | d6d          t          | d7d          t          | d8d          t          | d9d          t          | d:d          t          | d;d          t          | d<d          t          | d=d          t          | dd          t          | d>d          t          | d?d          t          | d@d          t          | dAd          B           ddCl2m3} i d6| j4        d7t          | d7d          dDt          | dDd          d8| j5        d9t          | d9d          d:t          | d:d          d;t          | d;d          d<| j6        d=t          | d=d          dt          | dd          dt          | dd          d>t          | d>d          d?t          | d?d          d@t          | d@d          d1t          | d1d          pt          | dEd          d/t          | d/d          pt          | dEd          dFt          | dFd          }dG |7                                D             }	  |dIi | dS # tp          $ r1}t          dH|            t          j	        d           Y d}~dS d}~ww xY w)JzRun interactive chat CLI.continue_lastNresumezNo session found matching ''.z5Use 'hermes sessions list' to see available sessions.r	   rd   rX   r  TUICLIzNo previous z session found to continue.no_restore_cwdFro  r   rb  r  r   u$   ⚠ session's recorded dir is gone (z); staying in u   ↪ restored workspace dir: )MIGRATION_GUIDE_URLRETIREMENT_DATEfind_retired_xai_refsformat_issuer  u   [33m⚠ xAI retires z model(s) in your config on z:[0m
u     [33m⚠[0m r  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>   r   r?  r  z4You can run 'hermes setup' at any time to configure.)prefetch_update_checkyolor   HERMES_YOLO_MODEignore_user_configHERMES_IGNORE_USER_CONFIGignore_rulesHERMES_IGNORE_RULESr  HERMES_SESSION_SOURCEr  r*   r+   r,   r  rm  r  r  rn  rp  rq  rr  rs  )r  r*   r+   r,   r  rm  r  r  rn  ro  rp  rq  rr  rs  main	reasoning	safe_modecompactc                     i | ]
\  }}|||S r>   r@   r  s      r   r  zcmd_chat.<locals>.<dictcomp>
  s    ???tq!arA   r  r@   )9r  _apply_safe_modern   r   rq   r  r  r8   r
   r   rk  rf  rc  r  rf   rr   r   rg   isdirr^  realpathchdirr   hermes_cli.xai_retirementr  r  r  r  r  r  r   r   r  r  hermes_cli.setupr  r  rO  rs   rP  r5   	cmd_setupr  hermes_cli.bannerr  r  re   r  r  r  rX   r  r*   r,   r  r  r   )r  use_tuicontinue_valr   r  last_idkind
resume_valrc  
_saved_cwdr  r  r  r  _load_config_for_xai_check_retired_xai_refs_refr  r  replyr  cli_mainkwargses                           r   cmd_chatr  	  s	   t$$GT 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 	h%%.66 j%00

	......$9;;224;??E2JJ5QQWUW^^``J C"'--
";"; CdZddWYW`WbWbddeeee C 0 0 < <@P@PQSQZQ\Q\@]@] ] ]$$$AZAABBB 	 	 	D		
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	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)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 	WT;55 	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st   /DJ 
JJCM! !
M.-M.:3P. .QQR- -
R:9R:>S 
SS_ 
`"&``c                 D    t                       ddlm}  ||            dS )zGateway management commands.r   )gateway_commandN)r  hermes_cli.gatewayr  )r  r  s     r   cmd_gatewayr  
  s6     """222222ODrA   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  r   r   r6   )r  
_cmd_proxyr   s      r   r  r  
  sY     =<<<<<	D		B"c rQwwnn wwrA   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r  r(  u   ⚕ WhatsApp Setupz2==================================================WHATSAPP_MODEr   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.Nr   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): rl  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]   rg  r*  u   
  ✗ Install cancelledr  r1  z(no output)u     ✗ npm install failed:u     ✓ Dependencies installedu)   ✓ Bridge dependencies already installedr  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	--sessionr  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(  r8   rO  rr   rP  r5   rs   rg  !gateway.platforms.whatsapp_commonr   rj   rZ  r[  rq   DEVNULLr+  r\  r   rh   rm  r(  r  r  rmtree)r  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  )r  r  s     r   cmd_whatsapp_cloudr    s4     !"""HHHHHH##%%%rA   c                 (    ddl m}  ||            dS )zInteractive setup wizard.r   )run_setup_wizardN)r  r  )r  r  s     r   r  r    s+    111111TrA   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.r  N)r   rn   hermes_cli.modelsr  r8   r   select_provider_and_model)r  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                 4  9:;<=>?@AB ddl m}m}m} ddlm@m}m} ddlm	=m
>m}  |            }|                    d          }t          |t                    r|                    dd          }|pd}d	}	|                    d          At          At                    rA                    d
          }	|	pt          j        d          pd? @|          }
dt          t"          t          t"          t"          f         f         f>@fd}dt"          dt"          fd< ||          :dt"          dt"          f:=fd}dt"          f:<?Afd} |            }|d}|s_?dk    rY |?|                    d          |
          }||j        }|j        dk    r ||          }nd? d}t)          d| d           |sE	  |d          }n8# |$ r0}?d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}m} t          |          B|r|:v r:|         d         }n|rB                    ||          nd}t)                       t)          d |            t)          d!|            t)                       d" |D             }d# |                    d$i           pi                     d%          pg D             99r|}i ;|D ]$}|j                                        h;|j        <   %|                                D ]S\  }};                    ||                                h                              |                                           T9;fd&|D             }nd' |D             } ||          }|r ||          nd}g } d}!|D ]}"|"d(         d)k    r\|"d*         }#|"                    d+d          }$|$r|"d,          d-|$ d.n
|"d,          d/}%d0|# }&tA          |          o|#|k    }'|"d1         }(nK|"d2         })|                    |)B                    |)|)                    }%|)}&tA          |          o|)|k    }'g }(|'r.| !                    |&|% d3|(f           tE          |           d4z
  }!| !                    |&|%|(f           :                                D ]\  }&}*|*d         }+|*d5         },|,#                    d6d          #                    d7d          $                    d8          }-|*                    dd          }.|.rd9|. nd}/|+ d:|- d.|/ }%|r4|&|k    r.| !                    |&|% d3g f           tE          |           d4z
  }!| !                    |&|%g f           | !                    dd;g f           t          |                    d<          tJ                    o!tA          |                    d<                    }0|0r| !                    d=d>g f           | !                    d?d@g f           | !                    dAdBg f           tM          dC | D             |!D          }1|1| |1         d         dAk    rt)          dE           d	S | |1         d         }2| |1         dF         }3|3rd}4||3v r|3'                    |          }4BfdG|3D             }5| |1         d4         (                    d/d4          d         }6tM          |5|4dH|6 dIJ          }7|7t)          dE           d	S |3|7         }8n|2}8|8d?k    rtS                       d	S |8dk    rtU          ||           n|8dKk    rtW          ||           n|8dLk    rtY          ||           n|8dMk    rt[          ||| N           n|8dOk    rt]          ||           n|8dPk    rt_          ||| N           n|8dQk    rta          ||           n|8dRk    rtc          ||| N           nn|8dSk    rte          ||           nV|8dTk    rtg          ||           n>|8dk    rti          |           n'|85                    dU          s|8:v rJ | |                                          |8          }*|*t)          dV           d	S tm          ||*           n|8d=k    rto          |           n|8dWk    rtq          ||           n|8dXk    rts          ||           n|8dYk    rtu          ||           ni|8dZk    rtw          ||           nR|8d[k    rty          ||           n;|8d\k    rt{          ||           n$|8d]v st}          |8          rt          ||8|           |8d^vr%|85                    dU          st                       d	S d	S d	S )_a  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  )custom_provider_aliasescustom_provider_slugresolve_provider_fullr*   r   r   z	(not set)Nr+   r  autor   c                    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 ]n}
t	          |
t                    s|
                    d          pd	                                }|
                    d          pd	                                }|r|sp|
                    d          pd	                                } ||          }|||
                    dd          |
                    dd          |
                    dd          |
                    di           |
                    dd          |
                    dd          | ||||
                    dd                     ||||
                    dd                    d|	|<   p|	S )Nr   r,  r   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 )Nr   ${)rq   rr   r  rs   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rA   custom_providersr   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 )Nr   r   rq   rr   rs   )r<  r   r.  r*   name_lcpkey_lcr4  s          r   _lookup_refzRselect_provider_and_model.<locals>._named_custom_provider_map.<locals>._lookup_refW  s     $*"oo++--3355G,,"--3355;;==G$$**,,E% 
% 
	 * * A; *8t#3#3>)))2rA   key_envmodelsdiscover_modelsTapi_mode)r   r  r  rB  r*   rC  rD  rE  r.  api_key_refbase_url_ref)
r  r-  rq   rf   r   r  rp   r  r  rr   )r  r-  raw_cfgr7  raw_list	raw_entryraw_providersraw_keyrA  custom_provider_mapr  r   r  r.  r}  r5  r6  r*  r(  s                  @@r   _named_custom_provider_mapz=select_provider_and_model.<locals>._named_custom_provider_map  sj   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 !IIn55;BBDDL&&t\::C$ 99Y33 99Y337B//))Hb11#(99->#E#E!IIj"55 ,*{$dL%))GR:P:P    !,%t\599Wb;Q;Q! !( ($$" #"rA   r:  c                     t          | pd                                                              d                                          S )Nr   r  )rq   rr   rstriprs   )r:  s    r   _norm_base_urlz1select_provider_and_model.<locals>._norm_base_url  s8    39"~~##%%,,S1177999rA   r  c           	         t          | pd                                                                          }                                D ]?\  }}| |                    dd          |                    dd                    v r|c S @| S )Nr   r   r.  )rq   rr   rs   r  rf   )r  	requestedr}  provider_info_custom_provider_mapr)  s       r   _canonical_named_custom_keyz>select_provider_and_model.<locals>._canonical_named_custom_key  s    )r**002288::	"6"<"<">"> 	 	C33!!&"--!!."55    


	
 rA   c                  
   dk    st          t                    sdS                      dd                    } | sdS                                 D ],\  }} |                    dd                    | k    r|c S -dS )Ncustomr   r  )r   rp   rf   r  )current_baser}  rT  rU  rQ  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rrA   r!  zuser-configz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  rX  )CANONICAL_PROVIDERS_PROVIDER_LABELS_PROVIDER_ALIASESgroup_providersprovider_group_for_slugr   nonez  Current model:    z  Active provider:  c                 (    i | ]}|j         |j        S r@   )slugtui_descr:  r7  s     r   r  z-select_provider_and_model.<locals>.<dictcomp>  s    GGGaqvqzGGGrA   c                 x    h | ]7}|t          |                                                                          8S r@   r>  rf  s     r   	<setcomp>z,select_provider_and_model.<locals>.<setcomp>  sH       A  rA   model_catalogexcluded_providersc                     g | ]?}                     |j        |j                                        h          z  8|j        @S r@   )rf   rd  rs   )r:  r7  _cli_excluded
_names_fors     r   r  z-select_provider_and_model.<locals>.<listcomp>  sS     
 
 
>>!&16<<>>*:;;mK
F
 
 
rA   c                     g | ]	}|j         
S r@   rd  rf  s     r   r  z-select_provider_and_model.<locals>.<listcomp>  s    >>>Q!&>>>rA   r  groupgroup_iddescriptionrQ  u    ▸ (r  u    ▸zgroup:membersrd  u     ← currently activer	   r  https://http://r      —  (z$Custom endpoint (enter URL manually)r8  remove-customzRemove a saved custom providerz
aux-configzConfigure auxiliary models...cancelzLeave unchangedc                     g | ]\  }}}|	S r@   r@   r:  r~  rQ  s      r   r  z-select_provider_and_model.<locals>.<listcomp>-  s    ***;1eQ***rA   r   
No change.r  c                 <    g | ]}                     ||          S r@   rf   )r:  mprovider_labelss     r   r  z-select_provider_and_model.<locals>.<listcomp>>  s6     
 
 
*+O1%%
 
 
rA   zSelect z
 provider:r   r  moaz
ai-gatewaynousr  openai-codex	xai-oauth
qwen-oauthzminimax-oauthcopilot-acpcopilotzcustom: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>   ry  rX  rx  )Ar  r%  r&  r'  r  r(  r  r  hermes_cli.providersr)  r*  r+  rf   r   rp   r   r  rq   r  r  r8   r  r]  r^  r_  r`  ra  rd  rs   r  r  r   r  r  r  rg  rP  r  _prompt_provider_choicer   r  _aux_config_menurI  r[  r\  rJ  rK  rL  rM  rN  rS  rR  rO  r  rQ  _remove_custom_providerrZ  rT  rU  rW  rX  rP  r#  rY  _clear_stale_openai_base_url)Cr  r%  r&  r'  r  r  r+  configcurrent_modelconfig_providercompatible_custom_providersrN  rV  r[  r  
active_defwarningr;   r]  r^  r_  r`  ra  active_labelcanonical_descs_alias_to_canonr"  _alias_canon_visible_slugsgrouped_rowsactive_groupordereddefault_idxrA  gid
group_descrQ  r}  	is_activers  rd  rT  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_providerrl  rU  rm  rQ  r)  r*  rZ  r(  r  r  sC                                                            @@@@@@@@@@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x#4T#s(^0C+D x# x# x# x# x# x# x#t:C :C : : : : 65         	c 	 	 	 	 	 	 	 	 	 .-//F~ R(F22**JJ{##'
 


 !]F M1144V<<%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 **_b117R<<=QRRXVX  M
  ?+*,
% 	4 	4B#%7==??"3Jrw-3355 	P 	PNFF!!&6<<>>*:;;??OOOO
 
 
 
 
/
 
 

 ?>*=>>>"?>22L 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	#	#....	l	*	*v}5555	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   G H&G==H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+   r   rX  Nr  r  z.Cleared stale OPENAI_BASE_URL from .env (was: z...)r  )r  r  r  r  rf   r   rp   rr   rs   r8   r  )r  r  r  r  r  r+   	stale_urls          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	
 	
 	
 	
 	

 
rA   ))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)memory_query_rewritezMemory query rewritezmemory retrieval queries)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_tasksr}  display_namerr  )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  r   r+   r*   rt  ru  r  zcustom (r      · )r   rp   rq   rf   rr   rg  rP  )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 ('''''OrA   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+   r   r*   r  r  N)r  r  r  r  r   rp   )
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rA   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+   >   Nr   r,  r,  Tr  r   r	   )r  r  r  r  r   rp   r  rf   )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LrA   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   r  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:  r~  r   s      r   r;  z#_aux_config_menu.<locals>.<genexpr>d  s.      ==ZQas4yy======rA   r  c              3   <   K   | ]\  }}}t          |          V  d S r>   r  )r:  r~  descs      r   r;  z#_aux_config_menu.<locals>.<genexpr>e  s.      ==ZQ4s4yy======rA   r  (r  )	__reset__zReset all to auto)__back__Backc                     g | ]\  }}|S r@   r@   r{  s      r   r  z$_aux_config_menu.<locals>.<listcomp>t  s    +++xq%U+++rA   r|  Nr  r  zReset z auxiliary task(s) to auto.z-All auxiliary tasks were already set to auto.)r  r  r   rf   rp   r8   r  r  r  ljustr  r  r  _aux_select_for_task)r  r  r  	all_tasksname_coldesc_colentriestask_keyr   r  r  currentrQ  rS  r}  r  s                   r   r  r  L  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"rA   c                     ddl m} ddlm}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           |                     ||||	                     |	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 )u'  Pick a provider + model for a single auxiliary task and persist it.

    Provider rows come from ``build_aux_picker_rows()`` — the shared aux-picker
    substrate — so this surface shows exactly what every other aux picker
    shows: authenticated built-ins, the user's own ``providers:`` /
    ``custom_providers:`` endpoints, and providers whose credential pool is
    temporarily exhausted. Only already-configured providers appear; users set
    up new ones through the normal ``hermes model`` flow, then route aux tasks
    to them here.
    r   r  )build_aux_picker_rowsformat_aux_picker_entriesr  r+   r,  r*   r   r  c              3   0   K   | ]\  }}}|k    |V  d S r>   r@   r:  r}  r   r~  r  s       r   r;  z'_aux_select_for_task.<locals>.<genexpr>  -      RR,#tQcTkkkkkkRRrA   )current_providerr  current_base_urlz*Could not detect authenticated providers: Nu     ← current__auto__zauto (recommended))r  r  
__custom__zCustom endpoint (direct URL)r  r  z  Configure u    — current: c                     g | ]\  }}}|	S r@   r@   r{  s      r   r  z(_aux_select_for_task.<locals>.<listcomp>  s    "D"D"D[Qq5"D"D"DrA   r|  r+   r*   r  r  z: reset to auto.)r  r  hermes_cli.inventoryr	  r
  r   rf   rp   rq   rr   nextr  r   r8   r  r  r  r  r  _aux_flow_custom_endpoint_aux_flow_provider_model)r  r  r	  r
  r  r  r  r  r  r  r  r!  r;   r  auto_markercustom_markerrS  rd  _labelrC  s   `                   r   r  r    s    .-----UUUUUUUU
+--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))-'-
 
 
		
    @3@@AAA						 13G ,v55>N5TV  NNJ B[ B BBGHHHNN!!--	
 	
 	
   (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   'E6 6
F FFprovider_slugcurated_modelsr  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>   r@   r  s       r   r;  z+_aux_flow_provider_model.<locals>.<genexpr>  r  rA   zNo curated model list for .z;Enter a model slug manually (blank = use provider default):zModel: Nr   )r  pricingconfirm_providerr}  r  : r  z (provider default model))r  r  r  r  r  r  r   r  r8   rO  rr   r5   rP  r  )r  r  r  r  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>   r@   r  s       r   r;  z,_aux_flow_custom_endpoint.<locals>.<genexpr>  r  rA   r  r   r*   z  Custom endpoint for zH  Provide an OpenAI-compatible base URL (e.g. http://localhost:11434/v1)z
Base URL [rL  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): rX  r  rt  ru  r  z
: custom (r  r  )hermes_cli.secret_promptr)  r  r  rq   rf   rr   r8   rO  r5   rP  r  rg  rP  )r  r  r)  r  r  r  
url_promptr:  model_promptr*   r  r  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:r  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_choicer	   u   →rl  r  rK  T
Choice [1-z] (): Please enter 1-Please enter a numberN)r  r/  r8   r   r)  rO  r  rr   r   r   r5   rP  )	choicesr   r  r/  rS  r  cr  r  s	            r   r  r  B  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_urlr   ))r   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:r	   detectedr  z [z / ]r  rK  z     z.Choice [1-4, Enter to keep current/detected]: z
Cancelled.N>   auto-detectr   r,  detect>   2chatcompletionsr9  r9  >   3codex	responsesr:  r:  >   4messagesr  r;  r;  zInvalid API mode choice: z. Falling back to auto-detect.)hermes_cli.runtime_providerr8  rq   rr   rs   r8   r)  r  rh   rO  r5   rP  )r  r6  r8  detected_modenormalized_currentdefault_modemode_optionsrS  rt   rQ  rr  markersr  ry   s                 r   !_prompt_custom_api_mode_selectionrN  w  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                 b   ddl }|                     dd                              dd                              d          } |j        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   Nrt  r   ru  r  z/v1/?$	localhost	127.0.0.1zLocal (r  runpodzRunPod ()r  rg  rP  subr  rs   
capitalize)r  r  cleanr   s       r   _auto_provider_namerV    s     IIIZ,,44YCCJJ3OOEBF9b%((E;;sADdkT11    	TZZ\\	!	!!$!!!  KrA   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.rF  r   rB  r  r0  }rq   rf   rr   )rT  resolved_api_keyrF  rB  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&&,,...rA   c                     t          |                     dd          pd                                          }|r|S t          |pd                                          S )zDReturn the value that should be persisted for a custom provider URL.rG  r   rY  )rT  resolved_base_urlrG  s      r   &_custom_provider_base_url_config_valuer^    s^    }((<<BCCIIKKL  &B''--///rA   c                 :   ddl m}m}  |            }	|	                    d          pg }
t	          |
t
                    sg }
|
D ]^}t	          |t                    rE|                    dd                              d          |                     d          k    rd}|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	}|rK|                    d          |k    s|                    d          r||d<   |                    dd           d	}|r|
|	d<    ||	            dS `|st          |           }|| d}|r||d<   n|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.

    When *key_env* is set the caller has already written the key to ``.env``,
    so the entry references it instead of inlining the secret (#69449).
    r   r  r8  r  r   r  Fr*   TrC  context_lengthrE  NrB  r  )r   r  u%     💾 Saved to custom providers as "z" (edit in config.yaml))r  r  r  rf   r   r  rp   rP  r  rV  r  r8   )r  r  r*   r`  r   rE  rB  r  r  r  r!  r  r  
models_cfgs                 r   _save_custom_providerrb    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+++ EIIi00G;;uyy?S?S;#*i 		)T*** !*3&'C   FF  -"8,,x00E #"i	 #"i g %$j F F #3^"DEhU'CK	
O$
O
O
OPPPPPrA   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  r8  zNo custom providers configured.NzRemove a custom provider:
r   unnamedr  r   rt  ru  r  rw  r  Cancelcurses_radiolistzSelect provider to remove:r  r&  cancel_returnsr	   r  rK  r0  rL  r}  u   ✅ Removed "z" from custom providers.)r  r  r  rf   r   r  r8   rp   rg  rP  r  rq   hermes_cli.curses_uirg  rM   NotImplementedErrorr   rZ  r  r)  rO  r  rr   r   r   r5   rP  r  )r  r  r  r  r!  r4  r  r   r:  r  rg  rS  r  r5  r  removedremoved_names                    r   r  r  3  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   rn  zmodule z has no attribute )_LAZY_MODEL_EXPORTSr  ro  globalsrL  __name__)r   ro  s     r   __getattr__rt  |  sR    """666666*		$
I8IIII
J
JJrA   c                     |                      d          }t          |t                    rHt          |                     d          pd                                                                          S dS )Nagentreasoning_effortr   )rf   r   rp   rq   rr   rs   )r  	agent_cfgs     r   _current_reasoning_effortry    sc    

7##I)T"" L9==!344:;;AACCIIKKK2rA   effortc                 r    |                      d          }t          |t                    si }|| d<   ||d<   d S )Nrv  rw  )rf   r   rp   )r  rz  rx  s      r   _set_reasoning_effortr|    sD    

7##Ii&& $	#w$*I !!!rA   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:  rz  s     r   r;  z5_prompt_reasoning_effort_selection.<locals>.<genexpr>  sk       
 
,2VARARATAT
KK%%''
 
 
 
 
 
rA   )minimallowmediumhighxhighr  ultrac                     g | ]}|v |	S r@   r@   )r:  rz  dedupeds     r   r  z6_prompt_reasoning_effort_selection.<locals>.<listcomp>  s#    III&v7H7Hv7H7H7HrA   c              3   $   K   | ]
}|v|V  d S r>   r@   )r:  rz  canonical_orders     r   r;  z5_prompt_reasoning_effort_selection.<locals>.<genexpr>  s-      QQf63P3P63P3P3P3PQQrA   Nc                     | k    r|  dS | S )Nu     ← currently in user@   )rz  current_efforts    r   r  z2_prompt_reasoning_effort_selection.<locals>._label  s"    ^##4444rA   zDisable reasoningzSkip (keep current)rb  r  r   rf  c                 &    g | ]} |          S r@   r@   )r:  rz  r  s     r   r  z6_prompt_reasoning_effort_selection.<locals>.<listcomp>  s!    888f66&>>888rA   zSelect reasoning effort:r  rh  r	   r  rK  r  Tr0  z] (default: keep current): r2  r3  )r  rp   fromkeysr  r  r   rj  rg  r  r8   rM   rk  r   rZ  r  r)  rO  rr   r   r   r5   rP  )effortsr  r  disable_label
skip_labelr  rg  r4  rS  r  rz  r  r  r  r  r  s    `           @@@r   "_prompt_reasoning_effort_selectionr    s    
 
6=
 
 
 	
 	
 G
 T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_keyexisting_sourcec                 f   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 |                    d          }	|	rdnd}
	 t          |
                                                                          }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(|	s& |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(  r   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 )Nr  z (Enter for no-auth default r1  z (or Enter to cancel): r   )rr   r5   rP  r8   )r  r.   enteredr  rB  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.)r   TT)r  z
Cancelled.zAPI key saved.F)format_secret_source_suffixr  z
 API key: Nr   u   ... ✓zcredential_pool:z"  [K]eep / [R]eplace (default K): z,  [K]eep / [R]eplace / [C]lear (default K): r  rQ  z  No change.z  API key updated.r5  z7  API key cleared.  Re-run `hermes setup` to configure z again.)r  r  r  r  r+  r)  r  r  rq   r8   r   hermes_cli.env_loaderr  r  rO  rr   rs   r5   rP  )r  r  r  r  r  r  new_keyr  source_suffixpool_backedmenur  r  rB  r)  s     `         @@@r   _prompt_api_keyr    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""!,,-?@@K 	<,,; 	
t""$$**,,x(     	!/??? 	'.!!!GGG&&w((("###~ k w###[gl[[[	
 	
 	
 x 
GGGs   3E !E0/E0c                 b    | pd                                                                 }d|v rdS dS )z>Infer the current StepFun region from the configured endpoint.r   zapi.stepfun.comchinainternational)rr   rs   )r  rN  s     r   _infer_stepfun_regionr  I  s8    .b''))//11JJ&&w?rA   regionc                 &    ddl m}m} | dk    r|n|S )Nr   )STEPFUN_STEP_PLAN_CN_BASE_URLSTEPFUN_STEP_PLAN_INTL_BASE_URLr  )r  r  r  )r  r  r  s      r   _stepfun_base_url_for_regionr  Q  sG            W 	&%,rA   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   r  rf   r8   r  r  )r  _dhh_fnr  r  r  r  s     r   ._activate_claude_code_credentials_if_availablezQ_run_anthropic_oauth_flow.<locals>._activate_claude_code_credentials_if_availabler  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  r  r8   r+  r)  rr   r5   rP  r  )
r  r  r  r  tokenr)  manual_tokenr  r  r  s
   `      @@@r   _run_anthropic_oauth_flowr  f  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  r  )r  r  s     r   	cmd_loginr    s*    ------M$rA   c                 (    ddl m}  ||            dS )zClear provider authentication.r   )logout_commandN)r  r  )r  r  s     r   
cmd_logoutr    s*    ......N4rA   c                 (    ddl m}  ||            dS )zManage pooled credentials.r   )auth_commandN)hermes_cli.auth_commandsr  )r  r  s     r   cmd_authr    s*    555555LrA   c                 (    ddl m}  ||            dS )zShow status of all components.r   )show_statusN)hermes_cli.statusr  )r  r  s     r   
cmd_statusr    s*    ------KrA   c                 (    ddl m}  ||            dS )zCron job management.r   )cron_commandN)hermes_cli.cronr  )r  r  s     r   cmd_cronr    s*    ,,,,,,LrA   c                 ^   ddl }t          | dd          }|dv rt          dt          j                   dS |dk    rdd	lm} t          | d
d          }|}	 |                    |          }n6# t          $ r)}t          d| t          j                   Y d}~dS d}~ww xY wt          d| d           t          dt          j                   dS t          |	                                           dS |dk    rdd	lm} | j
        }	 |                    || j                  }nj# |j        $ r)}t          d| t          j                   Y d}~dS d}~w|j        $ r,}t          d| d| t          j                   Y d}~dS d}~ww xY w|                    d          r*t          d| d|                    d           d           nt          d| d           dS |dv rkddlm}m}	 | j        }
 |	|
          s!t          d|
 dt          j                   dS  ||
|dk               t          d |dk    rd!nd" d#|
 d           dS dd	lm} |d$k    r|                                }t          |                    |d%d&'                     |                    d(          rt-          |                    d)          pg           }|                    d*          pg }t          d+| d,|                    d-           d.t          j                   |rBt          d/t-          |           d0d1                    |           d2t          j                   n0|                    d3          rt          d4t          j                   |                    d3          st          d5t          j                   n|                    d6          st          d7t          j                   na|                    d8          st          d9t          j                   n0|                    d:          st          d;t          j                   dS 	 |                                }n6# |j        $ r)}t          d<| t          j                   Y d}~dS d}~ww xY w|                    d6          st          d=t          j                   dS |                                st          d>t          j                   dS 	 |d?k    r|                    |@          }|                                }|rt-          |                    dA          pg           }t          dB| dCt          j                   |                    dD          pg }|rBt          dEt-          |           dFd1                    |           dGt          j                   nw|dHk    r|                    |dIJ          }nY|dKk    r3|                    |@          }|                    |dLJ          }||dM}n t          dN| t          j                   dS n6# |j        $ r)}t          dO| t          j                   Y d}~dS d}~ww xY wt          |                    |d%d&'                     dS )PuH   Skill Sync — personal sync across devices, plus sharing with your org.r   Nsync_command   Nr   a  usage: hermes sync <status|pull|push|now|enable|disable|device|propose>

Your skills, across your devices:
  status            Show what is synced, and from where
  pull              Pull your synced skills
  push              Push your opted-in skills
  now               Reconcile now: pull then push
  enable <skill>    Include a skill in your sync
  disable <skill>   Exclude a skill from your sync
  device [--name N] Show or set this device's label

Shared with your team:
  propose <skill>   Share a skill with your organisationr2   r	   device)skills_sync_clientdevice_namezerror: zdevice label set to 'r  z[New commits from this device will use this label; existing commits keep their previous one.propose)messagezcannot share this skill: zcould not share '': proposal_pendingzShared 'uE   ' with your organisation — an admin needs to approve it (proposal #proposal_idz*). It is not live for the team until then.zAdded 'z'' to your organisation's shared skills.>   enabledisable)set_syncis_curation_eligible'z' is not sync-eligible (bundled, hub-installed, external, or not found). Only agent-created / user-authored skills under ~/.hermes/skills/ can sync.r  zsync enableddisabledz for 'r  r  F)indentensure_asciiorg_available
org_skillsorg_skills_modifiedz
Org skills: z4 shared skill(s) from your organisation (your role: org_rolezJ). They load alongside your own, labeled by origin, and you can edit them.r  z" with local edits not yet shared: , z[
  Share them back with `hermes sync propose <skill>`. Org updates will not overwrite them.r  uU   
Org skills: not applicable — this account isn't a member of a shared organisation.u/   
Not logged into Nous Portal — sync is inert.
nous_adminz*
Sync is not enabled for your account yet.feature_enabledzt
Sync feature is off for this instance (set HERMES_SYNC_ENABLED=1 or config.yaml sync.enabled: true). Sync is inert.r  z`
No sync base URL configured (config.yaml sync.base_url or HERMES_SYNC_BASE_URL). Sync is inert.zsync inert: z3sync unavailable: not enabled for your account yet.z\sync inert: no sync base URL configured (config.yaml sync.base_url or HERMES_SYNC_BASE_URL).pullr4  updatedzorg: refreshed z( shared skill(s) from your organisation.
conflictedzorg: zJ skill(s) have BOTH local edits and org updates, so they were left as-is: z
     Your local version is intact. Review it, then either propose it or delete the local copy and pull again to take the org version.pushzhermes sync push)r4  r  nowzhermes sync now)r  r  zUnknown sync subcommand: zsync failed: )r  rn   r8   r
   r   toolsr  set_device_namer   stable_device_idr   propose_skillr  SyncInertError	SyncErrorrf   tools.skill_usager  r  skillsync_statusdumpsr  rh   resolve_identityresolve_sync_base_urlpull_skillsmaybe_pull_org_skillspush_skills)r  _jsonrS  sscr   storedr  r_  r  r  r  r  r  modifiedr4  
org_resultclashespull_respush_ress                      r   cmd_syncr    s   
$
-
-C
jG 	
 	
 	
 	
" q
h333333t]D11,,T22   mmm#*5555qqqqq 4&4445553Z   
 1c""$$%%%q
i333333y	&&tT\&BBFF! 	 	 	1a11
CCCC11111} 	 	 	2d22q22DDDD11111	 ::()) 	K54 5 5)/M)B)B5 5 5    IDIIIJJJq
###DDDDDDDD
##E** 	<E < < < Z	    1x(((S3(??ii
SS%SSSTTTq//////
h""ekk&k??@@@::o&& 	FJJ|,,233Azz"788>BHG G G%zz*55G G G Z	     <X < <yy**< < <     ZZ$$ 	,Z   
 zz+&& 	D3:VVVVVL)) 	=Z     -.. 	EZ    
 J'' 	8Z   
 q''))    Q  sz2222qqqqq <<%% A	
 	
 	
 	
 q$$&& (	
 	
 	
 	

 q#&==__h_77F 2244J 
y117R88%a % % %   
 %..66<" 	:G : :99W--: : : !Z    F]]__h@R_SSFFE\\99HBSTTH&99FF3c33#*EEEE1=   !a!!
3333qqqqq 
%++fQU+
;
;<<<1sm   A- -
B 7BB 
D' '
F1EF"!F		FQ/ /
R"9RR"	EY 
ZZ  Zc                 (    ddl m}  ||            dS )z Webhook subscription management.r   )webhook_commandN)hermes_cli.webhookr  )r  r  s     r   cmd_webhookr    s*    222222ODrA   c                     t          | dd          }|dv rt          dt          j                   dS |dk    r$dd	lm}  ||           }|rt          |          |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_commandNr  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.r2   r	   manifestr   )slack_manifest_commandzUnknown slack subcommand: )rn   r8   r
   r   hermes_cli.slack_clir	  r6   )r  rS  r	  r  s       r   	cmd_slackr    s     $
.
.C
j: 		
 		
 		
 		
 q
j??????''-- 	%V$$$	
,s
,
,3:>>>>1rA   c                 $    ddl m}  ||           S )z"Multi-profile collaboration board.r   )kanban_command)hermes_cli.kanbanr  )r  r  s     r   
cmd_kanbanr    s$    000000>$rA   c                 $    ddl m}  ||           S )z1Manage projects (named, multi-folder workspaces).r   )projects_command)hermes_cli.projects_cmdr  )r  r  s     r   cmd_projectr    s%    888888D!!!rA   c                 (    ddl m}  ||            dS )z%Shell-hook inspection and management.r   )hooks_commandN)hermes_cli.hooksr  )r  r  s     r   	cmd_hooksr    s*    ......M$rA   c                 (    ddl m}  ||            dS )z%Check configuration and dependencies.r   
run_doctorN)hermes_cli.doctorr  )r  r  s     r   
cmd_doctorr    *    ,,,,,,JtrA   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: r2   r  )rn   hermes_cli.security_auditr!  r
   r   r   r8   r   )r  rS  r!  r7   s       r   cmd_securityr#    s    
$*D
1
1C
o@@@@@@ "!$''TYQ   	
/#
/
/cjAAAAHQKKKKKrA   c                 T    ddl m}  ||           }|rt          j        |           |S )z%Dispatch `hermes approvals <subcmd>`.r   )approvals_command)hermes_cli.approvals_suggestr%  r
   r   )r  r%  r  s      r   cmd_approvalsr'    sA    >>>>>>t$$F MrA   c                 (    ddl m}  ||            dS )z)Dump setup summary for support/debugging.r   )run_dumpN)hermes_cli.dumpr)  )r  r)  s     r   cmd_dumpr+    s%    ((((((HTNNNNNrA   c                 (    ddl m}  ||            dS )z!Debug tools (share report, etc.).r   )	run_debugN)hermes_cli.debugr-  )r  r-  s     r   	cmd_debugr/    s%    ******IdOOOOOrA   c                 (    ddl m}  ||            dS )zConfiguration management.r   )config_commandN)r  r1  )r  r1  s     r   
cmd_configr2    s*    000000N4rA   c                 (    ddl m}  ||            dS )z#Skin management (list / use / set).r   )skin_commandN)hermes_cli.skin_cmdr4  )r  r4  s     r   cmd_skinr6    s*    000000LrA   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)rn   hermes_cli.backupr9  r:  )r  r9  r:  s      r   
cmd_backupr<  #  sg    tWe$$ 666666000000
4rA   c                 (    ddl m}  ||            dS )z(Restore a Hermes backup from a zip file.r   )
run_importN)r;  r>  )r  r>  s     r   
cmd_importr?  /  r  rA   check_updatesrA  c           	         ddl m} ddlm}m} t           |d |d                    j                   t          d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   )detect_install_method)CommandContextexecute_commandversionrX   )surfacezInstall directory: zInstall method: zPython: )rF  PackageNotFoundErrorzOpenAI SDK: openaizOpenAI SDK: Not installed)check_for_updates)recommended_update_commandr	   commitcommitszUpdate available: rl  u    behind — run 'r  z
Up to date)r  rC  hermes_cli.slash_execrD  rE  r8   rn  r  r
   rF  r  importlib.metadatarH  rM   r  rJ  rK  r   )
rA  rC  rD  rE  _pkg_versionrH  rJ  rK  behindcommits_words
             r   _print_version_inforS  6  sh   777777EEEEEEEE 
//)^^E%B%B%B
C
C
HIII	
.
.
.///	
B22<@@
B
BCCC 

-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   #C# ,C C# CC# CC# #C?>C?A	E+ E+ +
E98E9c                 &    t          d           dS )zShow version.Tr@  N)rS  r  s    r   cmd_versionrU  e  s    d++++++rA   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)
rn   hermes_cli.gui_uninstallrX  r8   r  r  r   hermes_cli.uninstallrY  r[  )r  rX  rY  r[  s       r   cmd_uninstallr^  j  s     t]E** @@@@@@dj,,..//000 tUE"" tUE** 	,*+++::::::$
 4&& "[!!!222222M$rA   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  rt  .venvr@   )r:  ds     r   r  z)_clear_bytecode_cache.<locals>.<listcomp>  s.     
 
 
OOO OOOrA   N__pycache__r	   )r   walkrg   basenamer  r  r   r  )r   rl  dirpathdirnamesr~  s        r   _clear_bytecode_cacherj    s     G "  1
 

 
 

 7G$$55g&&&1   NNNs   A,,
A98A9)L_add_upstream_remote_atomic_replace_dir_capture_head_sha_cmd_update_check_cmd_update_impl(_cold_start_windows_gateway_after_update_count_commits_between_detect_venv_python_processes_discard_lockfile_churn_discard_stashed_changes_ensure_acp_launcher_ensure_fhs_path_guard_ensure_uv_for_termux _finish_dashboard_update_cleanup_for_each_systemd_gateway_unit$_format_concurrent_instances_message_format_time_ago#_format_venv_python_holders_message_gateway_prompt_get_origin_url_has_upstream_remote_install_psutil_android_compat_invalidate_update_cache_is_android_python_is_fork_leftover_pausable_gateway_pids_log_only_write_mark_skip_upstream_prompt_npm_bin_exists_npm_lockfile_changed_npm_manifest_paths_npm_manifests_digest"_pause_windows_gateways_for_update_print_curator_first_run_notice _print_curator_recent_run_notice$_print_fts_optimize_available_notice_print_stash_cleanup_guidance_record_npm_lockfile_hash_refresh_active_lazy_features,_refresh_active_memory_provider_dependencies"_refresh_windows_gateway_launchers_reload_updated_runtime_modules_resolve_pre_update_backup_mode_resolve_stash_selector_restore_stashed_changes%_resume_windows_gateways_after_update_run_logged_subprocess_run_pre_update_backup_should_skip_upstream_prompt._stash_apply_failed_only_on_existing_untracked_stash_local_changes_if_needed_sync_fork_with_upstream_sync_with_upstream_if_needed_update_node_dependencies_update_via_zip _upgrade_pip_before_lazy_refresh_validate_critical_files_syntax!_validate_critical_modules_import_venv_core_imports_healthy_venv_launcher_ancestors%_wait_for_windows_update_gateway_exit&_warn_incomplete_gateway_fleet_restart_web_build_toolchain_ready_web_toolchain_roots%_write_lazy_refresh_incomplete_marker_write_marker_file_write_update_incomplete_marker!_write_update_planned_stop_marker_UPDATE_RUNTIME_RELOAD_MODULES_UPDATE_CRITICAL_FILES_UPDATE_CRITICAL_MODULESOFFICIAL_REPO_URLSOFFICIAL_REPO_URLSKIP_UPSTREAM_PROMPT_FILE_PRE_UPDATE_SNAPSHOT_KEEP"_PRE_UPDATE_SNAPSHOT_MAX_FILE_SIZEz.bytecode-fingerprintc                  N   	 t          t                    } | sdS t          t          z  }|                    |j        dz             }|                    | d           |                    |           dS # t          $ r&}t          	                    d|           Y d}~dS d}~ww xY w)u   Persist the current checkout fingerprint after a bytecode sweep.

    Never raises. A failed write just means the next launch re-sweeps —
    safe, merely redundant.
    Nz.tmpr]   r^   z)Could not record bytecode fingerprint: %s)
r  r  _BYTECODE_FINGERPRINT_FILE	with_namer   r  rg  r   r  r  )fingerprint
stamp_pathtmp_pathr;   s       r   _record_bytecode_fingerprintr    s    	G4\BB 	F!$>>
''
&(@AAK':::$$$$$ G G G@#FFFFFFFFFGs   A4 AA4 4
B$>BB$c                     	 t          t                    } | sdS t          t          z  }	 |                    d                                          }n# t
          $ r d}Y nw xY w|| k    rdS t          t                    }|r(t                              d|pd| ||dk    rdnd	           t                       dS # t          $ r&}t                              d
|           Y d}~dS d}~ww xY w)u7  Clear ``__pycache__`` at launch when the checkout changed underneath us.

    The stale-bytecode bug class (issues #6207, #60242; Dhruv's WhatsApp
    ``cannot import name 'parse_model_flags_detailed'`` report) has one
    shared shape: the checkout's ``.py`` files change (git pull inside
    ``hermes update``, a manual ``git pull``, a ZIP update, a file-sync
    restore) while ``__pycache__`` retains bytecode from the previous
    revision, and a later process trusts the stale ``.pyc`` instead of the
    fresh source.

    Update-time clears alone can never close this class: ``hermes update``
    always executes the PRE-pull updater code, so any hardening added to it
    only takes effect one update late, and manual ``git pull`` never runs
    the updater at all. This launch-time guard closes the loop: every
    ``hermes`` entry point compares the checkout fingerprint (cheap file
    reads, no git subprocess) against the last-validated stamp and sweeps
    the bytecode cache once when they diverge.

    Never raises — a failure here must not block launch.
    Nr]   r^   r   zVCheckout changed since last launch (%s -> %s): cleared %d stale __pycache__ director%sunknownr	   r?  iesz&Stale-bytecode launch sweep failed: %s)r  r  r  r  rr   r   rj  r  infor  r   r  )r  r  recordedrl  r;   s        r   )_sweep_stale_bytecode_if_checkout_changedr    s3   *D4\BB 	F!$>>
	!++W+==CCEEHH 	 	 	HHH	{""F'55 	KKh%I!||   	%&&&&& D D D=sCCCCCCCCCDsF   B; B; (A B; A"B; !A""	B; -AB; ;
C+C&&C+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 t                      }|                                sdS 	 t          j        |                    d	                    }n# t          t
          j	        f$ r Y dS w xY wt          |t                    sdS |                    d
          }|sdS t          ||           |k    S )a&  Return True if the web UI dist is missing or its source content changed.

    Uses a SHA-256 content hash of the web source tree (the same approach
    ``_desktop_build_needed()`` already uses for the Electron build), NOT
    mtime comparison. ``git checkout`` / ``git pull`` / ``hermes update``
    rewrite source mtimes without changing content, which made the old
    mtime check unreliable in both directions: it could skip a rebuild when
    source had genuinely changed (serving a stale dashboard) and force a
    rebuild when nothing had. A content hash is stable across mtime churn.

    The dashboard source lives under ``web/`` but Vite outputs to
    ``hermes_cli/web_dist/`` (per vite.config.ts outDir), NOT ``web/dist/``,
    so the dist directory is never part of the hashed source tree.
    apps
hermes_cliweb_distz.vitezmanifest.json
index.htmlTr]   r^   contentHash)r  r   rj   _web_ui_stamp_pathry  r  r  r  r   r  r   rp   rf   _compute_web_ui_content_hash)r  project_rootdist_dirsentinel
stamp_file
stamp_data
saved_hashs          r   _web_ui_build_neededr  =  s4    -4N,?6,I,I7>((w~Ll*Z7H'!O3H?? +l*?? t#%%J tZ
 4 4g 4 F FGG

T)*   ttj$'' t..J t'g>>*LLs   (B1 1C
Cr  c                 &    t          j                    dt          ddf fd}ddlm}  dz  }g }|                                r(|                    d	                                          }|                    d
|          dD ]]} |z  }|                                rBt          |
                                         }                    |          s ||           ^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 the web UI source tree.

    Covers ``web_dir`` (the dashboard frontend source) plus the root
    ``package.json`` / ``package-lock.json`` (workspace config that
    determines dependency resolution). Mirrors
    ``_compute_desktop_content_hash()``: ignored paths (``node_modules/``,
    ``dist/``, ``*.pyc``, ...) are skipped via the repo-root ``.gitignore``
    so build output never feeds back into its own staleness check.
    rg   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          $ r Y nw xY w                    d           d S )N    rbc                  .                          d          S Ni   rR  rT  s   r   <lambda>zB_compute_web_ui_content_hash.<locals>._hash_file.<locals>.<lambda>t      !&&-- rA   rA   )rq   r  r  encoderl   iterr   rg   r  chunkrT  hr  s      @r   
_hash_filez0_compute_web_ui_content_hash.<locals>._hash_filen  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   PathSpec
.gitignorer]   r^   	gitignorer  r  Ttopdownc           	          g | ]I}                     t          t                    |z                                                    G|JS r@   
match_filerq   r   r  r:  rd  rh  r  specs     r   r  z0_compute_web_ui_content_hash.<locals>.<listcomp>  \     
 
 
??3W(9'F'F|'T'T#U#UVV

 
 
rA   hashlibsha256r   pathspecr  ry  r  rm  
from_linesrq   r  r  r   rf  r  	hexdigest)r  r  r  r  r  linesr   r7  r  ri  	filenamesfnfprh  r  r  s   `            @@@r   r  r  b  s    	A
 
$ 
 
 
 
 
 
 
 "!!!!!|+IE C##W#55@@BB{E22D 6  499;; 	ammL1122C??3'' 
1
 )+(F(F(F 	 	$9
 
 
 
 
 

 
 
 ## 	 	Bg#Bbnn\2233C??3'' 
2		 ;;==rA   c                  (    ddl m}   |             dz  S )zBReturn the path to the web UI build stamp file under $HERMES_HOME.r   r'  zweb-ui-build-stamp.jsonr  r(  r'  s    r   r  r    s(    000000?888rA   c                    t                      }	 |j                            dd           ddlm}m} t          | |           |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)z6Write the web UI build stamp after a successful build.Tr  r   rE  timezone)r  builtAtr  r  r  r]   r^   z&Failed to write web UI build stamp: %sN)r  r  r  rE  r  r  r  utc	isoformatr  r  r  r   r  r  )r  r  r  rE  r  r  r;   s          r   _write_web_ui_build_stampr    s    #%%J
Dt<<<////////7gNN#x|HL11;;==
 

 	djA>>>EPWXXXXX D D D=sCCCCCCCCCDs   B
B 
C&CC       )idle_timeout_secondsr  r]  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]   rg  r	   )r  r   r   rn  r_   ri  bufsizer]  r  r   )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r   r_   asciirg  ri  )r   r8   rP  UnicodeEncodeErrorrn   r
   r  decoder  _time	monotonic)ro  encsafer  last_output_tsr  merged_chunksprocs      r   _readerz'_run_with_idle_timeout.<locals>._reader  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  rY  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LockrZ  Popenr+  STDOUTr   r4  rq   Threadr%  waitrp  	terminatekillrh   )rl  r  r  r  r]  r;   r  reader_threadidle_killedr   idler?  r=  r  r  r  r  s      `         @@@@r   _run_with_idle_timeoutr(    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 | j        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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)rb  rc  r  PYTHON)z	nix-shellr  r*  z--runzwhich python3Trg  FrJ   )rX  rn  r_   ri  ra  rY  )r  r   r  r   searchMr  r  r  rj   r   re   rq   rZ  r[  r\  r   rr   r   )r  
os_release	venv_namevenv_pythonr_  python3_paths         r   _nixos_build_envr2    s     III+,,666HH

   tt29]J55 t |I t ' > >	"Y.6B 	>=bj=(C,<,<=====	>
DDDdWYV[eg
 
 
 !!!=..00L >\ 2 2 9 9 ; ; >="*=h===    4s   #* 
885A5D, ,
D98D9r@   )
extra_argsrX  r]  r  r3  rX  c                   i t           j        |pi ddidt          t                   dt          j        ffddt          dt          j        ffd} ||           }|j        dk    r|S dd	lm} |j	        pd
 d|j
        pd
 } || |          }	|	s|S ddlm}
  |
          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.

    ``--include=dev`` is forced on every invocation: the callers are frontend
    builds (web UI / TUI / desktop workspaces), and those builds need the dev
    toolchain (``tsc``, ``vite``, ``electron-builder`` — all
    ``devDependencies``).  If the caller's environment has
    ``NODE_ENV=production`` (or npm config ``omit=dev``) — which leaks in from
    a shell profile, a container image, or the bundled TUI launcher that sets
    ``NODE_ENV=production`` on its subprocess env — npm silently omits
    devDependencies (exit 0, no error), so the build toolchain never installs
    and the subsequent build dies with ``tsc: command not found`` (exit 127).
    The flag overrides both the env var and npm config, unlike scrubbing
    ``NODE_ENV`` from the environment which only fixes the env-leak case.

    ``--no-save`` on the ``npm install`` fallback keeps it true to this
    function's contract: never mutate ``package-lock.json``.  Without it, an
    out-of-sync lockfile gets rewritten by the fallback, which drifts the
    committed lockfile and makes every future ``npm ci`` fail — a
    self-reinforcing cycle where web devDeps never install and a stale dist
    is served on every update (PR #65595).
    r)  r   rl  r   c                 *    t          |           S )N)r  r]  rX  )$_run_npm_watching_for_engine_failure)rl  rX  r  run_envs    r   _runz,_run_npm_install_deterministic.<locals>._runs  s&    3)	
 
 
 	
rA   npm_exec                     dz  }|                                 r | ddg          }|j        dk    r|S  | dddg          S )Nr  cir!  r   r   z	--no-save)rj   r\  )r9  lockfile	ci_resultr8  r  r3  s      r   _attemptz0_run_npm_install_deterministic.<locals>._attempt{  so    ,,?? 	!gt_JzJKKI#q((   tWioS
STTTrA   r   r/  r   r  r'  r  )r   re   r  rq   rZ  r4  r\  r5  r0  r   r   r  r(  )r  r  r3  rX  r]  r>  r_  r0  r?  r>  r(  r8  r7  s    ```       @@r   _run_npm_install_deterministicr?  J  s\   N 76	r6D#66G
$s) 

 ; 
 
 
 
 
 
 
 
U# U*"= U U U U U U U U Xc]]FA >=====-%2>>)<">>H**399L  766666++G44V<GFO8L!!!rA   c          
         |rt          j        | ||ddddd          S g }t          j        | ||t           j        ddd          5 }|j        \|j        D ]6}|                    |           t          j                            |           7t          j                                         |	                                }ddd           n# 1 swxY w Y   t          j
        | |dd                    |                    S )	a  Run *cmd*, always retaining stderr so ``EBADENGINE`` stays detectable.

    ``capture_output=False`` callers stream npm's progress live and would
    otherwise hand back a ``CompletedProcess`` with ``stderr=None``, leaving the
    engine-failure recovery nothing to read. Tee stderr instead: each line is
    forwarded to this process's stderr as it arrives (so live output is
    unchanged) and accumulated for the caller.
    Tr]   rg  F)r  r]  rX  rn  r_   ri  ra  )r  r]  r   rn  r_   ri  Nr   )rZ  r[  r  r+  r   r  r
   r   r   r"  r4  rh   )rl  r  r]  rX  capturedr  ro  r\  s           r   r6  r6    sR     

~	
 	
 	
 		
 H		
 
 
 ! 
;" ' '%%%
  &&&&JYY[[
! ! ! ! ! ! ! ! ! ! ! ! ! ! ! &sJbggh>O>OPPPs   A8C

CCoutputc           	          |                                  dD ].}t          fd| d| dd| dfD                       r|c S /dS )a  Return the build tool a failed ``npm run build`` could not resolve.

    Each shell words this differently: ``sh: 1: tsc: not found`` (dash),
    ``vite: command not found`` (bash/zsh), and ``'tsc' is not recognized as
    an internal or external command`` (cmd.exe).
    )tscvitec              3       K   | ]}|v V  	d S r>   r@   )r:  phraselowereds     r   r;  z*_missing_web_build_tool.<locals>.<genexpr>  s<       
 
 g
 
 
 
 
 
rA   z: not foundz: command not foundr  z' is not recognizedN)rs   r  )rB  toolrH  s     @r   _missing_web_build_toolrJ    s     llnnG 	 	 
 
 
 
 $$$,,,-D---
 
 
 
 
 	 KKK	 4rA   fatalrL  c                
   | dz                                   sdS 	 ddl}n!# t          $ r t          | |          cY S w xY w| j        j        dk    r| j        j        n| j        }|dz  dz  d	z  }	 t          |d
z  dd          }n!# t          $ r t          | |          cY S w xY w	 	 |                    |	                                |j
        |j        z             nh# t          $ r[ |                                 rY |                                 dS |                    |	                                |j
                   Y nw xY wt          | |          |                                 S # |                                 w xY w)u  Build the web UI frontend if npm is available, serializing across processes.

    Concurrent dashboard boots (e.g. the desktop app's retry loop after a
    readiness timeout) used to each spawn their own ``npm install`` +
    ``vite build`` over the same tree; the parallel builds starved each
    other, none finished, the dist sentinel never advanced, and every new
    boot re-triggered the build. One process builds under an exclusive
    flock; the rest serve the existing dist (stale is acceptable) or, when
    no dist exists yet, block until the builder finishes.

    Staleness is checked once, inside :func:`_do_build_web_ui`, after the
    lock is held — so a process that queued behind the builder skips the
    rebuild, and the (os.walk-based) check runs at most once per boot.
    r  Tr   NrK  r  r  r  r  z.web_ui_build.lockar]   r^   )rj   fcntlrM   _do_build_web_uir  r   rl   r   flockfilenoLOCK_EXLOCK_NBrh  )r  rL  rO  r  
dist_index	lock_files         r   _build_web_uirW    s    n$,,.. t6 6 6 6u5555556 -4N,?6,I,I7>((w~L,z9LHJ6(<<cGTTT		 6 6 6u5555556	;KK	((**EMEM,IJJJJ 	; 	; 	;  ""  
 	 KK	((**EM:::::	;  u555	sT     >>0B B$#B$)5C E, E=E, -EE, EE, ,Fc                   | dz                                   sdS t          |           sdS dt          ddfdddlm} t                      s|r d	            d
           | S  |             d           d,fd}t          |           | k    rdndt                      rt          |           \  dt          ddffd} |d          }|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    rt          |j        pd|j        pdz             }|r0 d| d             |d           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+           | j        j        d$k    r| j        j        n| j        }t-          ||            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  Trn  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  rg  r  )r8   r  rn   r
   r   r  r  )rn  r_   s     r   _sayz_do_build_web_ui.<locals>._say!  s    	^$KKKKK! 	^ 	^ 	^sz:t<<GH$++hy+99@@R[@\\]]]]]]	^s    A A76A7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]   rg  r  N)r   r   r   bytesr  rP  )r_  blobrn  rZ  s      r   _relayz _do_build_web_ui.<locals>._relay3  s     ]FM2 	 	D FPQUW\F]F]p4;;wy;99@@BBBcgcncncpcpD T


	 	rA   r@   )r  websilentc                 J    t          | r	g ddR ng dR           S )Nr"  z--prefer-offline)r3  r]  )r?  )ra  	build_envr  r,  r<  s    r   _install_web_depsz+_do_build_web_ui.<locals>._install_web_depsM  sl    -PV  FL+LZL9KLLL  ]F^p  ]F  sE  ]F  ]F	
 
 
 	
rA   )ra  r  u   ✗u   ⚠z Web UI npm install failedr   z# (hermes web will not be available)zD  Run manually:  npm install --workspace web && npm run build -w webFr[  r2  r  u     ⚠ Build could not resolve u%    — reinstalling web dependencies...r  
  ir  r  r  r  u<     ⚠ Web UI build failed — serving stale dist as fallbackz  Build error:
  z Web UI build failedu     ✓ Web UI built)r_  r[  r   N)rj   r  rq   r  r(  _resolve_node_runtime_npmr  rc  r  r  r\  r(  rJ  r   r   r  sleeprr   rh   rm  r  r   r  )r  rL  r(  r_  rd  r1r2missing_toolbuild_outputstderr_previewstderr_tailr  r  rU  rZ  rc  r  r,  r<  s                 @@@@@r   rP  rP    s    n$,,.. t(( t^3 ^4 ^ ^ ^ ^ 766666
#
%
%C  	WDFGGGDUVVVy%%''ID	!"""      g&&G 1870B0B""H^%'' Q&G&P&P##
T 
.K 
 
 
 
 
 
 
 
 
 
	$	'	'	'B	}F%*UFFFErr EG	
 	
 	
 	r


 	YDWXXXu 
 eW 57		R	R	RB	} /	RBIOQS/TUU 	[De,eeefffU++++'eW(=7PYZZZB=A KNNN'eW(=7PYZZZB	}
 	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	,3N,?6,I,I7>((w~LlG4444rA   desktop_dirc                 6    | dz  dz                                   S )z;Return True when a local desktop renderer build is present.r  r  rj   )rn  s    r   _desktop_dist_existsrq    s    & </77999rA   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.
    rg   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 )Nr  r  c                  .                          d          S r  r  r  s   r   r  zC_compute_desktop_content_hash.<locals>._hash_file.<locals>.<lambda>  r  rA   rA   )rq   r  r  r  rl   r  r   IOErrorr  s      @r   r  z1_compute_desktop_content_hash.<locals>._hash_file  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   r  r  r]   r^   r  r  r  r>  Tr  c           	          g | ]I}                     t          t                    |z                                                    G|JS r@   r  r  s     r   r  z1_compute_desktop_content_hash.<locals>.<listcomp>  r  rA   r  )r  r  r  r  r  r   r7  r  rn  ri  r  r  r  rh  r  r  s   `            @@@r   _compute_desktop_content_hashrw    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		 ;;==rA   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.jsonr  r'  s    r   _desktop_stamp_pathry    s(    000000?999rA   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^   
sourceModer  )rq  _desktop_packaged_executablery  ry  r  r  r  r   r  KeyErrorrf   rw  )rn  r  rz  r  r  r  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  )r  r|  r   r  r  r  r]   r^   z'Failed to write desktop build stamp: %sN)ry  r  r  rw  rE  r  r  r  r  r  r  r  r   r  r  )r  rz  r  content_hashrE  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                    | 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           j        dk    r3t	          |          dk    r t                      fd|D             }|r|}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-unpackedrG   Hermeszlinux-arm64-unpackedc                 :    g | ]}|                                 |S r@   rp  rf  s     r   r  z0_desktop_packaged_executable.<locals>.<listcomp>?  s%    444a4444rA   Nr	   c                 6    g | ]}t          |          v |S r@   )_pe_machine_or_none)r:  r7  expecteds     r   r  z0_desktop_packaged_executable.<locals>.<listcomp>J  s,    NNN!+>q+A+AX+M+MA+M+M+MrA   c                 4    |                                  j        S r>   )r  r  r7  s    r   r  z._desktop_packaged_executable.<locals>.<lambda>M  s    qvvxx'8 rA   )r}  )r
   rO   r  globr  _expected_windows_pe_machinesr  )rn  release_dirrS  existingmatchingr  s        @r   r}  r}  ,  sA   	)K
|x+**+RSSTT

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


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

 54:444H t
|w3x==1#4#4 122NNNNxNNN 	 Hx889999rA   iL  id  id  zx86 (32-bit)zx64 (AMD64)ARM64AMD64X86c                  \   ddl } ddl m} |                     dd          }|j        |j        _        g |j        _        |j        |                     |j                  |                     |j                  g|j	        _        |j
        |j	        _        |                    d          }|                    d          }|	                    |                                |                     |          |                     |                    sdS t                              |j                  S )a  Ask IsWow64Process2 for the OS-native machine (None if unavailable/fail).

    ctypes defaults ``GetCurrentProcess``'s restype to ``c_int``, so the
    current-process pseudo-handle ``(HANDLE)-1`` is truncated to
    ``0xFFFFFFFF`` and zero-extended into a 64-bit invalid handle. On Win64
    that makes ``IsWow64Process2`` fail with ``ERROR_INVALID_HANDLE`` (6),
    which is exactly the residual Windows-on-ARM failure after #71218: the
    gate fell through to ``PROCESSOR_ARCHITECTURE=AMD64`` (the emulated
    process arch) and rejected a correctly-built ARM64 ``Hermes.exe``.
    Binding ``restype``/``argtypes`` to ``wintypes.HANDLE`` keeps the full
    ``0xFFFFFFFFFFFFFFFF`` pseudo-handle.
    r   Nwintypeskernel32Tuse_last_error)rN   r  WinDLLHANDLEGetCurrentProcessrestypeargtypesPOINTERUSHORTIsWow64Process2BOOLbyref_PE_MACHINE_TO_NAMErf   rt   )rN   r  r  process_machinenative_machines        r   $_windows_native_machine_from_iswow64r  t  s    MMM}}Z}==H)1H&*,H'x''x'')H%
 (0}H$ooa((O__Q''N##""$$_%%^$$  
 t"">#7888rA   c                     ddl } ddl m} |                     dd          }|j        |                     | j                  g|j        _        | j        |j        _	        t                      }t          t          t          fD ]e}|                     d          }|                    ||                     |                    rA|j        t           z  r|                    |           f|pdS )u>  PE machines this host can run in user mode, via GetMachineTypeAttributes.

    This asks the question the integrity gate actually cares about — "can this
    Windows host load a PE of machine X?" — instead of inferring it from a
    host-architecture name. It is also the only documented API that reports
    AMD64-on-ARM64 emulation support; ``IsWow64GuestMachineSupported`` only
    answers for 32-bit guests.

    Returns None when the API is unavailable (pre-Windows-11 build 22000) or
    reports nothing runnable, so callers fall back to name-based detection.
    r   Nr  r  Tr  )rN   r  r  r  r  c_intGetMachineTypeAttributesr  c_longr  set_PE_MACHINE_ARM64_PE_MACHINE_AMD64_PE_MACHINE_I386r  rt   _MACHINE_ATTRIBUTE_USER_ENABLEDr   )rN   r  r  runnablemachine
attributess         r   "_windows_user_runnable_pe_machinesr    s     MMM}}Z}==Hv|$$2H%. 17H%-uuH%'8:JK " "\\!__
,,Wfll:6N6NOO 	== 	"LL!!!trA   c                     t           j        dk    r	 t                      } n%# t          t          t
          t          f$ r d} Y nw xY w| r| S t          j        	                    d          pt          j        	                    d          }|r|
                                S ddl}|                                pd
                                S )u  The Windows host OS's NATIVE machine architecture, normalized upper.

    ``platform.machine()`` reports the PROCESS architecture, which lies under
    emulation: the desktop update chain runs an x64 hermes-setup.exe (and thus
    x64 Python) on Windows-on-ARM devices, where ``platform.machine()``
    returns ``AMD64`` even though the OS is ARM64. The #71119 integrity gate
    then rejected the CORRECT ARM64 rebuild as an "architecture mismatch"
    (#69179 follow-up report). Probe order:

    1. ``IsWow64Process2`` with a correctly-typed current-process HANDLE
       (#71218 + HANDLE-truncation fix). This is the only API that tells the
       truth from an x64 process emulated on ARM64.
    2. ``PROCESSOR_ARCHITEW6432`` / ``PROCESSOR_ARCHITECTURE`` — WOW64
       (32-bit) hosts and pre-1511 Windows 10 without the newer API.
    3. ``platform.machine()``.

    Note ``GetNativeSystemInfo`` is deliberately NOT used: Microsoft documents
    that it "also returns emulated processor details when run from an app
    under emulation", so on the very WoA hosts this function exists to serve
    it reports AMD64 — no better than the env-var rung below it.
    r  NPROCESSOR_ARCHITEW6432PROCESSOR_ARCHITECTUREr   r   )r
   rO   r  r   rL  	TypeErrorr   r   re   rf   upperr  )r   env_arch	_platforms      r   _windows_native_machiner    s    , |w	799DDJ? 	 	 	 DDD	  	K:>>":;; 
rz~~$@
 @
  	$>>###    %2,,...   ! AAc                  \   t           j        dk    r9	 t                      } n%# t          t          t
          t          f$ r d} Y nw xY w| r| S t                                                      }|dv rt          t          hS |dv rt          t          hS |dv rt          hS t          t          t          hS )a  PE machine values the current Windows host can natively load.

    Preferred source is ``GetMachineTypeAttributes``, which answers this
    question directly (including AMD64-on-ARM64 emulation) instead of
    inferring it from an architecture name.

    Fallback is name-based: AMD64 hosts run x64 and (via WOW64) x86. ARM64
    hosts run ARM64 and (Windows 11 emulation) x64. 32-bit x86 hosts run only
    x86. Unknown machines return the permissive full set so the integrity gate
    can never brick launch on exotic hosts. Host detection uses the OS-native
    machine (see ``_windows_native_machine``), not the process architecture.
    r  N)r  X86_64X64)r  AARCH64)r  I386I486I586I686)r
   rO   r  r   rL  r  r   r  r  r  r  r  )r  r  s     r   r  r    s     |w	9;;HHJ? 	 	 	HHH	 	O%''--//G,,,!#344&&&!#455999 !!02BCCr  c                    ddl }	 |                                 j        }n$# t          $ r}t	          d|           d}~ww xY w|dk     rt	          d| d          |                     d          5 }|                    d          }t          |          dk     s|dd	         d
k    rt	          d          |                    d|d          d         }|dk    s	|dz   |k    rt	          d          |	                    |           |                    d          }t          |          dk     s|dd         dk    rt	          d          |                    d|d          \  }}	|                    d|d          d         }
|	                    |dz   |
z              d}t          |	          D ]f}|                    d          }t          |          dk     rt	          d          |                    d|d          \  }}t          |||z             }g||k     rt	          d| d| d          	 ddd           n# 1 swxY w Y   |S )u  Parse ``path`` as a PE executable and return its COFF machine field.

    Raises ``ValueError`` with a human-readable reason when the file is not a
    structurally complete PE: missing MZ/PE magic (an HTML error page or JSON
    body saved as .exe), header truncation, or raw section data extending past
    the end of the file (the truncated-download / interrupted-extraction
    shape). Purely a header walk — cheap even on a 200 MB Electron exe.
    r   Nzunreadable: i   zfile is only u3    bytes — far too small to be a Windows executabler  @   r  s   MZu^   missing MZ header — not a Windows executable (a truncated or non-binary file saved as .exe?)z<I<      z<corrupt DOS header: PE header offset points past end of filer  s   PE  u2   missing PE signature — corrupt executable headerz<HHz<Hr  r  ztruncated PE section tablez<II   ztruncated executable: file is z% bytes but its PE sections extend to  bytes)structr  r  r   r   rl   rR  r  unpack_fromseekr*  r  )rg   r  	file_sizer;   fhr  e_lfanewpe_headr  
n_sectionssize_of_optionalmax_section_endr~  sectionsize_of_rawpointer_to_raws                   r   _parse_pe_machiner    s    MMM/IIKK'		 / / /---.../3ZIZZZ
 
 	
 
4 Bwwr{{t99r>>T"1"X..B   %%dD$77:q==HrMI55[\\\
''"++w<<"} < <QRRR$00CC!--dGR@@C
2 00111z"" 	Q 	QAggbkkG7||b   !=>>>*0*<*<UGR*P*P'K!/>K3OPPOO&&> > >&5> > >   '1              : Ns%     
A<A2GI  IIc                 D    	 t          |           S # t          $ r Y d S w xY wr>   )r  r   rc  s    r   r  r  4  s7     &&&   tts    
c                     	 t          |           }n&# t          $ r}t          |          cY d}~S d}~ww xY wt                      }||vr4t                              |d|d          }d| dt                       dS dS )zReturn a human-readable reason ``path`` cannot run on this Windows host,
    or ``None`` when the exe parses as a complete PE of a loadable architecture.
    Nzunknown machine 0x04Xzarchitecture mismatch: built a z executable but this is a z Windows host)r  r   rq   r  _PE_MACHINE_NAMESrf   r  )rg   r  r;   r  gots        r   _desktop_exe_integrity_errorr  ;  s    #D))   3xx,..Hh##G-O'-O-O-OPP8c 8 8&((8 8 8	
 4s    
5055packaged_executablec                 4    | j         }|j         |j        dz   z  S )zDThe rollback tree before-pack.mjs preserves: ``<unpacked-dir>.bak``.z.bak)r  r   )r  unpackeds     r   _desktop_backup_unpacked_dirr  M  s    ")H?hmf455rA   c                    | j         }t          |           }|| j        z  }|                                sdS t	          |          dS |j         |j        dz   z  }	 t          j        |d           	 |                    |           n&# t          $ r t          j        |d           Y nw xY w|                    |           n# t          $ r Y dS w xY w|| j        z  }|                                r|ndS )aI  Restore the previous unpacked desktop app from its ``.bak`` tree.

    Returns the restored executable path, or ``None`` when no usable backup
    exists (missing, or its exe fails the same integrity probe). The corrupt
    tree is kept alongside as ``<unpacked-dir>.corrupt`` for diagnostics.
    Best-effort: never raises.
    Nz.corruptTr  )	r  r  r   rj   r  r  r  renamer   )r  r  
backup_dir
backup_execorrupt_dirrestoreds         r   _rollback_desktop_from_backupr  S  s1    #)H-.ABBJ166J t#J//;t/X]Z%?@Kk6666	8OOK(((( 	8 	8 	8M($777777	8(####   tt-22H((288d2s6   C 2B C  B+(C *B++C 
CCc                    |t           j        dk    r|dfS t          |          }||dfS t          d|            t          d|            t	          |            	 t                                                       n# t          $ r Y nw xY wt          |          }|1t          d           t          d           t          d           |d	fS t          d
           t          d           t          d           dS )u|  Windows post-build integrity gate for the self-update rebuild (#69179).

    Returns ``(verified_exe_or_None, rolled_back)``:

    - exe passed the probe → ``(exe, False)``
    - exe corrupt/wrong-arch, previous build restored → ``(old_exe, True)``
    - exe corrupt and nothing restorable → ``(None, False)``

    On any integrity failure the corrupt cached Electron zip is purged and the
    desktop build stamp invalidated, so the updater's retry-once rebuild pulls
    a fresh, SHASUM-verified Electron download instead of re-staging the same
    corrupt bytes. No-op off Windows and when there is no executable to check.
    Nr  Fu5   ✗ The built Hermes.exe failed its integrity check: z    at: uN     ↩ Update aborted — restored the previous working Hermes.exe from backup.zH    Your existing version was kept and still works. Run `hermes desktop`zI    (or the in-app update) again to retry with a fresh Electron download.Tu,     ✗ No usable backup was found to restore.zG    Run `hermes desktop --force-build` to rebuild, or re-run the Hermesz$    installer to repair the install.NF)	r
   rO   r  r8   _purge_electron_build_cachery  r  r   r  )rn  r  r"  r  s       r   _ensure_desktop_exe_launchabler  p  s=     "clg&=&="E))()<==E}"E))	
I%
I
IJJJ	
*(
*
*+++
  ,,,$$&&&&    --@AAH^___XYYYYZZZ~	
8999	
STTT	
0111;s     B 
BB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   ru   r   re   rf   r  r
   rO   r  ri   r   )	ru   rS  overridelocalxdgseenoutr5  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JrA   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  r  r  r   r  r  r  )rn  rl  	cache_dirzip_pathr  r  s         r   r  r    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  rp  )r  desktop_locals     r   _electron_dirr    sG     !6)I5FSM .(:55rA   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  r
   rO   )r  r  s     r   _electron_dist_binaryr    s`     &&/D
|xn$z1G;jHH
|wn$$*rA   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  rj   r   )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  ry  r  )r  electron_dirs     r   !_electron_pkg_staged_missing_distr  5  sT     ..L		&//11 	0L(1133	0!,///rA   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]  ra  )r  r  ry  r  r  r(  r  r  r  r   rZ  r[  rq   )
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  rf   _ELECTRON_FALLBACK_MIRROR)r  r]  s     r   _try_redownload_electron_distr  d  sI     s33 t
ww !! u$\3?XYYYYrA   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  )r
   rO   psutilr   r{  r   r   r   getpidprocess_iterr  rf   r   r   r  r  r#  r   r   
wait_procsr$  )rn  r"  r  mevictims	proc_iterr  r  r   r!  exe_pathstoppedr~  alives                 r   %_stop_desktop_processes_locking_buildr,  m  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?bundlec                    ddl }| dz  dz  }|                                sZ| j        dk    rOt          |                     d                    t          |                     d                    z   }|r|d         }|                                sdS 	 |                    |                                          }n# t          $ r Y dS w xY w|                    d          }|rt          |          ndS )	zEReturn a bundle/framework CFBundleIdentifier for local macOS signing.r   Nr
  z
Info.plist
.frameworkzVersions/*/Resources/Info.plistzResources/Info.plistCFBundleIdentifier)
plistlibrj   r  r  r  r  
read_bytesr   rf   rq   )r-  r1  r  rS  r  idents         r   _desktop_macos_bundle_idr4    s   OOOJ-D;;== !V]l::&++&GHHIIDKK.//M
 M
 

  	!a=D;;== t~~doo//00   ttHH)**E(3u:::D(s   'B: :
CCc                  x   t           j        dk    rdS 	 ddlm}   |                                 di           }t          |t                    sdS |                    d          }t          |t                    sdS |                                pdS # t          $ r}t          d| d           Y d}~dS d}~ww xY w)	u|  Return the opt-in keychain identity for local macOS desktop signing.

    ``desktop.macos_signing_identity`` in config.yaml names a persistent
    code-signing certificate in the user's login keychain (a self-signed
    "Code Signing" cert made in Keychain Access is enough — no Apple Developer
    account needed). Signing with any identity gives the app a
    certificate-anchored Designated Requirement, which is the strongest way to
    keep macOS TCC grants (Full Disk Access, Accessibility, Automation, Files
    and Folders) stable across local rebuilds. Empty/unset keeps the default
    identifier-pinned ad-hoc signing.
    r  Nr   r  r>  macos_signing_identityz;  (warning: could not load desktop.macos_signing_identity: z!; falling back to ad-hoc signing))r
   rO   r  r  rf   r   rp   rq   rr   r   r8   )r  r>  r4  r;   s       r   %_desktop_macos_local_signing_identityr7    s     |xt111111+--##Ir22'4(( 	4;;788(C(( 	4~~'4'   66 6 6	
 	
 	
 ttttts#   9B *B ;B 
B9B44B9appc           	      Z   t          j        d          }|sdS 	 t          j        |dt	          |           gddd          }|j         d|j         }|j        dk    sd|vsd	|v rdS t          j        |d
ddt	          |           gdd          }|j        dk    S # t          $ r Y dS w xY w)u  True when the bundle carries an intact non-ad-hoc (Team ID) signature.

    Used to make the relaunch fixup a no-op on properly signed/notarized
    builds even when CSC_LINK / APPLE_SIGNING_IDENTITY aren't in the
    environment (e.g. a release DMG install being repaired) — clobbering a
    Developer ID signature with an ad-hoc one would reset TCC grants and can
    break the hardened runtime. A *stale* real signature (in-place rebuild
    tampered with the bundle) fails --verify and returns False so the fixup
    can repair it.
    codesignFz-dvT)ra  rX  rn  r  r   zTeamIdentifier=zTeamIdentifier=not set--verify--deep--strictra  rX  )	r  r  rZ  r[  rq   r   r   r\  r   )r8  r:  r  rB  verifys        r   '_desktop_macos_has_valid_real_signaturer@    s     |J''H u~uc#hh'uTPT
 
 
 K004;00?a#4F#B#B+v555z8ZSB
 
 
  A%%   uus   AB (3B 
B*)B*r  r  r4  c                   t          j        d          sdS |dz  dz  }|dz  dz  }|                                r|                                st          d|dz             dddd	d
t          dt
          t                   dt
          t                   dt          ddf
fd}| dz  }g }t          j	        |          D ]u\  }}	}
t	          |          }|
                    |           j        }t          d |D                       rI|
D ])}||z  }|dv s	|j        dv r|                    |           *vt          |d d          D ]} ||d           g }|dz  }|                                rHt          j	        |          D ]3\  }}	}t	          |          }|j        dv r|                    |           4t          t!          |          d d          D ]5}|j        dk    rd|j        v r|nd} |||t%          |                     6 || |t%          |                      t'          j        dddt          |           gdd            dS )!u  Re-sign a local Desktop build so macOS TCC grants survive rebuilds.

    A plain ``codesign --deep --sign -`` leaves the bundle with a cdhash-only
    Designated Requirement and strips electron-builder's entitlements. Every
    rebuild changes the cdhash, so TCC (Full Disk Access, Accessibility,
    Automation, Files and Folders: Desktop/Downloads/Documents, microphone)
    treats the rebuilt app as different code and the user must re-grant
    everything — and the lost entitlements break microphone/JIT under the
    hardened runtime.

    Instead, sign inside-out (standalone Mach-O binaries, then nested
    frameworks/helper apps, then the main bundle), preserving the repo's
    entitlement plists, and pin an explicit identifier-based Designated
    Requirement when signing ad-hoc. With a real ``identity`` the certificate
    anchors the DR, so no explicit requirement is needed. Raises on signing
    failure; returns True after strict verification passes.
    r:  Fr  zentitlements.mac.plistzentitlements.mac.inherit.plistz)desktop entitlement plists missing under NT)entitlements
identifierr  rg   rB  rC  r  r   c                   dddg}|r|ddgz  }|(|                                 r|dt          |          gz  }|rdk    r|dd	| d
gz  }|                    t          |                      t          j        |dd           d S )N--force--signz--timestamp=nonez	--optionsr  z--entitlementsr  z--requirementsz=designated => identifier "r  Tr>  )rj   rq   r  rZ  r[  )rg   rB  rC  r  r  r:  r4  s        r   	sign_pathz0_desktop_macos_local_codesign.<locals>.sign_path.  s     )Xx9KL 	-[),,D#(;(;(=(=#%s<'8'899D 	T(c// %'RZ'R'R'RSSDCIIt4======rA   r
  c              3   @   K   | ]}|                     d           V  dS ).appN)endswithrG  s     r   r;  z0_desktop_macos_local_codesign.<locals>.<genexpr>J  s.      ;;t}}V$$;;;;;;rA   >   spawn-helperchrome_crashpad_handler>   .node.dylibc                 *    t          | j                  S r>   r  rp  r  s    r   r  z/_desktop_macos_local_codesign.<locals>.<lambda>S  s    s17|| rA   )r}  reverse)r  
Frameworks>   rI  r/  c                 *    t          | j                  S r>   rP  r  s    r   r  z/_desktop_macos_local_codesign.<locals>.<lambda>^  s    S\\ rA   rI  Helper)rB  rC  r;  r<  r=  r>  )r  r  rj   r  r   r   rq   r  r   rf  r  rp  r  r  r  r  r  r   r4  rZ  r[  )r8  rn  r4  ent_mainent_inheritrG  contents
standaloner   _dirsfiles	root_path	rel_partsr   r  bundlesframeworks_dir_filesr7  r-  entr:  s     `                  @r   _desktop_macos_local_codesignra  
  s>   ( |J''H uZ'*BBH
*-MMKOO 
+"4"4"6"6 
  Rj8PRR
 
 	
 (,$(> > >> tn> SM	>
 > 
> > > > > > >. ZHJ gh// & &eUJJ	))#..4	;;;;;;; 	 	& 	&DT!BBBBbi T G G !!"%%%	& Z%;%;TJJJ % %	"e$$$$$ G,N "#%7>#:#: 	" 	"D%T

Ax111q!!!W+A+A4PPP Y Y#]f44V[9P9PkkVZ	&s7OPV7W7WXXXXX Ic5Mc5R5RSSSSN	:xSXX>4    4rA   )publisher_signing_configuredrb  c          
         t           j        dk    rdS |Kt          t          j                            d          pt          j                            d                    }|rdS t          |           }|dS |j        d         }t          |          	                    d          r|
                                sdS t          j        d          }|sd	S t          |          rdS t          j        d
dt          |          gd	           t!                      pd}	 t#          || |          r|dk    rdnd}t%          d| d           dS nC# t&          $ r6}|dk    rt%          d|d           t%          d| d           Y d}~nd}~ww xY w	 t          j        |ddddt          |          gd	           n*# t&          $ r}t%          d| d           Y d}~nd}~ww xY wd	S )u`  Make a locally-built macOS desktop app survive in-place self-update
    without resetting the user's TCC permission grants.

    An ad-hoc-signed .app has no stable Designated Requirement, so when the
    self-updater rebuilds the bundle in place (new cdhash) Gatekeeper reports
    "Hermes is damaged and can't be opened" — and macOS TCC forgets every
    permission the user granted (Full Disk Access, Desktop/Downloads/Documents,
    Accessibility, Automation, microphone), re-prompting on every launch after
    every update.

    Clear the quarantine xattrs, then re-sign with a stable identity:
    ``desktop.macos_signing_identity`` (a persistent keychain cert — strongest)
    when configured, else ad-hoc with identifier-pinned Designated Requirements,
    preserving the repo's entitlement plists either way. No-op when a real
    publisher identity is configured (CSC_LINK / APPLE_SIGNING_IDENTITY) or the
    bundle already carries an intact Developer ID signature, so a properly
    signed/notarized build is never clobbered. Callers that already made the
    publisher-signing decision may pass it explicitly so a later dotenv load
    can't reverse it. Falls back to the legacy deep ad-hoc sign if the
    entitlement-preserving path fails. Best-effort: never raises. Returns True
    when no work was needed or signing + strict verification succeeded.
    r  TNCSC_LINKAPPLE_SIGNING_IDENTITYr  rI  r:  Fxattrz-crra  r  )rn  r4  zkeychain identityzstable ad-hoc identityu      → macOS desktop signed with z$; TCC grants persist across rebuildsz6  (warning: configured macOS signing identity failed: uB   ; falling back to ad-hoc — TCC grants may need to be re-granted)z)  (warning: stable macOS signing failed (z); using legacy ad-hoc sign)rE  r<  rF  z*  (warning: macOS relaunch fixup skipped: r  )r
   rO   r  r   re   rf   r}  r  rq   rJ  r   r  r  r@  rZ  r[  r7  ra  r8   r   )rn  rb  r!  r8  r:  r4  rQ  r;   s           r   !_desktop_macos_relaunchable_fixuprh  k  ss   6 |xt#+'+JNN:&&R"*..9Q*R*R(
 (
$ $ t
&{
3
3C
{t
+a.Cs88V$$ CJJLL t|J''H u.s33 tNGUCHH-U;;;;466=#H](+PXYYY 	+3s??''@XE`U```aaa4	  ] ] ]s??S S S S   	[#[[[\\\\\\\\]C)Xxc#hhOW\]]]]] C C CA3AAABBBBBBBBC5s0   /E 
F,F		F)F< <
G#GG#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  Frd  re  CSC_IDENTITY_AUTO_DISCOVERYfalseT)r
   rO   rf   )r]  rz  s     r   _force_adhoc_macos_signingrl    sh    $ |x;u
wwz cgg&>?? u$++u)0C%&4rA   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^   r   N)	r
   rO   r   r   r   rl   rR  rr   r   r  s    r   _desktop_linux_needs_no_sandboxro    s      |wur9 "*,,!"3"3uJU\]]] 	+ab6688>>##s*	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+   uus6   B *B
=B 
BB BB 
B%$B%c                     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.rn  Fchrome-sandbox)r
   rO   r  lstatr   r  S_ISREGst_mode)r  sandboxsandbox_lstats      r   -_desktop_linux_sandbox_helper_is_regular_filerw    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.rn  Trq  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	  r{  uN   ✗ Hermes Desktop requires sudo to configure Electron's Linux sandbox helper.u@   → Configuring Electron Linux sandbox helper (sudo required)...chownz	root:rootchmod4755rg  u9   ✗ Failed to configure Electron's Linux sandbox helper: )r
   rO   r  rj   r8   rr  r   r  rs  rt  st_uidS_IMODEr  r  rq   rZ  r[  r\  )r  ru  rv  r{  commands        r   _desktop_linux_sandbox_fixupr    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   r  r>  electron_flagsnt)posixc                 n    g | ]2}t          |                                          #t          |          3S r@   )rq   rr   )r:  rT  s     r   r  z+_desktop_launch_options.<locals>.<listcomp>/  s1    ===Ac!ffllnn=Q===rA   disable_gpur   0)r   r  r  r  )r  rk  nor  )r  r  rf   r   r   rq   shlexr  r   r   r  r  r  rr   rs   )flagsr  r  desktop_cfg	raw_flagsraw_gpur  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   '. ??r  c           	      
   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	}  |            }t          | dd          rd|d<   t          | dd          rd|d<   t          | dd          rHt          t          | j                                                                                            |d<   t          | dd          rIt          t          | j                                                                                            |d<   nt#          j                    |d<   t'                      \  }}|dk    rdt"          j        vr||d<   t          | dd          }t          | dd          }t          | dd          }	t+          |          }
|s|sCt-                      }|s2t          d           t          d           t          j        d           nd}|rA|rt/          |          sGt          d|dz              t          d           t          d            t          j        d           t1          t                     dz                                  sJt          d!           t          d"t            d#           t          d            t          j        d           t          d$|dz              nb|
It          d%|d&z              t          d'           t          d(           t          j        d           nt          d)|
            n|	pt3          |t           |*          }|s|rd+nd,}t          d-| d.           nt          d/            |t5                                }t7          |t           d|0          }|j        dk    rt;          t                     sBt          d1           t          d2t            d#           t          j        |j        pd           t=          t           |          }|rt          d3           nt          d4           |rd+nd,}t          d5| d6           |rd7nd8}t?          ||*          rt          d9           |sJtA          |          }|r9t          d:d;!                    tE          t          |                     d<           tG          j$        |d=|g||d>          }|j        dk    r|st+          |          g }d}tK          t                     s$tM          |          }tO          t           |          }|rPt          d?           |D ]}t          d@|            tA          |           tG          j$        |d=|g||d>          }|j        dk    r|s|(                    dA          st+          |          t          dB           tR          }tU          |          }||dA<   tK          t                     stO          t           ||C           tA          |           tG          j$        |d=|g||d>          }|j        dk    rt          dD           t          dE|            t          j+        dFk    rt          dG           t          dH           t          dI           t          dJ           t          j        |j        pd           t+          |          }
|s>tY          |           t[          ||
          \  }}|
|s|t          j        d           |}
t]          t           |*           t          | dKd          r|rOt/          |          s)t          dL|dz              t          j        d           t          dM|dz   dN           nN|
9t          dO|d&z              t          dP           t          j        d           nt          dQ|
 dN           dS |rEt          dR           tG          j$        |dSdTdUdVg||d>          }t          j        |j                   |
8t          dW|d&z              t          dP           t          j        d           t          |
          g}t_          |
          sVta                      r4tc          |
          r%t          dX           |2                    dY           nt          j        d           |3                    |           t          dZd[!                    |                      tG          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: r	   r   r4  r<  r@  r'  	fake_bootFr   HERMES_DESKTOP_BOOT_FAKEignore_existingHERMES_DESKTOP_IGNORE_EXISTINGr   NHERMES_DESKTOP_HERMES_ROOTr  HERMES_DESKTOP_CWDr,  HERMES_DESKTOP_DISABLE_GPUr  
skip_buildforce_buildz@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 )rz  zsource buildzpackaged appu   ✓ Desktop z& is up to date (content stamp matches)u0   → Installing desktop workspace dependencies...)rX  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 rJ  r2  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 r  r  r[  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...r  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: rl  )4r  rj   r8   r
   r   hermes_loggingr5  r   r  r(  rn   rq   r   r   ri   r{  r  r   r^  r  re   r}  rf  rq  r  r  r2  r?  r\  r  r  rl  r,  rh   maprZ  r[  r  r  r  rf   r  rp   rO   rh  r  r  r  ro  rw  r  r  )r  rn  _setup_logging_guir(  r]  config_electron_flagsconfig_disable_gpurz  r  r  r  r  build_neededbuild_label	nixos_envinstall_resultrepairedbuild_scriptr*  build_resultpurgedr  r7  r  
mirror_envverified_executablerolled_backlaunch_resultlaunch_commands                                r   cmd_guir  ?  s
   ')3K.(0022 ?+??@@@FFFFFF&&&&&    766666  

!
!Ct[%(( .*-&'t&.. 403,-t]D)) _,/T5E0F0F0Q0Q0S0S0[0[0]0],^,^()tUD!! 0$'TX(A(A(C(C(K(K(M(M$N$N !!$&IKK ! 1H0I0I--V##(DBJ(V(V,>()$%00K|U33J$u55K6{CC * ')) 	TUUU:;;;HQKKK T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 # 
&;;'
 '
 '
  x	N,7K..^KTTTTUUUUDEEE .-.>.@.@AAI;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+>>> 4R!44 40#[ '2 3#6#>HQKKK&9# '|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,)#_detect_concurrent_hermes_instances_kill_stale_dashboard_processes_scan_dashboard_processesexclude_pidsr  c                 6    d t          |           D             S )zJReturn PIDs of stale ``dashboard``/``serve`` processes for update cleanup.c                     g | ]\  }}|S r@   r@   )r:  r   _cmds      r   r  z._find_stale_dashboard_pids.<locals>.<listcomp>B  s    VVVICCVVVrA   r  )r  r  s    r   _find_stale_dashboard_pidsr  =  s%    
 WV!:!U!U!UVVVVrA   r~  c                     d}t           fddD                       rd}nt           fddD                       rd}|dS d}d	}t          j        d
           }|r5	 t          |                    d                    }n# t
          $ r Y dS w xY wt          j        d           }|r*|                    d                              d          pd	}|||fS )zJBest-effort parse of a dashboard/server cmdline into mode, host, and port.Nc              3       K   | ]}|v V  	d S r>   r@   r:  patternr~  s     r   r;  z+_parse_dashboard_runtime.<locals>.<genexpr>H  s<         	7     rA   )zhermes dashboardzhermes_cli.main dashboardzhermes_cli/main.py dashboardr?  c              3       K   | ]}|v V  	d S r>   r@   r  s     r   r;  z+_parse_dashboard_runtime.<locals>.<genexpr>Q  s<         	7     rA   )zhermes servezhermes_cli.main servezhermes_cli/main.py server=  i#  rQ  z(?:^|\s)--port(?:=|\s+)(\d+)r	   z/(?:^|\s)--host(?:=|\s+)(\"[^\"]+\"|'[^']+'|\S+)z"')r  r  r,  r   rp  r   rr   )r~  rA  porthost
port_match
host_matchs   `     r   _parse_dashboard_runtimer  E  sM   D
    
      	    
   
 
  |tDD:GDDJ 	z''**++DD 	 	 	44	 MwWWJ ?""((//>;ts   "B 
BBr  c                 d    | pd                                                      d          }|dv rdS |S )zDMap wildcard binds to a loopback address suitable for local probing.rQ  z[]>   0.0.0.0r   ::rF  )r  rN  s     r   _dashboard_probe_hostr  n  s=    %+,,..44T::J***{rA   zhermes-dashboard.servicer   unitc           	         t           j        dk    rdS dddt          dt          dt          j        fd}d	}d	}d
D ]~}	  |g |dddR  }n"# t          t          j        t          f$ r Y 2w xY w|j	        dk    rB|j
        pd                                }t          fd|D                       r|}|} n||dS 	  |g |dR  } |g |dR  }	n## t          t          j        t          f$ r Y dS w xY w|j
        pd                                }
|	j
        pd                                }|
dk    r|dvrdS t                       t          d|  d           |rdnd}dg|dR }|g}|s|                    ddddf           g }|D ]}	 t	          j        t#          |          ddddd !          }nV# t          t          j        t          f$ r7}|                    d"                    |           d#|            Y d	}~yd	}~ww xY w|j	        dk    rt          d$             dS |                    d"                    |           d#|j        p|j
        pd                                            t          d%            |D ](}|                                rt          d&|            )t          d'           t          d(| d)            dS )*a  Restart a systemd-managed dashboard instead of raw-killing its PID.

    Returns True when a dashboard unit was found and handled (successfully or
    with a printed actionable failure).  Returning True deliberately prevents
    the caller from falling back to ``os.kill``: systemd treats a direct
    SIGTERM of the service's main PID as a clean stop, so ``Restart=on-failure``
    will not bring the dashboard back.
    r  Fr	  r  r  rY  r   c                 <    t          j        dg|dddd|           S )N	systemctlTr]   rg  rW  )rZ  r[  )rY  r  s     r   
_systemctlz6_restart_managed_dashboard_service.<locals>._systemctl  s3    ~ 4 		
 
 
 	
rA   N))--userr@   zlist-unit-filesz--no-legendz
--no-pagerr   r   c              3      K   | ]9}|                                 |                                 d d         gk    V  :dS )r   r	   N)r  )r:  rA  r  s     r   r;  z5_restart_managed_dashboard_service.<locals>.<genexpr>  sH      NNc#))++Nsyy{{1Q3D6)NNNNNNrA   z	is-activez
is-enabledr  >   linked-runtimeenabled-runtimelinkedstaticr  	generatedu*   ⟲ Restarting managed dashboard service (r  zsystemctl --userzsudo systemctlr  restartr{  r|  Tr]   rg  r  rW  rl  r$  u       ✓ restarted u       ✗ failed to restart z      zl  Dashboard is managed by systemd; not raw-killing its PID because systemd would treat that as a clean stop.z  Restart manually: z	 restart )r
   rO   rq   r   rZ  r4  r  rp  r   r\  r   rm  r  rr   r8   r  r[  r  rh   r   )r   r  r  scopelistedr   r_  	unit_rowsr  r  active_stateenabled_statescope_labelr  commandsri  r~  r  r  s    `                 r   "_restart_managed_dashboard_servicer  y  sD    |wu.0 
 
 
# 
 
Z5P 
 
 
 
 %)E15F&  		Z -/35BDP  FF ":#<gF 	 	 	H	!!](b4466	NNNNYNNNNN 	EFE	
 }u6U6K6666*8e8\84888z8'B   uu M'R..00L^)r0022MxM : % % u	GGG	
@v
@
@
@AAA(-C$$3CK4U4I4t44GyH F 	{ItDEEEF 
 
		^W#GI	  FF ":#<gF 	 	 	MMSXXg..55!55666HHHH	 !!-t--...44xx  TTV]%Ifm%Ir$P$P$R$RTT	
 	
 	
 	
 

-t
-
-... " "99;; 	".3..!!!		4   

=
=
=t
=
=>>>4s;   AA.-A.C C:9C:'F>>H-HHr   c                    	 t          d|  d          }|                                sdS |                    dd          }|                                D ]}|                                }|                    dd          }t          |          d	k    r@|d         }|                    d
          r"|                    dd          d         }|r|c S n# t          t          f$ r Y nw xY wdS )a,  If *pid* belongs to a systemd service unit, return the unit name.

    Reads ``/proc/<pid>/cgroup`` and extracts the service name (e.g.
    ``hermes-serve.service``).  Returns ``None`` when the PID is not
    part of a systemd service, when the file is unreadable, or on
    non-Linux platforms.
    /proc//cgroupNr]   rg  rh  r  r	   r  z.servicer  r  )r   ry  r  rm  rr   r  r  rJ  rsplitr   PermissionError)r   cgroup_pathrn  ro  rp  cg_pathsvc_names          r   _get_systemd_service_for_pidr    s   0C00011""$$ 	4$$gi$HHOO%% 	$ 	$D::<<D JJtQ''E5zzQAhG
++ $">>#q11"5 $#OOO	$ _%   4s   'C B)C C C+*C+cgroup_entryc                     d| v rdS d| v rdS dS )a%  Extract the systemd scope (``user`` or ``system``) from a cgroup path.

    The cgroup path format is ``/system.slice/<name>.service`` for system
    services and ``/user.slice/user-<uid>.slice/<name>.service`` for user
    services.  Returns ``None`` when the scope cannot be determined.
    z/system.slice/rP   z/user.slice/userNr@   )r  s    r   _extract_scope_from_cgroupr    s)     <''x%%v4rA   c                 t   	 t          d|  d          }|                                sdS |                    dd          }|                                D ]I}|                                }|                    dd          }t          |          d	k    r
|d         c S Jn# t          t          f$ r Y nw xY wdS )
zReturn the cgroup path from ``/proc/<pid>/cgroup``, or ``None``.

    Only the unified (``0::``) hierarchy cgroup entry is examined.
    r  r  Nr]   rg  rh  r  r	   r  )	r   ry  r  rm  rr   r  r  r   r  )r   r  rn  ro  rp  s        r   _get_pid_cgroup_pathr    s    
0C00011""$$ 	4$$gi$HHOO%% 	  	 D::<<DJJtQ''E5zzQQx 	 
 _%   4s   'B! A3B! B! !B54B5r  r  c           	         |rt          |          nd}|dk    rddd| g}ni|dk    rdd| g}n]d}dd| gddd| gfD ]M}	 t          j        |dddd	d
          }|j        dk    r dS ,# t          t          j        t          f$ r Y Jw xY wdS 	 t          j        |dddd	d
          }|j        dk    S # t          t          j        t          f$ r Y dS w xY w)zAttempt to restart *svc_name* via systemctl.

    Uses ``systemctl --user`` for user-scope services and ``systemctl``
    for system-scope services.  Returns ``True`` on success.
    Nr  r  r  r  rP   Tr]   rg  rJ   rW  r   F)r  rZ  r[  r\  r  rp  r   )r  r  r  rl  r   rQ  s         r   _try_restart_systemd_servicer  %  sX    8CL&{333EHi:	(		Ix0 )X.(Ix8
 	 	I
N#'		   <1$$44 %%z'@'J   u	N		
 
 
 |q  z8'B   uus#   %A&&BB$B0 0CCc                    t           j        dk    rdS 	 d|  d}t          j                            |          r_t          |d          5 }|                                }ddd           n# 1 swxY w Y   d |                    d          D             }|pdS t          j	        dd	t          |           d
dgddddd          }|j        dk    rdS |j        pd                                }|sdS 	 t          j        |          }n$# t          $ r |                                }Y nw xY w|pdS # t           t          t          j        f$ r Y dS w xY w)u  Return the exact argv of a running process, when recoverable.

    Linux: reads ``/proc/<pid>/cmdline`` (NUL-separated, lossless).
    macOS: falls back to ``ps -o command=`` + shlex (best effort — quoting
    is reconstructed, but hermes launch commands don't embed exotic args).
    Windows: returns ``None``; taskkill /F gives no graceful window and the
    desktop app manages its own backend there.
    r  Nr  z/cmdliner  c                 @    g | ]}||                     d d          S )r]   rg  r  )r  rG  s     r   r  z._dashboard_cmdline_for_pid.<locals>.<listcomp>`  s>       GI66  rA   r  psr  z-ozcommand=Tr]   rg  r	  rW  r   r   )r
   rO   r   rg   rj   rl   rR  r  rZ  r[  rq   r\  r   rr   r  r   r   rp  )r   cmdline_pathrT  ry   r}   r_  r~  s          r   _dashboard_cmdline_for_pidr  P  s    |wt----7>>,'' 	 lD)) Qffhh               IIg..  D
 <44S44		
 
 
 !!4=&B--// 	4	#;w''DD 	# 	# 	#==??DDD	#|tZ!:;   ttse   5D. 	A*D. *A..D. 1A.2%D. 7D. D. 1D D. D'$D. &D''D. .EEr  c           
         ddl m} g }g } |            dz  dz  }	 |j                            dd           n# t          $ r Y nw xY w| D ]}	 d|v r
d|vrg |d}t          |d	          5 }t          j        |t          j        |t          j	        dd
           ddd           n# 1 swxY w Y   |
                    |           }# t          t          f$ r.}|
                    |t          |          f           Y d}~d}~ww xY w|D ]&}t          dt          j        |                      '|D ],\  }}t          dt          j        |           d|            -d |D             S )a!  Best-effort respawn of manually-started dashboards after ``hermes update``.

    Spawns each recovered argv detached (new session, output to the profile's
    ``logs/dashboard-restart.log``).  Returns the commands that failed to
    spawn; the caller prints the manual hint for those.
    r   r'  logszdashboard-restart.logTr  r?  	--no-openab)r   r   r   start_new_session	close_fdsNu       ✓ restarted: u       ✗ failed to restart (r1  c                     g | ]\  }}|S r@   r@   )r:  r~  r~  s      r   r  z0_respawn_dashboard_processes.<locals>.<listcomp>  s    ---
G---rA   )r  r(  r  r  r   rl   rZ  r  r  r   r  r   rq   r8   r  rh   )	r  r(  	respawnedfailedlog_pathr~  log_fr;   err_msgs	            r   _respawn_dashboard_processesr  {  s?    100000!#I*,F  6),CCHdT::::     / /	/ g%%+W*D*D1G1[1h%%  $, %,&*"                  W%%%%$ 	/ 	/ 	/MM7CHH-........	/  ; ;9EJw$7$799::::" O OMEJw,?,?MMGMMNNNN--f----sK   9 
AAC,/B'C'B+	+C.B+	/CD$DDallrp  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.tomlr  projectzoptional-dependencies[r=  r	   )
tomllibr  rl   rm   rf   r   r   rp   r  r  )	rp  r	  handler  optional_depsr<  
referencedre  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r  r@   rA   r   _update_marker_pathr	    s    ...rA   c                      t           dz  S )Nz.lazy-refresh-incompleter	  r@   rA   r   _lazy_refresh_marker_pathr
	    s    444rA   c                     dt           j        v o3| t          t                                                    j        j        k    S )a  True when running under pytest AND ``root`` is this checkout itself.

    Tests that drive update/recovery without sandboxing ``PROJECT_ROOT``
    must neither litter the live repo root with recovery breadcrumbs
    (a leftover ``.lazy-refresh-incomplete`` / ``.update-incomplete``
    false-arms recovery on the developer's next real launch) nor run a real
    reinstall against the executing venv. Sandboxed tests point at a
    tmp_path and are unaffected (same posture as
    ``managed_scope._under_pytest``).PYTEST_CURRENT_TEST)r   re   r   r  r{  r  )r   s    r   _pytest_owns_live_checkoutr	    s8     	+ 	;DNN**,,3::rA   rQ  c                    	 |                                   dS # t          $ r Y dS t          $ r'}t                              d||           Y d}~dS d}~ww xY w)z3Remove an update-recovery breadcrumb. Never raises.zCould not clear %s marker: %sN)r  r  r   r  r  )rg   rQ  r;   s      r   _clear_marker_filer	    s~    B    B B B4eSAAAAAAAAABs    
A	AAAc                  @    t          t                      d           dS )z=Remove the interrupted core-install breadcrumb. Never raises.zupdate-incompleterQ  N)r	  r	  r@   rA   r   _clear_update_incomplete_markerr	    s#    *,,4GHHHHHHrA   c                  @    t          t                      d           dS )z=Remove the interrupted lazy-refresh breadcrumb. Never raises.zlazy-refresh-incompleter	  N)r	  r
	  r@   rA   r   %_clear_lazy_refresh_incomplete_markerr	    s#    022:STTTTTTrA   c                     t          t                    rdS t                                                      } t	                                                      }| s|sdS t          dz                                  st                       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%}t0                              d|           Y d}~nd}~ww xY wd}t4          j        }	 	 t          j        d          }t          j        dd           n# t.          $ r d}Y nw xY wt4          j        t4          _        |rt?                       t                                                      rtA                       |t4          _        |;	 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# |t4          _        |;	 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 update work left half-done by a prior ``hermes update``.

    Handles two independent breadcrumbs:

    - ``.update-incomplete`` — core ``.[all]`` install interrupted. Recovers
      via full quarantined reinstall. Never cleared by the narrow lazy-refresh
      import probes alone.
    - ``.lazy-refresh-incomplete`` — lazy-backend refresh may have corrupted
      packages. Recovers via package-only import probes; cleared only when
      probes confirm healthy/repaired (indeterminate keeps the marker).

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

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

    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.lockr  i  z*Could not create install-recovery lock: %sr	   r  )!r	  r  r	  rj   r
	  ry  r	  r	  r   rl   O_CREATO_EXCLO_WRONLYr   r#  r  rh  FileExistsErrorr  timer  r  r  r   r  r  r
   r   dupdup2r   #_recover_lazy_refresh_marker_locked"_recover_core_update_marker_locked)core_markerlazy_marker	lock_pathfdr;   saved_stdout_fdsaved_sys_stdouts          r   !_recover_from_interrupted_installr%	    s   4 ",// %''..00K+--4466K { 
 ++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
 Oz	# fQiiOGAqMMMM 	# 	# 	#"OOO	#Z
 	2/111  '')) 	1.000%
&+++))))   	 	 	 	DD	 &
&+++))))   	 	 	 	D	s   "BD+ +
F=6AE<;F=<
F	F=F		F=	F=F88F=)G: 9K :H	K H		AK /)J 
J&%J&*K   
KKM!)L
M
LMLML10M1
L>;M=L>>Mc                  $   t          d           t                      \  } }t          | |          }|dv rt                       t          d           dS |dk    rt          d           dS t          d           t          d	           t	          t          t          t                                                                        }t          d
d	                    |            dd	                    d |D                       z              dS )zDHeal ``.lazy-refresh-incomplete`` via confirmed import-probe repair.uo   ⚠ A previous lazy-backend refresh may have left the venv unhealthy — running import-based package repair...r|  )healthyr  uF   ✓ Lazy-refresh venv recovery confirmed — install is healthy again.Nindeterminateuw     ⚠ Import probes unavailable — cannot confirm venv health. Leaving `.lazy-refresh-incomplete` for the next launch.ue     ⚠ Lazy-refresh package repair incomplete. Leaving `.lazy-refresh-incomplete` for the next launch.  Recover manually with:r  rl   install --force-reinstall c              3   >   K   | ]}t          j        |          V  d S r>   r  quoter:  r  s     r   r;  z6_recover_lazy_refresh_marker_locked.<locals>.<genexpr>}  s*      99!u{1~~999999rA   )
r8   _default_venv_install_target_repair_venv_via_import_probesr	  _lazy_refresh_repair_specsr  r  _LAZY_REFRESH_REPAIR_PACKAGESr  rh   )install_prefixinstall_envr  	all_specss       r   r	  r	  a  sK   		1   #?"@"@NK+NLLLF(((-///VWWW  F	
 	
 	
 	
 	

 	F	
 	
 	
 	())).34;;==>>??
 
	 	H388N++HHHhh99y99999:	
 	
 	
 	
 	
rA   c                     t          d           t                      } | r1t                      \  }}t          d           t          ||           	 ddlm} 	 t          j        t          j	        dddd	gt          d
           n2# t          $ r%}t                              d|           Y d}~nd}~ww xY w |            }|ri t          j        dt!          t          dz            i}t#          |          r,|                    dd           |                    dd           t'          |dg|t#          |          rdnd           n.t'          t          j	        ddgt#                      rdnd           t)                       t          d           dS # t          $ r}t                              d|           t          d           | rEt          d           t          dt           d           t          dt          j	         d           nft          d           t          d t                      t          d!t          j	         d"           t          d!t          j	         d#           Y d}~dS Y d}~dS d}~ww xY w)$u'  Heal ``.update-incomplete`` via full ``.[all]`` reinstall only.

    Narrow lazy-refresh import probes are not sufficient proof that a generic
    interrupted core install finished — a missing dep outside that probe set
    would otherwise look healthy and clear the breadcrumb too early.
    ug   ⚠ A previous `hermes update` was interrupted mid-install — finishing dependency installation now...u     → Running from hermes.exe; applying package-only first aid, then quarantined full reinstall (core marker stays until that succeeds)...r|  r   	ensure_uvr   	ensurepip	--upgradez--default-pipT)r  rX  z,ensurepip during install recovery failed: %sNVIRTUAL_ENVrb  
PYTHONPATH
PYTHONHOMEpipz
termux-allr  r]  rp  rp  uH   ✓ Dependency installation recovered — your install is healthy again.z'Interrupted-install recovery failed: %su3   ✗ Could not auto-recover the interrupted install.z  Hermes is still running from the launcher that needs replacing. Close other Hermes windows, restart from a different terminal, then run:z    cd /d "r  z    "z" -m pip install -e ".[all]"r)	  z    cd r  z -m ensurepip --upgradez -m pip install -e '.[all]')r8   '_windows_running_hermes_launcher_lockedr/	  r0	  hermes_cli.managed_uvr8	  rZ  r[  r
   rf  r  r   r  r  r   re   rq   _is_termux_envr  3_install_python_dependencies_with_optional_fallbackr	  )self_lockedr3	  r4	  r8	  r;   uv_binuv_envs          r   r	  r	    sI    
	3   :;;K H&B&D&D#	
 	
 	

 	'~;GGGG5F333333
	NN{KQ #    
  	N 	N 	NLLGMMMMMMMM	N  	N
NM3|f7L3M3MNNFf%% /

<...

<...?&4V&<&<Gll%     @u-&4&6&6AllE   
 	()))XYYYYY F F F 	>DDDCDDD 	F0  
 ////000DDDD    ,---*L**+++@@@@AAADDDDEEEEEEEEE     FsD   F +B F 
B4B/*F /B44C"F 
J"CI??Jc                  (   t                      sdS t                      } | dS t          |           }|sdS t                      }|D ]}	 |                    t          |                                                                                     J# t          $ r7 |                    t          |                                                     Y w xY w	 ddl	}|
                                }|gt          |                                          z   D ]m}	 t          t          |                                                                                                                    }n# t          $ r Y bw xY w||v r dS nn# t          $ r Y dS w xY wdS )zTrue when a venv ``hermes*.exe`` shim is this process or an ancestor.

    Best-effort: returns False when psutil is unavailable or inspection fails.
    FNr   T)_is_windows_venv_scripts_dir_hermes_exe_shimsr  r   rq   r{  rs   r   r"  Processr  r  r   r!  r   )scripts_dirshimsshim_setshimr"  r&  r  exe_norms           r   rA	  rA	    s   
 == u#%%Kuk**E uH , ,	,LLT\\^^,,22445555 	, 	, 	,LLT**+++++	,^^D4

--- 	 	DtDHHJJ//7799::@@BB   8##tt $	    uu5sP   	AB>CC?F AE('F (
E52F 4E55F ?F 
FFc                  @   	 ddl m}   |             }n# t          $ r d}Y nw xY w|rhi t          j        dt          t          dz            i}t          |          r,|                    dd           |                    dd           |dg|fS t          j
        d	dgdfS )
zHReturn ``(install_cmd_prefix, env)`` for the project venv when possible.r   r7	  Nr;	  rb  r<	  r=	  r>	  r   )rB	  r8	  r   r   re   rq   r  rC	  r  r
   rf  )r8	  rF	  r]  s      r   r/	  r/	    s    333333    $GG]Cv0E,F,FGG# 	(GGL$'''GGL$'''##ND%($..s    ""   )r]  heartbeat_interval_secondsrT	  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  )r"  r   r  r	  r8   )elapseddonerT	  r%  s    r   
_heartbeatz/_run_install_with_heartbeat.<locals>._heartbeat  s    ))677 	%*,,.//GL L L L    ))677 	 	 	 	 	rA   Tr  )r  ra  r]  g?r  r  )r  Eventr  r	  r!  r%  rZ  r[  r  r  rh   )rl  r]  rT	  rY	  r  rX	  r%  s     `  @@r   _run_install_with_heartbeatr[	    s     ?DJLLE        	
4888AGGIII			
 	
 	
 	
 	


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


	ss   B& &,Cc                  "    t           j        dk    S )Nr  )r
   rO   r@   rA   r   rI	  rI	  .  s    <7""rA   c                      t           dz  } |                                 sdS ddlm}  || t	                                }|                                r|ndS )zKReturn the venv Scripts directory if we're running inside the project venv.rb  Nr   )venv_bin_dirwindows)r  r   r  r^	  rI	  )venv_dirr^	  r  s      r   rJ	  rJ	  2  sf    f$H?? t------l8[]];;;Gnn&&077D0rA   rM	  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-acphermes-agentrG   zhermes-gatewayc                      g | ]
}| d z  S .exer@   r:  r   rM	  s     r   r  z%_hermes_exe_shims.<locals>.<listcomp>K  s$    BBBDKT---'BBBrA   )rI	  r  _load_console_script_namesr   r  )rM	  namess   ` r   rK	  rK	  =  sk     == 	*,,--Y1Y1Y1YE 
IIBBBBF5MMBBBBrA   r  )max_attemptsrk	  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        rm	  r	   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 rw  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`.)rI	  r	  r   r  r+  r  rK	  rj   with_suffixr  r*  rg  r  r  r   _schedule_replace_on_rebootr8   r   	__class__rs  )rM	  rk	  movedr	  r  
backoff_msattemptsrP	  r  last_excattemptdelayr  	scheduleds                 r   _quarantine_running_hermes_exer|	  N  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-rP	  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   Nr  r	   r  )rI	  rN   r  windllr  MoveFileExWLPCWSTRDWORDr  r  r  rq   r  r   )rP	  r}	  rN   r  MOVEFILE_REPLACE_EXISTINGMOVEFILE_DELAY_UNTIL_REBOOTr	  rz  s           r   rs	  rs	    s     == u######$'!&)#m,8 ( 0(2BHNS&m[II!""%(CC
 

 Bxx   uus   A<B 
BBru	  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)rj   r  r   )ru	  originalquarantineds      r   _restore_quarantined_exesr	    s    !&  +	??$$ -););)=)= -""8,,, 	 	 	D		 s   =A
AAr]  rM	  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.
    Nr|  )r|	  r[	  r&   r	  )rl  r]  rM	  ru	  s       r   _run_quarantined_installr	    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.*)rI	  rJ	  r  r  r   )rM	  stales     r   _cleanup_quarantined_exesr	     s     == ')) %%m44 	 	E   	 	
    s4   A% AA% 
A A% A  A% %
A32A3_LAZY_REFRESH_IMPORT_PROBESr2	  r|  c                (    t          | |           dS )a   Run a package-only pip/uv install without quarantining entry-point shims.

    ``pip install --upgrade pip`` and ``--force-reinstall <pkg>`` do not
    rewrite ``hermes.exe``. The editable-install quarantine path would rename
    shims without uv recreating them on Windows (#57828).
    r|  N)r[	  )rl  r]  s     r   _run_package_only_installr	  )   s      ------rA   r  c                 :   	 ddl }n# t          $ r | cY S w xY wt          dz  }|                                s| S 	 t	          |d          5 }|                    |                              di                               dg           pg }ddd           n# 1 swxY w Y   n4# t          $ r'}t          	                    d|           | cY d}~S d}~ww xY wi 	 ddl
m} |D ]f}	  ||          }|                    d	d
          d                                         |j                                        <   W# t          $ r Y cw xY wn# t          $ r |D ]}|                    d	d
          d                                         }	|	}
dD ]$}||
v r|
                    |d
          d         }
 n%|
                                                    dd
          d                                                                         }|r|	|<   Y nw xY wfd| D             S )zGMap repair package names to their declared pin specs in pyproject.toml.r   Nr  r  r  dependenciesz*lazy refresh repair spec lookup failed: %sRequirement;r	   z==z>=z<=z~=>r  z!=r 	  c                 `    g | ]*}                     |                                |          +S r@   )rf   rs   )r:  r  name_to_specs     r   r  z._lazy_refresh_repair_specs.<locals>.<listcomp>_   s1    CCC3LSYY[[#..CCCrA   )r	  rM   r  ry  rl   rm   rf   r   r  r  packaging.requirementsr	  r  rr   r   rs   )r  r	  	pyprojectrT  raw_depsr;   r	  r  reqr  bareopr}  r	  s                @r   r1	  r1	  7   s       //I )T"" 	Xa||A**9b99==nbQQWUWH	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X   A3GGG $&L)666666 	 	D!k$''15C1C1CA1F1L1L1N1NSX^^--..   		  
) 
) 
) 		) 		)D::c1%%a(..00DD>  ::::b!,,Q/DE  **,,$$S!,,Q/5577==??C )$(S!		) 		)
) DCCC(CCCCs    B# A BB# BB# BB# #
C-C	CC
E
 %AD98E
 9
EE
 EE
 
B?HHinstall_cmd_prefixc          	         t          | |          }|dS d                    d t          D                       }d| d}	 t          j        t          |          d|gdddd	d
|          }n3# t          $ r&}t                              d|           Y d}~dS d}~ww xY w|j	        dk    r2t                              d|j	        |j
        pddd                    dS d |j                                        D             }g }t                      }	|D ]L}
t                              |
          }|r.||	vr*|	                    |           |                    |           M|S )ua  Probe lazy-refresh packages via real imports.

    Returns:
      - ``[]`` when probes ran and every package imported cleanly
      - ``[dist, ...]`` when probes ran and some packages failed
      - ``None`` when the probe could not run (missing venv Python, subprocess
        failure, non-zero probe exit) — this is *indeterminate*, not healthy
    Nr  c              3   .   K   | ]\  }}d |d|dV  dS )z    (r  z),Nr@   )r:  modrB  s      r   r;  z6_detect_broken_lazy_refresh_imports.<locals>.<genexpr>s   sK        (1T######     rA   z import os
import sys
probes = [
a\  
]
broken = []
for mod, attr in probes:
    try:
        imported = __import__(mod)
        if not hasattr(imported, attr):
            broken.append(mod)
        elif mod == 'certifi':
            # The module can import cleanly while cacert.pem is
            # missing/corrupt (brew Python upgrade, interrupted venv
            # rebuild) - every TLS call then fails (#29866).
            bundle = imported.where()
            if not os.path.isfile(bundle) or os.path.getsize(bundle) < 1024:
                broken.append(mod)
    except Exception:
        broken.append(mod)
print('\n'.join(broken))
r   Tr]   rg  FrX  rn  r_   ri  ra  r]  z$lazy refresh import probe failed: %sr   z'lazy refresh import probe exited %s: %sr      c                 ^    g | ]*}|                                 |                                 +S r@   rF  r:  ro  s     r   r  z7_detect_broken_lazy_refresh_imports.<locals>.<listcomp>   s=       tzz||

  rA   )_resolve_install_target_pythonrh   r	  rZ  r[  rq   r   r  r  r\  r   r   rm  r  r2	  rf   r   r  )r	  r]  r0  probe_linescheck_scriptr_  r;   broken_modulesr  r   r	  r  s               r   #_detect_broken_lazy_refresh_importsr	  b   s    11CSIIKt))  5P    K	& 	& 	& 	& .
t\2	
 
 
    ;SAAAttttt A5] b$3$'	
 	
 	

 t !'!9!9!;!;  N HUUD ! !+//44 	!3d??HHSMMMOOC   Os    +A, ,
B6BBc                    |sdS t          |          }	 t          | ddg|z   |           n8# t          j        $ r&}t                              d|           Y d}~dS d}~ww xY wt          | |          }|g k    S )z@Force-reinstall ``packages`` and re-probe imports. Never raises.Tr   z--force-reinstallr|  z#lazy refresh venv repair failed: %sNF)r1	  r	  rZ  CalledProcessErrorr  r  r	  )r	  r  r]  specsr;   afters         r   #_repair_broken_lazy_refresh_importsr	     s      t&x00E!)-@!I5!II	
 	
 	
 	
 	
 (   <cBBBuuuuu 00BLLLEB;s   . A#AA#c                   t          | |          }|t          d           dS |sdS t          dd                    |           d           t          | ||          rt          d	           d
S d                    d t	          |          D                       }t          d           t          dd                    |            d|            dS )u  Probe imports and force-reinstall any broken lazy-refresh packages.

    Uses real ``import`` checks (not distribution metadata) so a venv where
    METADATA remains but ``.py`` files were wiped mid-install is still
    detected (#57828). Package-only reinstall — never rewrites ``hermes.exe``.

    Never raises. Returns one of:
      - ``"healthy"`` — probes ran and found nothing broken
      - ``"repaired"`` — probes found breakage and force-reinstall confirmed clean
      - ``"failed"`` — probes found breakage and repair did not confirm clean
      - ``"indeterminate"`` — probes could not run; do NOT treat as healthy
    r|  NuG     ⚠ Import probes unavailable — cannot confirm venv package health.r(	  r'	  u:     → Detected corrupted venv packages via import probes: r  z; repairing...u     ✓ Venv repair succeededr  rl  c              3   >   K   | ]}t          j        |          V  d S r>   r,	  r.	  s     r   r;  z1_repair_venv_via_import_probes.<locals>.<genexpr>   s;        A     rA   uA     ⚠ Venv repair incomplete. Run manually, then `hermes update`:r  r*	  r  )r	  r8   rh   r	  r1	  )r	  r]  brokenmanuals       r   r0	  r0	     s1   " 11CMMMF~U	
 	
 	
  y		-99V	- 	- 	-   +F    	+,,,zXX   :6 B B    F 

MNNN	Psxx*++PPPP   8rA   r?	  c                    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.
    Nr  r   c                 2    t          | z              d S )Nr	  )r	  )r  r]  r	  rM	  s    r   _installzE_install_python_dependencies_with_optional_fallback.<locals>._install!  s0     %3K	
 	
 	
 	
 	
 	
rA   r   r  z.[r=  r|  u`     ⚠ Optional extras failed, reinstalling base dependencies and retrying extras individually...)r   r  r!  r@	  u0     ✓ Reinstalled optional extras individually: r  u1     ⚠ Skipped optional extras that still failed: r?	  )rI	  rJ	  r  rq   !_verify_console_scripts_installedrZ  r	  r8   r	  r  rh   #_verify_core_dependencies_installed)r	  r]  rp  r	  failed_extrasinstalled_extrasr  rM	  s   ``     @r   rD	  rD	     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  r  r  r  c                 0    g | ]}|t          |          S r@   )rq   )r:  r   s     r   r  z._load_console_script_names.<locals>.<listcomp>D!  s#    666d6D		666rA   z>console script verification: failed to read pyproject.toml: %s)
r	  rM   r  ry  rl   rm   rf   r   r  r  )r	  r	  rT  r  r  r  s         r   ri	  ri	  5!  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 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 rf	  )ry  rh	  s     r   r  zG_verify_console_scripts_installed.<locals>._missing.<locals>.<listcomp>g!  sF     
 
 
T---/88::

 
 
rA   r@   )rj	  rM	  s   r   _missingz3_verify_console_scripts_installed.<locals>._missingf!  s.    
 
 
 

 
 
 	
rA        ⚠ Verification: z$ console script(s) missing on disk: r  u3     → Reinstalling entry points with --reinstall...r   --reinstallr  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)rI	  rJ	  ri	  r  rq   r8   r  rh   r	  rZ  r	  r  r  )r	  r]  r	  missingr  still_missingrj	  rM	  s         @@r   r	  r	  J!  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  r  r  r	  z3dep verification: failed to read pyproject.toml: %sr	  r	  r	   r	  r 	  r   c            	         d} 	 t          j        t                    d| gdd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   Tr]   rg  Fr	  z'dep verification: subprocess failed: %sc                 ^    g | ]*}|                                 |                                 +S r@   rF  r	  s     r   r  zN_verify_core_dependencies_installed.<locals>._missing_deps.<locals>.<listcomp>!  s-    TTTtzz||T

TTTrA   )rZ  r[  rq   r   r  r  r   rm  )r	  r_  r  
applicabler]  r0  s      r   _missing_depsz:_verify_core_dependencies_installed.<locals>._missing_deps!  s    + 	
	^[!!4C
C#GI  FF  	 	 	LLBAFFFIIIIII	 UT)A)A)C)CTTTTs   -3 
A$AA$A$r	  z( declared dep(s) missing after install: r  r   rJ  r   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 r@   r  )r:  r  r	  s     r   r  z7_verify_core_dependencies_installed.<locals>.<listcomp>,"  s)    ;;;\a##;;;rA   u1     → Force-installing remaining missing dep(s): r   r	  r|  z/dep verification: per-package repair failed: %su     ⚠ Could not install: zC. Run `hermes update --force` after closing other hermes processes.r	  )r	  rM   r  ry  rl   rm   rf   r   r  r  r	  r	  r  r   r  r  rr   evaluater	  r  rq   r8   r  rh   rI	  rJ	  r	  rZ  r	  r  r[	  )r	  r]  rp  r	  r	  rT  r  r	  r  depsr	  r  r	  r  r	  r   r  r	  r	  rM	  repair_argsr	  r	  r	  final_missingr	  r	  r0  s    `                       @@@r   r	  r	  !  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                 6   |rNd|v rJddl m} t          |d                   } ||t                                }|                                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;	  r   )venv_python_pathr_	  uvN)r  r	  r   rI	  rj   r   rs   )r	  r]  r	  	venv_rootr   firsts         r   r	  r	  F"  s      }##555555]+,,	$$YFFF	 	  '*++<<>> 	d%**:*:*<*<<<L4rA   c                      t          |           S r>   )rc  r|  s    r   rC	  rC	  c"  s    )#...rA   npm_pathc                     |                                  }|                    d          p|                    d          pd| v S )u&  Return True if ``npm_path`` points at a Windows npm shim.

    On WSL the Windows install dir is exposed through the ``/mnt/c`` drive
    mount and PATH interop, so ``shutil.which("npm")`` can hand back
    ``/mnt/c/Program Files/nodejs/npm`` (or the ``npm.cmd`` / ``npm.exe``
    shim). Those are detected here by their ``.exe``/``.cmd``/``.bat``
    suffix, a ``/mnt/`` drive-mount prefix, or an embedded backslash (a UNC
    path). Callers use this only on a POSIX host — on native Windows an
    ``npm.cmd`` shim is the correct executable.
    )rg	  z.cmd.bat/mnt/\)rs   rJ  r  )r	  r  s     r   _is_windows_npm_pathr	  g"  sI     ..

C-.. 	>>'""	8rA   c                     ddl m}   | d          }t                      r|S |sdS t          |          s|S t          j                            dd                              t          j                  D ]W}|r'|	                                
                    d          r,t          j        d|          }|rt          |          s|c S XdS )	a5  Resolve an npm executable that belongs to the host's Node runtime.

    On WSL/Linux ``shutil.which("npm")`` may resolve a Windows npm exposed
    through PATH interop. Running that Windows npm against the Linux checkout
    operates over ``\wsl.localhost\...`` UNC paths and fails with EISDIR /
    symlink errors in symlink-heavy trees like ``ui-tui`` (#30271). Refuse a
    Windows npm on a POSIX host and re-scan PATH (skipping ``/mnt/*`` interop
    entries) for a Linux-native npm. Returns the npm path, or ``None`` when
    no suitable npm is reachable.
    r   r  r  Nr  r   r	  rc  )r  r  rI	  r	  r   re   rf   r  r  rs   r  r  r  )r  r  	directoryr   s       r   rf  rf  z"  s     655555

u
%
%C }} 
 t$$ 

 Z^^FB//55bjAA  	 	IOO--88AA 	LY777	 	1)<< 	4rA   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 r  )	_original_log_original_broken)selfr	  log_files      r   __init__z_UpdateOutputStream.__init__"  s    !	 %rA   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	  r   r   r	  r   rq   r]  r  r	  BrokenPipeErrorr   r   )r	  r  s     r   r   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	  r   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	  r   r   r	  s    r   r   z_UpdateOutputStream.isatty"  sM      	5	>((*** 	 	 	55	s   $ 
22c                 4    | j                                         S r>   )r	  rR  r	  s    r   rR  z_UpdateOutputStream.fileno"  s     ~$$&&&rA   c                 ,    t          | j        |          S r>   )rn   r	  )r	  r   s     r   rt  z_UpdateOutputStream.__getattr__"  s    t~t,,,rA   N)
rs  
__module____qualname____doc__r	  r   r   r   rR  rt  r@   rA   r   r	  r	  "  s{         "& & &
F F F() ) )  ' ' '
- - - - -rA   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'  r  Tr  z
update.logrN  r	   r]   )	bufferingr_   z
=== hermes update started seconds)timespecz ===
r	  r	  r	  r  )r
   r   r   signalr   r	  SIG_IGNr   r   r  r(  r  rl   rE  r   r  r  r	  r   )r	  state_signal_get_hermes_homelogs_dirr  r	  _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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	  )rf   r
   r   r   r   r   rh  )r	  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.
    branchNr  )rn   rr   r  s    r   _resolve_update_branchr	
  P#  s,     D(D))3V::<<FFrA   saved_mbc                 ,    | dk    rd| ddS d|  ddS )u%  Human label for a before/after database size delta, in MB.

    A negative delta means the file GREW — concurrent session writes during a
    long optimize can outweigh what the rebuild freed. Printing
    "reclaimed -163.0 MB" for that reads as data loss, so say "grew by"
    instead.
    r   z
reclaimed z.1fz MBzgrew by r@   )r

  s    r   _size_delta_labelr
  [#  s7     1}}-H-----(xi(((((rA   c           
         ddl m}m}m}m}m}  |            r |d           dS  |t                    }|dk    r+t           |                       t          j	        d           |dv r,t           ||                     t          j	        d           t          | dd	          r>t          |           }t          |t          t          | d
d                               dS t          | dd	          }t          |          }	ddlm}
m}m}  |            }|                                s@t           ||j                             t+          |	           t          j	        |
           	 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   )rC  format_docker_update_message
is_managedmanaged_error%recommended_update_command_for_methodzupdate Hermes AgentNdockerr	   >   nixnixosra  Fr
  )r
  branch_explicitgateway)r	  )UPDATE_EXIT_CONCURRENT
UpdateLockdescribe_holder)r  rC  r
  r
  r
  r
  r  r8   r
   r   rn   r	
  rn  r  r
  hermes_cli.update_lockr
  r
  r
  acquireholderr
  ro  r  )r  rC  r
  r
  r
  r
  install_methodr
  r	  _update_io_stater
  r
  r
  _update_locks                 r   
cmd_updater 
  h#  sY                 z|| +,,, +*<88N!!**,,---)))33NCCDDDtWe$$  (-- x!>!>??	
 	
 	
 	
 	4E22L
 2|LLL          :<<L!! )ool122333 0111'(((2L9999 011111 	 01111s   0F& &%G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  rA  clawcrondumpr  r  loginr*   r=  setupr  backupr  doctorhonchoimportlogoutmemoryr  r  r  r>  r
  pairingpluginsprofilerF  webhookinsightsr0  r  r  r?  rZ  
completionr  >   r   r   r   r  r   r	   r  rl  )r  r  r  rh   )r}   _SUBCOMMANDS_SESSION_FLAGSr_  r  r  rp  s          r   _coalesce_session_name_argsr6
  #  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--& MrA   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   )list_profilescreate_profiledelete_profileseed_profile_skillsset_active_profileget_active_profile_namecheck_alias_collisioncreate_wrapper_scriptremove_wrapper_script_is_wrapper_dir_in_path_get_wrapper_dirr  profile_actionNz
Active profile: zPath:           r   zModel:          rw  r  r   zGateway:        runningr*  zSkills:         z
 installedzAlias:          u    → hermes -p r  zNo profiles found.z
 Profilez<16rl  Modelz<28Gatewayz<12AliasDistributionu-   ───────────────r  uQ   ───────────────────────────u!   ───────────u<   ────────────────────u    ◆r     —   @?rS	  z<15usez Switched to: default (~/.hermes)zSwitched to: r  r	   createcloneF	clone_allno_alias	no_skills
clone_fromrr  )r   rT
  rQ
  clone_configrR
  rS
  rr  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: skipped_opt_outz`No bundled skills seeded (--no-skills). Delete .no-bundled-skills in the profile to opt back in.copiedz 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,  	overwritern  r  z'profile describe: --all requires --autor2   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] )rr  r]
  zDescription updated for 'r  )r  T)missing_onlyz'All profiles already have descriptions.)r\
  zDescribed 'r  zprofile describe r$  show)get_profile_dirprofile_exists_read_config_model_check_gateway_running_count_skills_read_distribution_metarB
  find_alias_for_profilezError: Profile 'z' does not exist.z

Profile: z	Path:    z	Model:   z	Gateway: z	Skills:  z	.env:    r*  rj   znot configuredz	SOUL.md: zSOUL.mdzDistribution: zInstalled from: z  (run `hermes profile info z` for full manifest)r  r	  z	Alias:   z  (aliasremove
alias_name)ra
  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: r  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_)rb  install_name)override_namez
Proceed with install? [y/N] >   r?  r  zInstall cancelled.rD  )r   rD  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_manifestrr
  )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.)rz
  u   
✓ Updated 'u   ' → vz6  Cron files were refreshed.  Review with:  hermes -p r  )describe_distributionrr
  z	Profile 'z/' is not a distribution (no distribution.yaml).
Distribution: r   zVersion:      rF  zDescription:  authorzAuthor:       licensezLicense:      hermes_requireszRequires:     Hermes r  zSource:       installed_atzInstalled:    env_requiresz
Environment variables:requiredr  rv  z      default: )_r  r8
  r9
  r:
  r;
  r<
  r=
  r>
  r?
  r@
  rA
  rB
  r  r  rn   r8   r   
is_defaultr*   r+   gateway_runningskill_count
alias_pathri
  distribution_namedistribution_versionr  r   r  r
   r   plugins.memory.honcho.clirV
  r   rf   r  formatrq   r  r   ru   r	  r  r   r  r   ry
  r(  r`
  r   read_profile_metawrite_profile_metar  list_describable_profilesdescribe_profilerz  rr  r   ra
  rb
  rc
  rd
  re
  rf
  rj   rO   rj
  rk
  old_namenew_namerm
  rB  rn
  archiver  hermes_cli.profile_distributionrp
  rq
  rr
  TemporaryDirectoryr  _render_distribution_planrO  rr   rs   rP  r5   r  rF  
target_dirr
  has_cronrw
  rx
  r{
  )dr  r8
  r9
  r:
  r;
  r<
  r=
  r>
  r?
  r@
  rA
  rB
  r  actionr  dhhr   r7  alias_displayr  r  r   r*   gwrg
  r  r  rP
  rQ
  rR
  rS
  rT
  rU
  profile_dirsource_labelrV
  r_  rX
  	collisionwrapper_pathprofile_dir_displayr  _profiles_modall_flag	auto_flagoverwrite_flag
text_value_hhr;   metar  tag_pdtargetsok_count
fail_counttgtoutcomer`
  ra
  rb
  rc
  rd
  re
  rf
  r+   r  	dist_namedist_versiondist_sourceri
  
is_windowsrN  rh
  custom_namerj
  rk
  new_dirrm
  rB  result_pathrn
  r  rp
  rq
  rr
  tmpplananswerrw
  rx
  ry
  canonr  rz
  r{
  r  env_reqserro  sd                                                                                                       r   cmd_profiler
  $  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        rut          d           |j        D ]]}|j        rdnd}t          j                            |j                  du}|s| j
                                        r| j
        dz  }|                                r	 |                    d                                          D ]k}|                                }	|	r|	                    d          r.|	                    dd          d                                         }
|
|j        k    rd} nln# t0          t2          f$ 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          |	           _| 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*  z	utf-8-sigr^   rj  r  r	   Tu   ✓ setzneeds settingrJ
  u       • rw  r  r  rv  uj   
  ⚠ This distribution ships cron jobs.  They will NOT run automatically — review and enable manually.)r
  r
  r  r8   r   rF  rr  r}
  r
  
provenancer
  r  ry  r
  r
  r   re   rf   r   r  rm  rr   r  r  r   r  r
  )r
  r
  mfexisting_is_distributionr
  r
  alreadyenv_pathry   ro  r}  r  s               r   r
  r
  K&  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{#5#K#K#V#V#X#X & &C#&99;;D#' )4??3+?+? ) ("&**S!"4"4Q"7"="="?"?C"bg~~*. %  . $%78    	
 #*ZYY2;0YTYF9bg9999999D~ 10000$KKKK} 
<	
 	
 	
 	
 	

 
s   BHH.-H.c                     ddl m}  g }t                      D ]^\  }}t          |          }||\  }}}|dk    r$|dk    s | |          s6t	          ||          sG|                    ||f           _|st          d           dS t          t          |           d           |D ]\  }}t          d| d|            t          |          S )	z>Print live listening dashboard processes and return the count.r   )_pid_existsNr?  &No hermes dashboard processes running.z& hermes dashboard process(es) running:z    PID r$  )gateway.statusr
  r  r  _dashboard_listeningr  r8   r  )r
  liver   r~  r  rA  r  r  s           r   _report_dashboard_statusr
  &  s#   ******"$D133 $ $W*733?"dD;199KK,,9#D$// 	S'N#### 6777q	SYY
>
>
>??? + +W)))))****t99rA   r  c                     ddl }	 |                    t          |           |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   Ng      ?r  TF)socketcreate_connectionr  r   )r  r  r
  s      r   r
  r
  &  s     MMM%%'<T'B'BD&ISV%WW 	 		 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	   uus.   &A ;A ?A ?A 
AAc                 h	   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d lm}  |            }|                    d!i           }|                    d"i           }||d#<   ||d$<   d%|d&<   t7          |                    d'd%          pd%                                          s||d'<    ||          rt          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/| d0           t          d1           t          d2           t                       dS )3u/  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  rQ  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]: r   z
  Cancelled.r	   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)r   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  )*ensure_basic_auth_plugin_enabled_in_configr?  
basic_authusernamepassword_hashr   passwordsecretuJ     ✓ Re-enabled the bundled 'basic' auth plugin (was in plugins.disabled)u#     ✗ Failed to write config.yaml: discover_pluginsTrC  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: z).z4    Saved to config.yaml under dashboard.basic_auth.z4    Sign in at the dashboard with these credentials.)rn   hermes_cli.web_serverr
  r   hermes_cli.dashboard_authr
  r
   r   r   r   r8   rO  rr   rP  r5   r   getpasssecretsplugins.dashboard_auth.basicr
  token_urlsafer  r  r  hermes_cli.plugins_cmdr
  r  rq   rf   r  r
  )r  r  r
  r
  r  r
  r
  r
  r
  confirmr
  r;   r
  r
  r  r  r
  r  dashbasicr
  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>>>>>>>>UUUUUUkmm~~k2..b11$j!.oj599Xr**0b117799 	%$E(O 65c:: 	,   	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5B4O* *
P$4&PP$(P; ;
Q"QQ"c                 D   t           j        dk    rddlm}  ||           S ddl}ddlm} t          j        	                    |           st          d           ||           }|                                dz  dz  }	 |                    |          }n"# t          $ r}t          d	          |d}~ww xY wt          |j                  d
k    s t!          j        d|j        d                   st          d          t!          j        d|j        d                   st          d          t          j        t'          t          dd          z  t'          t          dd          z  }t          j        t'          t          dd          z  }	d}
d}d}	 	 t          j        ||          }
t          j        |
          }|                    |j                  st          d          t1          t          d          r+|j        t          j                    k    rt          d          t          j        |j        d         ||
          }t          j        |          }|                    |j                  st          d          t1          t          d          r+|j        t          j                    k    rt          d          |j        dz  dk    rt          d          t          j        |j        d         |	|          }nc# t          $ r  t6          $ rL}|j        t'          t;          d          dd          k    rt          d          |t          d          |d}~ww xY wt          j        |          }|                    |j                  st          d           |j        d!k    rt          d"          t1          t          d          r+|j        t          j                    k    rt          d#          t1          t          d          r|j        dz  d$z  rt          d%          t          j         |d&          5 }d}|!                    d'          }ddd           n# 1 swxY w Y   t!          j        d(|          st          d"          ||dk    rt          j"        |           |dk    rG	 t          j#        |j        d         |           n# t6          $ r Y nw xY wt          j"        |           |
dk    rt          j"        |
           S S # |dk    rt          j"        |           |dk    rG	 t          j#        |j        d         |           n# t6          $ r Y nw xY wt          j"        |           |
dk    rt          j"        |
           w w xY w))zGRead and unlink a Desktop SSH token from its private runtime directory.r  r   )
read_tokenNr   z)--ssh-session-token-file must be absoluter\   zdesktop-sshz@--ssh-session-token-file must be under the desktop-ssh directoryr  z[0-9a-f]{32}z4--ssh-session-token-file has an invalid runtime pathz[0-9a-f]{16}\.tokenr	   z0--ssh-session-token-file has an invalid filenameO_DIRECTORY
O_NOFOLLOWr  z3--ssh-session-token-file has an unsafe runtime rootgetuidz9--ssh-session-token-file runtime root has the wrong owner)dir_fdz7--ssh-session-token-file has an unsafe parent directoryz3--ssh-session-token-file parent has the wrong owneri  i  z6--ssh-session-token-file parent has unsafe permissionserrnoELOOPz%--ssh-session-token-file is a symlinkz*--ssh-session-token-file is not accessiblez.--ssh-session-token-file is not a regular filer  z2--ssh-session-token-file contains an invalid tokenz,--ssh-session-token-file has the wrong owneriz/--ssh-session-token-file has unsafe permissionsrQ  A   z[0-9a-f]{64})$r
   rO   hermes_cli.windows_ssh_runtimer
  r  pathlibr   r   rg   isabsr6   ru   r  r   r  rp  r  	fullmatchO_RDONLYrn   rl   fstatS_ISDIRrt  r   r|  r
  r   r
  
__import__rs  r  fdopenrR  rh  r  )rg   r
  _stat_Path
token_path
token_rootrelativer;   directory_flags
file_flagsroot_fddirectory_fdfile_fd	root_statdirectory_stat	file_stattoken_streamr  s                     r   _read_ssh_session_token_filer  S'  s   
|w======z$%%%%%%7== FDEEEtJ 	)M9Jf))*55 f f f[\\beef
8>ar|OX^TUEV'W'WOPPP<.q0ABB MKLLLkGBq$A$AAGBP\^_D`D``Owr<;;;JGLG3	Tgj/::G))I==!233 X !VWWWr8$$ ^)9RY[[)H)H !\]]]78>!#4ogVVVLXl33N==!788 \ !Z[[[r8$$ X)>")++)M)M !VWWW&.588 !YZZZghnQ/LQQQGG 	 	 	 	T 	T 	TyGJw$7$7"EEEE !HIIsRIJJPSS	T
 HW%%	}}Y.// 	OMNNN""QRRR2x   	MY%5%D%DKLLL2x   	Pi&7%&?6%I 	PNOOOYw$$ 	*G %%b))E	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* |OU33 	SQRRRa<<HW1	(.+LAAAAA   H\"""a<<HW  a<<HW1	(.+LAAAAA   H\"""a<<HW s    B 
B5 B00B5E0L T M$AMM$$C T Q(T (Q,,T /Q,0(T 9!S
S('S(!V=!UV
U,)V+U,,3Vc                 :    | sdS d|                      dd          v S )uY  True when *path* looks like an Electron-packaged renderer dist.

    Packaged Desktop sets ``HERMES_WEB_DIST`` to ``.../app.asar/dist`` or
    ``.../app.asar.unpacked/dist``. A standalone ``hermes dashboard`` that
    inherits that value serves the desktop frontend in the browser
    (issue #52945 — "Desktop IPC bridge is unavailable").
    Fzapp.asarr	  r  )rg  rc  s    r   _is_electron_packaged_web_distr  '  s,      u dC0000rA   c                    t          | dd          }|r1t          | dd          st          | dd          rt          d          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           t          | dd          }t          | dd          }|r$t          j	        d|          st          d          d}|r|st          d          t          j                            d          dk    rOt          j                            dd          }t          |          r t          j                            dd           |s t          j                            dd           	 ddlm}	  |	            }
n# t"          $ r d}
Y nw xY w|
dvrt          | dd          st          | dd          spt          j                            d          dk    rLd| 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|rd(nd)d*t3          | j                  d+| j        d,|
g}|r|                    d-|g           |r|                    d.|g           | j        r|                    d/           t          | d0d          r|                    d1           t          | d2d          r|                    d3           dd4lm}  |dd5          }	 dd6lm} t3           |                      |d7<   n&# t"          $ r |                    d7d           Y nw xY wt          j         d8k    r=tC          j"        ||9          }t          j        |#                                           n t          j$        t          j        ||           |rtK          |          }	 dd:l&m'}  |d;<           n# t"          $ r Y nw xY w	 ddl(}ddl)}nq# tT          $ rd}t          d=           t          d>tV           d?t          j         d@           t          dA|            t          j        d           Y d}~nd}~ww xY wtY                       	 ddBl-m.}  |             n,# t"          $ r t^          0                    dCdDE           Y nw xY w|rdt          j        d<   nCdt          j        vr@t          | d2d          s/tc          tV          dFz  dDG          st          j        d           nt          | d2d          r)dt          j        v rte          t          j        d                   ntV          dHz  dIz  }|dJz  3                                sdt          j        v}|r:t          dK|            t          dL           tc          tV          dFz  dDG           |dJz  3                                sUt          dM|            |rt          dN           t          dO           t          dP           t          j        d           t          dQ           t          dR|            nte          t          j        d                   4                                }|dJz  3                                sDt          dS|            t          dO           t          dT           t          j        d           t3          |          t          j        d<   t          dU|            	 ddVl5m6}  |             n5# t"          $ r(}t          dW| t          j7        X           Y d}~nd}~ww xY w	 ddYl8m9}  |t^          dZ[           n,# t"          $ r t^          0                    d\dDE           Y nw xY wdd]l:m;} ty          |             || j        | j        | j         t          | d0d          t          | dd          pd|||^           dS )_zGStart the web UI server, or (with --stop/--status) manage running ones.ssh_session_token_fileNr  Fstopz?--ssh-session-token-file cannot be used with --status or --stopr   r
  zrequested via --stopr   r	   headless_backendssh_owner_noncez[0-9a-f]{16}z5--ssh-owner-nonce must be 16 lowercase hex charactersz8--ssh-session-token-file is only valid with hermes serveHERMES_DESKTOPr   HERMES_WEB_DISTr   HERMES_SERVE_HEADLESS)r=
  r   )r   rX  isolatedopen_profileru  rQ  ru  z
/?profile=z*Machine dashboard already running on port r!  z  Managing profile 'r  z+Routing to the machine dashboard (profile 'zB' preselected). Use --isolated for a dedicated per-profile server.r   zhermes_cli.mainr  r=  r?  z--portz--hostz--open-profilez--ssh-owner-noncez--ssh-session-token-filer  insecurez
--insecurer  z--skip-buildru  rw  r	  rY   r  r|  r4  r<  r@  z;Web UI dependencies not installed (need fastapi + uvicorn).zMRe-install the package into this interpreter so metadata updates apply:
  cd re  zP -m pip install -e .
If `pip` is missing in this venv, use:  uv pip install -e .zImport error: rz  u9   terminal config → env bridge failed for dashboard/serveTr}  r`  rK  r  r  r  u6   ⚠ --skip-build was passed but no web dist found at: z0  Attempting one recovery build of the web UI...u6   ✗ --skip-build was passed but no web dist found at: z3  The recovery build did not produce a usable dist.zG  Pre-build first:  npm install --workspace web && npm run build -w webz.  Or drop --skip-build to build automatically.u(     ✓ Recovery build produced a web distu8   → Skipping web UI build (--skip-build); using dist at u5   ✗ HERMES_WEB_DIST is set but no web dist found at: zD  Or unset HERMES_WEB_DIST to build and use the default web UI dist.u)   → Using web dist from HERMES_WEB_DIST: r
  u   ⚠ Plugin discovery failed: r2   start_background_mcp_discoveryzdashboard-mcp-discoveryr  thread_namez9Background MCP tool discovery failed at dashboard startup)start_server)r  r  open_browserallow_publicinitial_profileheadlessssh_session_tokenr  )=rn   r6   r
  r
   r   r  r8   r  r  r
  r   re   rf   r  r  r  r=
  r   r  r  r
  no_open
webbrowserrl   rf  rq   r  r  r  rv  r  r
  rO   rZ  r  r"  execvper  r  r5  fastapiuvicornrM   r  r  r  r{  r  r  rW  r   rj   ri   r  r
  r   hermes_cli.mcp_startupr  r
  r  r
  )r  _token_filer  pids	remaining_headless_backend_ssh_owner_nonce_ssh_session_token_inherited_web_distr=
  _launch_profiler:  r  reexec_argvrv  r]  r
  r  r  r   r!  r  r{  
_dist_root_recoverabler
  r;   r  r  s                                r   cmd_dashboardr.  '  s   $ 8$??K \h&&\*1$*F*F\ Z[[[ tXu%% )(**eqjja((( tVU## 
()++ 	:;;;HQKKK'/EFFFF /00	i&Q'''
  &8%@@t%6== R_>N O O RPQQQ U, USTTT 
z~~&''3.. jnn->CC)*=>> 	4JNN,d333 6

.555$??????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) )9GGkc$)nndio	
  	H 35EFGGG 	J :KHIII< 	,{+++4U++ 	-|,,,4u-- 	/~...AAAAAA #"USSS	)@@@@@@!$%<%<%>%>!?!?C 	) 	) 	) GGM4(((((	) <7""#KS999DHTYY[[!!!!Js~{C888 G9+FFFFFFFF&&&&&    	 	 	KLLLJ J JJ J J	
 	
 	
 	"q""###	 !"""$BBBBBB$$&&&& $ $ $P" 	 	$ 	$ 	$ 	$ 	$$  5H /2
*++	"*	,	,WT<QV5W5W	,\E1>>> 	HQKKK	|U	+	+ .H !BJ.. -.///,z9 	
 \)1133 	> -BJ>L @[z[[\\\HIIIlU2$????-5577 [z[[\\\ QOPPP_```FGGG<===UUUVVVV "*%6788CCEE
\)1133 	V*VVWWW[\\\XYYYHQKKK ),J
$%F*FFGGG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   ,G= =HHK 
K)(K)4 P  P87P8:S 
SSS' '
U1AUU'U8 8&V! V!;` 
`>`99`>a &bb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_registerr0  r  _impls     r   r0  r0  (  s%    MMMMMM	E$KKKKKrA   c                 (    ddl m}  ||            dS )z4Enroll a self-hosted gateway with a relay connector.r   )cmd_gateway_enrollN)hermes_cli.gateway_enrollr5  r2  s     r   r5  r5  (  s%    EEEEEE	E$KKKKKrA   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.completionr8  r9  r:  rn   r8   )r  parserr8  r9  r:  r;  s         r   cmd_completionr@  )  s    PPPPPPPPPPD'6**E~~ll6""#####	&mmF##$$$$$mmF##$$$$$rA   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_sizerC  r2  s     r   rC  rC  )  s%    ??????	E$KKKKKrA   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_namerv  r  Nr  rH  followFlevelr  since	component)	num_linesrI  rJ  r  rK  rL  )hermes_cli.logsrF  rG  rn   )r  rF  rG  rH  s       r   cmd_logsrO  )  s    33333333tZ11<WH6	H$,,tXu--dGT**i..dGT**$T22     rA   c                 "    ddl m}  |            S )z%Open the safe Hermes command console.r   )run_console_repl)hermes_cli.console_enginerQ  )r  rQ  s     r   cmd_consolerS  ,)  s#    ::::::rA   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 r@   ro  rf  s     r   r  z+_build_provider_choices.<locals>.<listcomp>7)  s    ///a16///rA   )r,  r\  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  novitar  r  r  r  r  r  r  r  )r  r]  r   )_cps    r   _build_provider_choicesrX  3)  sn    
@@@@@@x//3///// 
 
 

 
 
 	
 	
 	

s    ))>D   prompt-sizeimport-agentr"
  r<  lspr   r  r  rA  r#
  r$
  r%
  helpr  petssendskinsyncr  hooksr&
  r*   proxyr=  r'
  slackr  r(
  r  r)
  egressr+
  kanbanr,
  r-
  portalr  r  r  r]  consoler  r>  r
  journeymigrater.
  r/
  r0
  r  r
  rF  r1
  fallbackr2
  learningr0  r  r  	approvalsr?  rZ  r3
  
monitoringrp  computer-usememory-graphr  >   r   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).
    r	   Nr   r  r  r  r  )r
   r}   r  r  _TOP_LEVEL_VALUE_FLAGS)r}   r  toks      r   _first_positional_argvrs  |)  s     8ABB<D	A
c$ii--1g$;;1us4yy  AE{"4>># 		czzQ,,,QT1B1BQFA
4rA   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)rs  _BUILTIN_SUBCOMMANDS)r	  s    r   _plugin_cli_discovery_neededrv  )  s1     #$$E}u$$$u 4rA   c                     | sdS 	 ddl m} |                    |            dS # t          $ r9}t	          j        t                                        d| |           Y d}~dS d}~ww xY w)a+  Materialize the deferred platform whose top-level CLI command matches.

    Bundled platform plugins are cheap-registered as *deferred* entries to
    avoid importing every gateway SDK during normal startup. A platform that
    registers a top-level ``hermes <name>`` command (e.g. Photon ->
    ``ctx.register_cli_command(name="photon", ...)``) only runs that side
    effect when its module is imported. On the unknown-top-level-command slow
    path, ``discover_plugins()`` records the deferred loader but does not
    import it, so the CLI registration never happens and ``hermes photon``
    fails with argparse ``invalid choice`` (issue #54678).

    Resolving only the platform whose name matches the first positional token
    keeps normal startup cheap while making the targeted command available.
    Nr   )platform_registryz2Deferred platform CLI resolution failed for %s: %s)gateway.platform_registryrx  rf   r   r   	getLoggerrs  r  )r   rx  r;   s      r   &_resolve_deferred_platform_cli_commandr{  )  s      	
??????l+++++ 
 
 
(##))@	
 	
 	
 	
 	
 	
 	
 	
 	

s   # 
A&.A!!A&>   Nrlr"
  rA  r  r[  tickr  
mcp_actionr=  )r$
  r
  r   c                     t          t          | dd          p"t          j                            d          dk              S )Nrd   Fr   r   )r  rn   r   re   rf   r  s    r   _is_tui_chat_launchr  )  s4    eU++Rrz~~l/K/Ks/RSSSrA   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  r[  r$
  r  >   r[  r}  F)r~  rn   r  s    r   "_command_has_dedicated_mcp_startupr  )  sj    |ut|y  WT3Dd%K%Ku%T%Tt|v'$"E"E"X"Xt5rA   c                 6    t          |           rdS | j        dv S )NF>   Nr|  rA  )r  r~  r  s    r   _should_background_mcp_startupr  )  s%    4   u<///rA   c                    t          | dd          rdt          j        d<   t          |            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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.r  Fr   r  )NNNrs  r   r
  z&plugin discovery failed at CLI startupTr}  r  zcli-mcp-discoveryr  z3Background MCP tool discovery failed at CLI startup)discover_mcp_toolsz(MCP tool discovery failed at CLI startupr  )register_from_config)rs  z-shell-hook registration failed at CLI startup)rn   r   re   r  _AGENT_SUBCOMMANDSrf   r~  _AGENT_COMMANDSr  r  r
  r   r  r  r  r  r  r"  r  r  r%   r  r  r  agent.shell_hooksr  agent.outbound_webhooks)r  	_sub_attr_sub_set_accept_hooksr
  _run_inline_mcp_discoveryr  r  r  r  
_hooks_cfgregister_outbound_webhookss               r   _prepare_agent_startupr  )  s'    tVU## -),
%&T,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:::::: []]
ZmDDDD	
 	
 	
 	
 	
 	
 	#":..... 
 
 
; 	 	
 	
 	
 	
 	
 	

sH   B( (&CC
D# #&EEE% %&FF4G &G21G2c                     t          | dd          sd S dt          j        d<   dt          j        d<   dt          j        d<   d S )Nr  Fr   HERMES_SAFE_MODEr  r  )rn   r   re   r  s    r   r  r  B*  sH    4e,, %(BJ!".1BJ*+(+BJ$%%%rA   c                 X    dD ]&\  }}t          | |          st          | ||           'd S )N)r  Nr*   Nr+   Nr,   N)rm  Fr  Nr  Nro  F)r   setattr)r  rB  r   s      r   _set_chat_arg_defaultsr  J*  sH    	 ) )g tT"" 	)D$((() )rA   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          rdt%          |           t'          |j        t#          |dd          t#          |dd          t#          |dd          t#          |dd                     |j        s|j        r|j        d|_        |j        dv rt1          |           t#          |dd           ot#          |dd           }|rPt3          |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.FHERMES_TERMUX_DISABLE_FAST_CLIr   r	   N-h--helpr@  Tc              3   X   K   | ]%}|d k    p|dk    p|                     d          V  &dS )r   r   z
--oneshot=Nr8  r9  s     r   r;  z._try_termux_fast_cli_launch.<locals>.<genexpr>n*  sU         	tIsk)IS^^L-I-I     rA      NrA  r   build_top_level_parserfuncrF  oneshotr*   r+   r,   r-   r)   rA  r  rn  r  HERMES_DEFER_AGENT_STARTUPHERMES_FAST_STARTUP_BANNERrs  r  )rc  r   re   rf   r
   r}   r   r   rS  rs  r  hermes_cli._parserr  set_defaultsr  
parse_argsr6
  rn   r  r<   r  r  r  r~  r  r  )	r}   r	  has_oneshotr  r?  _subparserschat_parserr  interactive_prompts	            r   _try_termux_fast_cli_launchr  Y*  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$$$L$..T:t44T:t44t\4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5rA   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  r	   Nr  r  r   r  r  rF  r  r~  T)rc  r
   r}   r   rs  r  r  r  r  r  r6
  rn   r  )	wants_tuir	  r  r?  r  r  r  s          r   _try_termux_fast_tui_launchr  *  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4rA   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_commandr  r   r  r-
  r   r+   u%   
  ✓ Memory provider: built-in onlyz  Saved to config.yaml
reset)r(  r  memoriesr  r  >   r  r-
  )z	MEMORY.mdzagent notes>   r  r  )zUSER.mdzuser profilec                 L    g | ] \  }}|z                                   ||f!S r@   rp  )r:  rT  r  mem_dirs      r   r  zcmd_memory.<locals>.<listcomp>*  sF     
 
 
!!T!7K7K7M7M
I
 
 
rA   u1   
  Nothing to reset — no memory files found in z/memories/
z:
  This will permanently erase the following memory files:u       ◆ rw  u   ) — rI  r  r  Fz
  Type 'yes' to confirm: z
  Cancelled.
z  Cancelled.
u     ✓ Deleted r  zE
  Memory reset complete. New sessions will start with a blank slate.z  Files were in: )r  )rn   r  r  r  r   rf   rp   r8   r  r(  r  r  r  r  rO  rr   rs   rP  r5   r  hermes_cli.memory_setupr  )r  rS  r  r  r  r(  r  r  files_to_resetr  rT  r  rg   sizer
  r  r  s                   @r   
cmd_memoryr  *  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_versionF	--versionra  z--checkr'
  z--setupsetup_browserz--setup-browser
assume_yes--yeszACP dependencies not installed.r2   z+Install them with:  pip install -e '.[acp]'r	   N)	acp_adapter.entryr  rn   r  rM   r8   r
   r   r   )r  acp_mainacp_argvs      r   cmd_acpr  +  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  r  r  r   )tools_disable_enable_commandz
post-setup)run_post_setup_commandr  )tools_command)rn   hermes_cli.tools_configr  r  r
   r   r   r  )r  r
  r  r  r  s        r   	cmd_toolsr  +  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rA   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   rb  )InsightsEngine)daysr  zError generating insights: )rf  rc  agent.insightsr  generater  r  r8   format_terminalrh  r   )r  rc  r  ri  enginereportr  s          r   cmd_insightsr  /+  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           	         ddl m} t          | dd          pd} |            }|                    d          }t	          |t
                    r|ni }|dk    r4ddlm} |                    d          }t	          |t
                    r|ni }|                    d	          }	t	          |	t
                    r|	ni }
|
                    d
          }t	          |t
                    r|ni }t          d           t          d|                    d          rdnd d           |                    d          rt          d|                    dd          rdnd d|                    dd           d           t          d|                    dd          rdnd            t          d|                    dd          rdnd d|                    dd           d           t          d           |                    d           pd!}|                    d          r|rt          d"|            nt          d#           t          d$|	                                rd%nd& d'           t          d(           t          d)           dS t          d*| t          j        +           t          j        d,           dS )-z?Gateway monitoring status: health & diagnostics export posture.r   r  monitoring_actionNr  rm  )otlp_exportergateway_health_exportrl
  otlpzGateway monitoringz  Health export:  r  r  z+ (monitoring.gateway_health_export.enabled)z    Metrics:            metrics_enabledTr  r  z (interval export_interval_secondsr  zs)z    Diagnostic events:  diagnostic_events_enabledz    Warning/error logs: warning_error_events_enabledlogs_export_interval_secondsr  zZ    Content safety:     always on (rendered messages are never exported; not configurable)endpointr   z  OTLP endpoint:  z9  OTLP endpoint:  not configured (monitoring.export.otlp)z  OTel SDK:       r  znot installedz% (optional extra: hermes-agent[otlp])z=
  Scope: gateway service health + redacted diagnostics only.zF  No prompts, messages, tool args/results, usage analytics, or traces.zUnknown monitoring action: r2   r  )r  r  rn   rf   r   rp   agent.monitoringr  r8   is_availabler
   r   r   )r  r  r
  r  mon_rawmonr  gh_rawgh
export_raw
export_cfgotlp_rawr  r  s                 r   cmd_monitoringr  =+  s^   ------T.55AF[]]Fjj&&G%gt44<"C222222011'55=662WWX&&
)3J)E)EM::2
>>&))!+Hd!;!;CXX"### <y0A0A#Q99z < < < 	= 	= 	=66) 	N IRVV<Mt5T5T-_TTZ_ I I!vv&?DDI I I J J JkRVV<WY]5^5^-iTTdikklll MRVV<Z\`5a5a-lTTgl M M!vv&DaHHM M M N N N M N N N88J''-288I 	O8 	O1x112222MNNN 6-2L2L2N2N#c;;Tc 6 6 6 	7 	7 	7NOOOVWWW	
0
0
0szBBBBHQKKKKKrA   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)rn   r   hermes_cli.skills_configr  hermes_cli.skills_hub)r  skills_config_commandr  s      r   
cmd_skillsr  j+  sx    t_d++x77_%%%TTTTTTd#####888888trA   c                 (    ddl m}  ||            d S )Nr   )pairing_command)hermes_cli.pairingr  )r  r  s     r   cmd_pairingr  w+  s*    222222ODrA   c                 (    ddl m}  ||            d S )Nr   )plugins_command)r
  r  )r  r  s     r   cmd_pluginsr  }+  s*    666666ODrA   c                 (    ddl m}  ||            d S )Nr   )mcp_command)hermes_cli.mcp_configr  )r  r  s     r   cmd_mcpr  +  s*    111111KrA   c                 (    ddl m}  ||            d S )Nr   )claw_command)hermes_cli.clawr  )r  r  s     r   cmd_clawr  +  s*    ,,,,,,LrA   c                  -  TUVWXYZ t                       	 ddlm}   |              n# t          $ r Y nw xY w	 t	                       n# t          $ r Y nw xY wt                       	 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}  |            \  Y}}|                    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.          ZZ                    d/          }|                    d0d1gd2          }|                    d3d4d5gd6          }dd7lm} dd8lm} |                    |           |                    |           Zfd9}Z                    |           |                    d:d;d<          Xdd=lm} |                    X           TXfd>TX                    T           dd?lm}m} |                    d@dAdB          }|                    dC          }|                    dDdEdF          }|                    dGdHdIJ           |                    dKdHdLJ           |                    |           |                    |           tA          |tB          tD          tF          M           	 ddNl$m%}  ||           n2# t          $ r%}tL          '                    dO|           Y d}~nd}~ww xY wtQ          |tR          P           tU          |tV          Q           |                    dRdSdT          }|                    tX                     t[          |t\          U           ddVl/m0}  ||           tc          |td          W           tg          |th          X           tk          |tl          Y           to          |tp          Z           ts          |tt          [           tw          |tx          \           t{          |t|          ]           dd^l?m}  ||           dd_l@mA}  ||          }|                    t                     dd_lCmA}  ||          } |                     t                     t          |t          `           t          |t          a           t          |t          b           t          |t          c           t          |t          d           t          |t          e           t          |t          f           |                    dgdhdi          }!ddjlSm}"  |"|!           t          |t          k           dl }#t          ||#m           t          |t          n           t          |t          o           t          |t          p           t          |t          q           t          |t          r           |                    dsdtdu          }$ddvlam}%mb}&  |%|$           |$                    |&           t          |t          w           t                      r	 ddxlfmg}' ddylhmi}(mj}) t                      }* |'            D ]}+|                    |+d         |+dz         |+l                    d{d|          t          d}          jn        ~          }, |+d         |,           |+l                    d          |,                    |+d                    |*o                    |+d                     |(             t          t                                  |)            jr        s                                D ]}+|+d         |*v r|                    |+d         |+dz         |+l                    d{d|          t          d}          jn        ~          }, |+d         |,           |+l                    d          |,                    |+d                    nD# t          $ r7}-t          ju        t                    '                    d|-           Y d}-~-nd}-~-ww xY w|                    ddd          }.	 ddjlwm}/  |/|.           nD# t          $ r7}-t          ju        t                    '                    d|-           Y d}-~-nd}-~-ww xY w|                    ddd          }0	 ddjlxm}1  |1|0           nD# t          $ r7}-t          ju        t                    '                    d|-           Y d}-~-nd}-~-ww xY w|                    dddgdd          }2	 ddjlym}3  |3|2           nD# t          $ r7}-t          ju        t                    '                    d|-           Y d}-~-nd}-~-ww xY wt          |t                     t          |t                     |                    ddd          VV                    d          }4|4                    dd          }5|5                    ddHdJ           |4                    dd           |4                    ddd          }6|6                    ddg dd           |6                    ddg dd           |6                    ddHdJ           |4                    ddd          WW                    d          }7|7                    dd          }8|8                    ddHdJ           |7                    dd           VWfd}9V                    |9           t          |t                     |                    ddd          }:|:                    d          };|;                    dd          }<|<                    dd           |<                    dt           dd           |<                    ddd           d }=|;                    ddĬ          }>|>                    dddƬ           |>                    dg dȢddʬ˦           |>                    ddHdͬJ           |>                    ddHdϬJ           |>                    ddHdѬJ           |>                    ddgdԬզ           |>                    dd׬            |=|>dئ           |>                    ddHdڬJ           |>                    dddgddެ˦           |>                    ddHdJ           |>                    ddHdJ           |;                    dd          }?|?                    dd           |?                    dddHdJ           |;                    dd          }@ |=|@d           |@                    ddHdJ           |;                    dd          }A |=|Ad           |;                    dd           |;                    ddd          }B|B                    ddHdd           |B                    dddHdd           |;                    ddd          }C|C                    ddHdJ           |C                    dKdHd J           |;                    ddd          }D|D                    dt          dd           |D                    dt          d	           |D                    d
dHdJ           |D                    dt          d	           |D                    dt           dd           |D                    ddHdJ           |D                    dt          d	           |;                    dd           |;                    dd          }E|E                    dd           |E                    ddd           |;                    ddd          }F|F                    dGdHd J           |F                    dt           d!d"           |;                    d#d$          }G|G                    dd           |G                    dt           d%d&           |:                    t          j        t          |:'                     t          |t          (           t          |t          )           t          |t          *           t          |t          +           t          |t          ,           t          |t           -           t#          |t$          .           t'          |t(          /           |                    d0d1          }H|H                    d2dd3g d4d56           |H                    Yfd7           t+          |t,          t.          8           t1          |t2          9           t5          |t6          :           t9          |t:          ;           dd<lm}I  |I            }J|Jr7tA          |Jt          j        dd                    t          j        d           tE          t          j        dd                   }Kddl}LtI          |d=          r&t          |j                                                  nt                      UtO          Ufd>|KD                       }M|Mrd|_        t          j        }N	 |L                                t          _        Y                    |K          }O|Nt          _        nh# tX          $ r>}P|Nt          _        |Pj        dk    r d|_        Y                    |K          }OY d}P~Pn$d}P~Pww xY wd|_        Y                    |K          }O|Oj        rt          |O           dS t_          |Od?d          rd@t`          j        dA<   te          |O           t_          |OdBd          r_tg          |Oj        t_          |OdCd          t_          |OdDd          t_          |OdEd          t_          |OdFd          G           |Oj        s|Oj        rL|Oj        EdH|O_        dID ](\  }Q}RtI          |O|Q          stq          |O|Q|R           )t          |O           dS |Oj        =dJD ](\  }Q}RtI          |O|Q          stq          |O|Q|R           )t          |O           dS tI          |OdK          rL|O                    |O          }Stu          |St                     r|Sdk    rt          j        |S           dS dS dS Y                                 dS (L  z Main entry point for hermes CLI.r   )configure_windows_stdior  r	   Nr  r  )r  )cmd_moar  z0Configure Mixture of Agents provider/model slotsz7Configure the provider/model set used by /moa <prompt>.)r\  rr  moa_command)destr  lszShow current MoA model slots)aliasesr\  	configurer  zInteractively pick MoA modelsr   rM
  zPreset name to create or update)nargsr\  rY
  rmzDelete a MoA presetzPreset name to deleter\  )cmd_fallbackrj  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 chainrh
  z&Pick an entry to delete from the chainr  zRemove all fallback entriesr
  z5Manage external secret sources (Bitwarden, 1Password)zPull API keys from an external secret manager at process startup instead of storing them in ~/.hermes/.env.  Supports Bitwarden Secrets Manager and 1Password.  See: https://hermes-agent.nousresearch.com/docs/user-guide/secrets/secrets_command	bitwardenbwz%Bitwarden Secrets Manager integrationonepasswordr	  	1passwordz(1Password (op:// references) integration)secrets_cli)onepassword_secrets_clic                    t          | dd           }t          | dd           }t          | dd           }|dv r||                     |           S |dv r||                     |           S                                  dS )Nr  secrets_bw_commandsecrets_op_command)r  r  )r  r	  r  r   )rn   r  
print_help)r  rS  bw_subop_subsecrets_parsers       r   _dispatch_secretszmain.<locals>._dispatch_secrets,  s    d-t443T::3T::%%%&*<99T??"4449K99T??"!!###qrA   rd  z:Manage the iron-proxy egress credential-injection firewallzManage iron-proxy, the optional TLS-intercepting egress firewall that swaps proxy tokens for real API credentials before outbound requests leave a sandbox.  Disabled by default.  See: https://hermes-agent.nousresearch.com/docs/user-guide/egress/iron-proxy)	proxy_clic                     t          | dd           }|.t          | d          r| j        ur|                     |           S                                  dS )Negress_commandr  r   )rn   r   r  r  )r  rS  _dispatch_egressegress_parsers     r   r%  zmain.<locals>._dispatch_egress?,  s^     d,d33?wtV44?JZ9Z9Z99T??"  """qrA   )cmd_migratecmd_migrate_xairi  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_typer  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  r5  )register_subparserzLSP CLI registration failed: %s)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)r  )r  )r  )r  )r  )r  )r  )
add_parser)build_parser)r  )r  )r#  )r'  )r+  )r/  )r<  rp  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?  c                 (    ddl m}  ||            d S )Nr   )import_agent_command)hermes_cli.agent_importr2  )r  r2  s     r   cmd_import_agentzmain.<locals>.cmd_import_agent-  s+    @@@@@@T"""""rA   )r4  )r2  )r6  )rS  )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.)r0  bundles_command)r  )discover_plugin_cli_commands)r
  get_plugin_managerr\  rr  r   argparse)r\  rr  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: %srh  rk  ro  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\  rr  zjourney CLI wiring failed: %s)r  )r  rn  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                 >	   t          | dd           }|dk    r0ddlm}  |t          t          | dd                               d S |dk    rUdd l}dd	lm}m}  |            }|rd
}	 ddlm}  |j	        |dgddddd |                      j
                                        }n# t          $ r Y nw xY w|rt          d| d| d           nt          d|            	  |            }	|	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/|	d0          d           t%          j        d-           |	d1         s#t          d2           t%          j        d-           d3 }t          d4|	d5         pd1 d|	d0          d           |	d6         r\t          d7 ||	d8                    d9           t          d7 ||	d:                    d;           |	d,         st          d<           n+t          d7 ||	d,                    d=|	d0          d           |	d>         D ]/}|d         d?k    r!t          d@|dA          dB|dC                     0|	dD         rt          d@|	dD                     t%          j        |	d,         rdnd-                                            d S                                  d S )ENr<  r   r   )install_cua_driverupgradeF)rD  r  )cua_driver_update_checkresolve_cua_driver_cmdr   )_cua_driver_envr  Tr]   rg  r  )rX  rn  r_   ri  rY  r]  zcua-driver: installed at rw  r  update_availablelatest_versionrM
  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  includeskipr  )rJ  rK  json_outputr?  r@  rA  )request_permissions_grant)computer_use_statusr  )r  	sort_keysreadyr	   platform_supportedz!Computer Use is not supported on rO   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   •r@   )r  s    r   r  z0main.<locals>.cmd_computer_use.<locals>.<lambda>y.  s    199%%AJJ55TY rA   zcua-driver: rF  	can_grantr  accessibilityz Accessibilityscreen_recordingz Screen Recordingz.  Grant: hermes computer-use permissions grantz) driver health (no permission toggles on checksrz  rq	  rQ  r$  r  r"  )rn   r  rC  r  rZ  tools.computer_use.cua_backendrE  rF  rG  r[  r   rr   r   r8   rf   tools.computer_use.doctorr  r  r
   r   tools.computer_use.permissionsrM  r  rN  r  r  )r  r
  rC  rZ  rE  rF  rg   rF  rG  stlatestr  r7   perms_actionrM  r  rN  glyphr5  computer_use_parsercomputer_use_permss                      r   cmd_computer_usezmain.<locals>.cmd_computer_use0.  s    4d;;YBBBBBBtGD)U,K,K'L'LMMMMFX        *)++D GGGGGG,jn{+'+$QZde+O--   UUWW	 G
 !   D >HdHHgHHHIIII<d<<===X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+ +
B87B8'A<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 sessions--sourcez/Filter by source (cli, telegram, discord, etc.)z--limitr  zMax sessions to show)typer   r\  r  NEEDLEzgOnly sessions in one workspace: a git repo root or project dir (matched by path substring or basename).r>  r\  c                    |                      dd|           |                      ddd           |                      ddd           |                      d	dd
           |                      dd           |                      dd           |                      dd           |                      dd           |                      dt          d           |                      dt          d           |                      dd           |                      dd           |                      dd           |                      dd            |                      d!d"           |                      d#d$           |                      d%t          d&           |                      d't          d(           |                      d)t          d*           |                      d+t          d,           |                      d-t          d.           |                      d/t          d0           |                      d1d2d34           |                      d5d6d2d74           d S )8Nz--older-thanAGEre  z--newer-thanzZOnly match sessions active within the last AGE (e.g. '5h', '2d') or after an ISO timestampz--beforeTIMEzjOnly match sessions started before TIME (duration ago like '5h', or ISO timestamp like '2026-07-05 14:30')z--afterzTOnly match sessions started at/after TIME (duration ago like '5h', or ISO timestamp)rb  z$Only match sessions from this sourcer  z--titlez7Only match sessions whose title contains this substringz--end-reasonz(Only match sessions with this end reasonz--cwdz>Only match sessions whose working directory is under this pathz--min-messagesz&Only match sessions with >= N messagesrc  r\  z--max-messagesz&Only match sessions with <= N messagesr   z_Only match sessions whose model name contains this substring (e.g. 'sonnet', 'gpt-5', 'hermes')r   zSOnly match sessions billed through this provider (e.g. openrouter, anthropic, nous)r  z%Only match sessions from this user IDz	--chat-idz-Only match sessions from this chat/channel IDz--chat-typez8Only match sessions with this chat type (e.g. dm, group)z--branchz<Only match sessions whose git branch contains this substringz--min-tokensz9Only match sessions with >= N total tokens (input+output)z--max-tokensz9Only match sessions with <= N total tokens (input+output)z
--min-costz:Only match sessions costing >= N USD (actual or estimated)z
--max-costz:Only match sessions costing <= N USD (actual or estimated)z--min-tool-callsz(Only match sessions with >= N tool callsz--max-tool-callsz(Only match sessions with <= N tool callsz	--dry-runr*  z0List matching sessions without changing anythingr+  r  -ySkip confirmation)add_argumentr   float)r7  default_older_helps     r   _add_session_filter_argsz&main.<locals>._add_session_filter_args.  st   	# 	 	
 	
 	

 	
: 	 	
 	
 	
 	
Q 	 	
 	
 	
 	
9 	 	
 	
 	
 	
z(NOOO	U 	 	
 	
 	
 	
!K 	 	
 	
 	
 	
Z 	 	
 	
 	
 	
3-U 	 	
 	
 	
 	
3-U 	 	
 	
 	
 	
1 	 	
 	
 	

 	
1 	 	
 	
 	

 	
B 	 	
 	
 	
 	
M 	 	
 	
 	
 	
K 	 	
 	
 	
 	
O 	 	
 	
 	
 	
L 	 	
 	
 	
 	
L 	 	
 	
 	
 	
uM 	 	
 	
 	
 	
uM 	 	
 	
 	
 	
S; 	 	
 	
 	
 	
S; 	 	
 	
 	
 	
C 	 	
 	
 	

 	
T,5H 	 	
 	
 	
 	
 	
rA   rl
  z*Export sessions to JSONL, Markdown, or QMDrB  z}Output path. JSONL: file path (use - for stdout, required). md/qmd: output directory (default: <hermes home>/session-exports)ry  )jsonlmdqmdhtmltracerp  zgExport format (default: jsonl). 'trace' emits Claude Code JSONL for the Hugging Face Agent Trace Viewer)r4  r   r\  z--uploadzgtrace only: upload to your Hugging Face traces dataset instead of writing a local file (needs HF_TOKEN)z--publiczFtrace --upload only: create/update a public dataset instead of privatez--no-redactzJtrace only: skip the forced secret redaction; only use after manual reviewz--onlyzuser-promptszhExport only a filtered view (user-prompts: one prompt record per line for jsonl, headed sections for md))r4  r\  z--session-idz%Session ID or unique prefix to exportzgOnly export sessions older than AGE (duration like '5h'/'2d', bare number of days, or an ISO timestamp)z--redactzDRedact secrets (API keys, tokens, credentials) from exported contentz	--lineagesinglelogicalz6md/qmd only: export one row or its compression lineagez--delete-after-verifiedzTmd/qmd only: after verified single-session export, delete that session (needs --yes)rE  z.md/qmd only: overwrite an existing export filezDelete a specific sessionr  zSession ID to deleter  rj  rk  prunezCDelete old sessions (filterable by time window, source, title, ...)u   Delete sessions older than AGE — days if bare number, or a duration like '5h'/'2d'/'1w', or an ISO timestamp (bare prune with no filters defaults to 90 days; any filter matches all ages)z--include-archivedz3Also delete archived sessions (excluded by default)r
  uB   Bulk-archive (soft-hide) sessions matching filters — no deletionzeOnly archive sessions older than AGE (duration like '5h'/'2d', bare number of days, or ISO timestamp)optimizezAReclaim disk space: merge FTS5 segments + VACUUM (no data change)zoptimize-storagezOMigrate the search index to the compact v23 layout (reclaims disk on large DBs)u  Rebuild the full-text search index in the compact v23 external-content layout. On large databases this reclaims a large fraction of state.db (the old layout stored duplicate copies of every message and indexed tool output). Runs foreground with a progress bar, throttles so a running gateway stays responsive, and VACUUMs at the end. Safe to interrupt and re-run — it resumes where it left off. No conversation data is changed; only the search index is rebuilt.z--no-vacuumFzgSkip the final VACUUM (index is rebuilt but freed pages aren't returned to the OS until a later VACUUM))r
  r   r\  z'Skip the disk-space confirmation prompt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)recoverz=Rebuild canonical session data into a separate clean databasea5  Offline, non-destructive recovery for a damaged state.db. The source database and its WAL/SHM/rollback-journal sidecars are copied before SQLite opens anything. Canonical rows are rebuilt into a new output database; derived search indexes are recreated and the active database is never replaced automatically.Tz6Source state.db or preserved backup to inspect/recover)rc  r
  r\  z--outputz;New recovery database path (required unless --inspect-only)ri  z--inspect-onlyzIOnly report canonical table readability; do not create an output databasez
--work-dirzNExisting directory for the disposable source copy (defaults beside the output)z--chunk-sizerm	  z1Rows committed per recovery batch (default: 1000)z--allow-partialznBest-effort salvage across damaged row ranges; the output remains separate and every skipped range is recordedz--reportz5JSON report path (defaults to <output>.recovery.json)statszShow session store statisticsr  zSet or change a session's titlezSession ID to renamer  +zNew title for the sessionzretitle-skillsz@Re-title sessions whose auto-title came from a /skill's own textu  Sessions opened with a /skill were auto-titled from the expanded message, which embeds the whole skill body — so the title describes the SKILL, not the request. This regenerates those titles from what the user actually typed. Lists what it would change unless --apply is passed.z'Write the new titles (default: dry run)r	  z*Maximum sessions to examine (default: 200)browseuB   Interactive session picker — browse, search, and resume sessionsrp	  z#Max sessions to load (default: 500))sessions_parser)r  )r  )r  )rU  )r 
  )r^  )r  )r
  r3
  z2Print shell completion script (bash, zsh, or fish)r;  r  )r  r<  r=  zShell type (default: bash))r  r   r4  r\  c                 $    t          |           S r>   )r@  )r  r?  s    r   r  zmain.<locals>.<lambda>K0  s    ^D&5Q5Q rA   )r.  r0  )r  )rO  rB  )get_container_exec_infor4  c              3   J   K   | ]}|                     d           |v V  dS r7  r8  )r:  r  _known_cmdss     r   r;  zmain.<locals>.<genexpr>0  sM        c9J9J	[     rA   r  r   r  r  r*   r+   r,   r-   r)   rA  )r  r  r  r  rm  Nr  )r  r  r  r  r  r  r  r  r  )rU   hermes_cli.stdior  r   r	  r  r
   r}   r%	  r  r  r  r  r  r  r   r  hermes_cli.moa_cmdr  r.  add_subparsersrl  hermes_cli.fallback_cmdr  r  r  r  r0  r"  hermes_cli.migrater'  r(  r   r  r  r5  agent.lsp.clir,  r  r  r   r  r   r  r  r   r  hermes_cli.send_cmdr-  r   r  r   r  r   r  r   r  r   r  r   r  r   r  hermes_cli.portal_clir  r/  r  r  r  r   r  r   r  r   r#  r   r'  r   r+  r   r/  r   r<  hermes_cli.checkpointsr   r?  r   r   r2  r   r6  r   rS  r   r  r   r  hermes_cli.bundlesr5  r   r  rv  plugins.memoryr6  r  r
  r7  r  rf   r
  RawDescriptionHelpFormatterr   r{  rs  _cli_commandsr  r   rz  rs  hermes_cli.curatorhermes_cli.petshermes_cli.journeyr   r  r   r  r   r  r   r   
_functoolspartialr   r   r  r   r  r   r  r   rU  r   r 
  r   r^  r   r  r   r
  r   r.  r0  r   r  r   rO  r   rC  r  r  r  r   r6
  ior   r4  keysr  r
  r   StringIOr  r6   r7   rF  rn   r   re   r  r<   r  r  r  r~  r  r  r   r  )[r  r  
subparsersr  r  
moa_parsermoa_subparsersmoa_configure
moa_deleter  fallback_parserfallback_subparserssecrets_subparsers
secrets_bw
secrets_op_secrets_cli_op_secrets_clir!  
_proxy_clir'  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_clir4  bundles_parser_bundles_registerr5  r6  r
  r7  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_statusr`  r~  sessions_subparserssessions_listro  sessions_exportsessions_deletesessions_prunesessions_archivesessions_optimize_storagesessions_repairsessions_recoversessions_renamesessions_retitlesessions_browsecompletion_parserr  rr  _processed_argv_io_has_cmd_token_saved_stderrr  r;   rB  r   r   r%  r  r^  r_  r&  r?  r   s[                                                                                       @@@@@@@r   r  r  +  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
  **DM	 + 	 	N (66<M6NN#..4 /  J $..{#7 /  J 766666EEEEEEj)))  ,,,	 	 	 	 	 %6777 ))IV	 * 	 	M 322222M***      $4555
 @???????**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 *<@@@@
 '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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:::: *<@@@@
 :]CCCC
 j84444
 zY7777
 
z::::
 $..=E /   QPPPPP0111
 J:>>>>
# # # j;KLLLL
 
z::::
 j84444
 ====
 ====
 
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::::
 33I3K3KLLL..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 $$*:$;;; Z1111
 !++K> ,  O
 *88>O8PP'226@V2WWMJ     R.D     3    _
 _
 _
B *44C 5  O   P	 !      7776 !      7	 !      U !   
   +	 !       :	 !      D !    	4  
   S !   
   9%E	 !      !c !   
   = !    *442 5  O   4J KKK  l1D !    )33R 4  N 	<   B      +55Q 6   	1   ""P #   
 !4 > >^9	 !? ! ! **v	 +    **6	 +    *44MG	 5 	 	O   O !   
   A !    +55LG	 6 
 
 !!E	 "    !!J "   
 !!X "   
 !!] "   
 !!@	 "    !!;	 "    !!D "    ""71P"QQQ)448 5  O   4J KKK  :U VVV*55O/	 6 
 
 !!6 "   
 !!9	 "    *44Q 5  O   J !      S/T !      oNNN !    *<@@@@J~FFFF
 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 tVU## -),
%& 4    tY%% 
L$..T:t44T:t44t\488	
 	
 	
 	
 	 t) t|/C
 		- 		-MD' 4&& -dG,,, |	
 	- 	-MD' 4&& -dG,,, tV YYt__b# 	277HRLLLLL	 	77 	s   ( 
55A 
AA'$B 
BB=Q 
Q>Q99Q>F2e? ?
g 	-f;;g g. .
h/8-h**h/i 
j'-jj=k 
l-llQ8?AR8 R8ATS4AS<S<AT__main__r  r>   )rX   )F)rV  )NFNNNNNFNNFFFNF)r   )r   r   NNNr   )r   r   )r  (/  r	  hermes_bootstrapModuleNotFoundErrorhermes_cli._subprocess_compatr   r   r
   rg   r  rh   rd  r  pardir_bootstrap_rootr  r  r   r   _early_recovery_modrecover_if_neededr   objectr   r!   r(   rq   r<   rB   rE   rU   rV   __annotations__r|   r  r   r   r   r  r   r   r   r   r   r   r   r   r   r6   r8  r  r  r  r  r  r  rZ  r
  r   typingr   	functoolsr  hermes_cli.sessions_cmdr   hermes_cli.subcommands._sharedr   _add_accept_hooks_flaghermes_cli.subcommands.cronr   hermes_cli.subcommands.syncr   hermes_cli.subcommands.gatewayr   hermes_cli.subcommands.profiler   hermes_cli.subcommands.modelr   hermes_cli.subcommands.setupr   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.approvalsr   hermes_cli.subcommands.dumpr   hermes_cli.subcommands.debugr   hermes_cli.subcommands.backupr   !hermes_cli.subcommands.import_cmdr   #hermes_cli.subcommands.import_agentr   hermes_cli.subcommands.configr   hermes_cli.subcommands.skinr   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.monitoringr   hermes_cli.subcommands.skillsr   hermes_cli.subcommands.pairingr   hermes_cli.subcommands.pluginsr   hermes_cli.subcommands.mcpr   hermes_cli.subcommands.clawr   r   r  r&  r  r(  r  r)  _FORCE_IPV4_EARLYr-  _read_raw_early	_cfg_pathrj   _early_cfg_rawr.  apply_managed_overlayre   rf   _early_sec_cfgr   rp   _early_redactrs   _early_net_cfgr  r5  _setup_loggingr  r}   r  rB  _apply_ipv4r   r  r	  r  rE  rF  rG  hermes_cli.model_setup_flowsrH  rI  rJ  rK  rL  rM  rN  rO  rP  rQ  rR  rS  rT  rU  rV  rW  rX  rY  rZ  r[  r\  rz  rs  r  rc  rq  r  r  r  r  r  r  r  r  r  rT  r`  rk  rq  r  r  r  r  	frozensetr  r  r  r  r  r  r  r  r  r  r  r  r  r  rB  rO  r   rU  r[  r^  rk  r  r  r  r  r  r  r  r  r  r  r  r#  r  r  r  r  r  r  r  r  r  r  r  r  _DEFAULT_QWEN_PORTAL_MODELSrN  rV  r[  r^  rb  r  rq  rt  ry  r|  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r#  r'  r+  r/  r2  r6  r<  r?  rS  rU  r^  rj  hermes_cli.update_cmdrk  rl  rm  rn  ro  rp  rq  rr  rs  rt  ru  rv  rw  rx  ry  rz  r{  r|  r}  r~  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r4  r(  r2  r?  r6  rJ  rW  rP  rq  rw  ry  r  r  r}  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r,  r4  r7  r@  ra  rh  rl  ro  rw  r  r  	Namespacer  hermes_cli.dashboard_procsr  r  r  r  r  r  _DASHBOARD_SYSTEMD_UNITr  r  r  r  r  r  r  _warn_stale_dashboard_processesr	  r	  r
	  r	  r	  r	  r	  r%	  r	  r	  rA	  r/	  r[	  rI	  rJ	  rK	  r|	  rs	  r	  r	  r	  LAZY_REFRESH_IMPORT_PROBESr	  LAZY_REFRESH_REPAIR_PACKAGESr2	  r	  r1	  r	  r	  r0	  rD	  ri	  r	  r	  r	  rC	  r	  rf  r	  r
  r
  r	
  rm  r
  r 
  r6
  r
  r
  r
  r
  r
  r  r  r.  r0  r5  r@  rC  rO  rS  rX  ru  rq  rs  rv  r{  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r@   rA   r   <module>r"     s!4  * * *r	 	 	 	D	 H G G G G G      				 



 '""27<<0I0I29#U#UVV#(""HOOA''' $ $ $ $ $ $ > = = = = =	))++++ 	 	 	D	F t    :  $ $ $ $T -  -  - -  -  	- 
 -  -  
-  -  -  - `, , , , ,0 0 0 0% % % %Z )-  , , ,    >6 6- 6 6 6 6 6L   ,     )T ) ) ) )
;tCy ;T ; ; ; ;;tCy ;T ; ; ; ;< < < < <G G G G G G8D 8 8 8 8/3: / / / /
, , , ,, , , , ,
$t $ $ $ $ " ! # # # 
*Q--    				                        0 0 0 0 0 0 Z Z Z Z Z Z 9 9 9 9 9 9 9 9 9 9 9 9 ? ? ? ? ? ? ? ? ? ? ? ? ; ; ; ; ; ; ; ; ; ; ; ; A A A A A A ; ; ; ; ; ; ; ; ; ; ; ; = = = = = = 9 9 9 9 9 9 = = = = = = ? ? ? ? ? ? ; ; ; ; ; ; = = = = = = A A A A A A C C C C C C 9 9 9 9 9 9 ; ; ; ; ; ; = = = = = = E E E E E E I I I I I I = = = = = = 9 9 9 9 9 9 ? ? ? ? ? ? ? ? ? ? ? ? = = = = = = 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 E E E E E E = = = = = = ? ? ? ? ? ? ? ? ? ? ? ? 7 7 7 7 7 7 9 9 9 9 9 9s t    $ t**,,-- ! ! # # #jF jF jF jFZ      . - - - - - 4 4 4 4 4 4  |f4 5 5 5 5   	
 EDDDDD!!M1I (**0b	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    0 # (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    6:0 :0 :0 :0z2 2dTk 2TD[ 2 2 2 2D T    64 D    >SND SN4 SNE$s)T/4J SN SN SN SNl5f 5c 5 5 5 50,8C= , , , ,^: :S :C : : : :0! !x~ ! ! ! ! !.t . . . . .. (,""!#c c}cc C=c sm	c
 c c d^c c C=c C=c c c c }c c c c cL   *   ((d ( ( ( (VR R Rj    fY fY fYR& & &&  
) 
) 
)c d    @' @' @' @'F
 
 
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GQ GQ GQ GQX7B 7B 7BL , K K K    +# +$ + + + +M M M Mn 	[ [[ [ 	[
 [ [ [ [@C C    
 
 
 
 
 
*Z Z Z~          @ @ @F    D     " " "    
 
 
          	 	 	   26 , , ,$ ,$ , , , ,^, , ,
  <     @M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M Md 5 G G G G$+D +D +D +D\"M$ "M4 "M "M "M "MJ6t 6d 6s 6 6 6 6r9D 9 9 9 9DD D4 DD D D D D( !$!%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 #%!%Q" Q" Q"	Q"	Q" c3h	Q"
 Q" 
c3h$	Q"  Q" Q" Q" Q"h+Q	c+Q 
+Q 
c3h	+Q
 +Q  +Q +Q +Q +Q\C C$J    * 38 ( ( (4 (4 (D ( ( ( (V 6; K K Kd Kd Kt K K K K\: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~ !: !: !: !:f     n}w  wwe  #- "9hsm "9 "9 "9 "9JHSM    B&/ &/ &/ &/ &/RDs D D D D<0D 0S 0 0 0 0fd x}    t     $6d 6t 6 6 6 63t 3 3 3 3 3:--,4TN-
- - - -`"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)T )hsm ) ) ) )*x}    @ $    @ 69^ ^ ^	^#^/2^	^ ^ ^ ^H 48A A AA #+4.A 
	A A A AHD $ 4    8    6	/t 	/PT 	/ 	/ 	/ 	/#d #t # # # #L#tCy#~!6 # # # #Lr'($ r' r' r' r'p          %)W W Wc(T/W 
#YW W W W&c &eCcM.BT.I & & & &Rd
 s     5 
 (i ii
i 
i i i iXc cDj    <S S4Z    c cDj    (( (3 (S4Z (SW ( ( ( (V(C (DI,< ( ( ( (V(.4S	? (.tDI (. (. (. (.Z #B  S T#Y    N/T / / / /54 5 5 5 5T d     BT BS BT B B B BI I I I
U U U U
[ [ [ [|
 
 
 
@OF OF OF OFd         F/eDItCH~7L,L&M / / / /( "&&(	$ $ $	c$ 
c3h$	$ !$	$
 
$ $ $ $N#T # # # #14$; 1 1 1 1C4 CDJ C C C C$ /0d d dd(+d	%d

d d d dN d  t          FT%d
*;%<      "&#	  	c 
c3h$	 	
 
   D 4$; $    : 2 U5c?C#78   
 4 tCH~    "&. . .	c. 
c3h$	. 
	. . . .(Dc (DtCy (D (D (D (D\ "&I I IS	I 
c3h$	I 
#Y	I I I I` "&	  S	3i 
c3h$		
 
   8 "&) ) )S	) 
c3h$	) 		) ) ) )^ "&	?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[   :/ /S#X- / / / / /3 4    &#3: # # # #LF- F- F- F- F- F- F- F-RN NT N N N Nb  ,GC G G G G
) 
)# 
) 
) 
) 
)H2 H2 H2VLd Lt L L L L^F	 F	 F	RB
 B
 B
 B
J#    8s # $    U U U UpVs Vs V V V Vr1 1 1 1 1 1w w wt	    
% 
% 
% 
%    *  
c 
 
 
 
2 !y    < #    $ d
        Fd    *
t 
 
 
 
 
: .--eV_-!E7+7)$  T T T T T    0D 0 0 0 0R
 R
 R
 R
j, , , ,) ) ) )CT C C C CL&T & & & &R< < <~  .  "1 1 1* * *Z
 
 
        i i iX+ zDFFFFF sy   
 .B> >CC3P M( 'P (M0-P /M00B%P PP"9Q Q$#Q$*Q= =RR