
    #j#                        d Z ddlmZ ddlZddlZddlZddlZddlZddlZddl	m
Z
 ddlmZ ddlmZ  ej        e          Zdd	Zd dZ G d de          Zd dZd Zd dZd!dZd"dZd"dZd#d$dZdS )%u  Managed uv — one path, no guessing.

Hermes owns its own uv binary at ``$HERMES_HOME/bin/uv`` (or ``uv.exe`` on
Windows).  Every code path that needs uv resolves it from that single location.
If the binary is missing, ``ensure_uv()`` bootstraps it via the official
standalone installer with ``UV_UNMANAGED_INSTALL`` / ``UV_INSTALL_DIR`` pointed
at ``$HERMES_HOME/bin`` so the installer writes directly there — no PATH
probing, no conda guards, no multi-location resolution chains.
    )annotationsN)Path)Optional)get_hermes_homereturnr   c                 l    t                      } t          j                    dk    r| dz  dz  S | dz  dz  S )u   Return the path where Hermes keeps *its* uv binary.

    ``$HERMES_HOME/bin/uv`` on POSIX, ``$HERMES_HOME\bin\uv.exe`` on
    Windows.  The directory may not exist yet — callers should use
    ``ensure_uv()`` to bootstrap it.
    Windowsbinzuv.exeuv)r   platformsystem)homes    :/home/ubuntu/.hermes/hermes-agent/hermes_cli/managed_uv.pymanaged_uv_pathr      s@     DI%%e|h&&%<$    Optional[str]c                     t                      } |                                 r.t          j        | t          j                  rt          |           S dS )ub   Return the managed uv path if it exists, else ``None``.

    No side effects — pure lookup.
    N)r   is_fileosaccessX_OKstr)ps    r   
resolve_uvr   +   sD    
 	Ayy{{ ryBG,, 1vv4r   c                  8     e Zd ZU dZded<   dd fd
Zd Z xZS )	_UvResultu  ``ensure_uv()`` return value that survives an update boundary.

    ``ensure_uv()``'s arity has flipped between a single path string and a
    ``(path, fresh_bootstrap)`` tuple across releases. ``hermes update`` runs
    the call site from the *old*, already-imported ``hermes_cli.main`` against
    this *freshly pulled* module, so the two can disagree on how many values
    ``ensure_uv()`` returns. An install parked on a 2-tuple release runs
    ``uv_bin, fresh_bootstrap = ensure_uv()`` against the single-value module
    and crashes the first update: the returned path is a plain ``str``, which is
    itself iterable, so the 2-target unpack walks its characters and raises
    ``ValueError: too many values to unpack (expected 2)`` (and on the failure
    path the ``None`` return raises ``TypeError: cannot unpack non-iterable
    NoneType``). This wrapper answers to both conventions:

        uv_bin = ensure_uv()         # behaves as the path str ("" when absent)
        uv_bin, fresh = ensure_uv()  # unpacks as (path|None, fresh_bootstrap)

    Missing uv is the empty string (falsy) instead of ``None`` so legacy
    2-target call sites can still unpack a failure without raising, while
    ``if not uv_bin`` keeps working for single-value callers.

    POSIX only. This wrapper is **never** returned on Windows — see
    ``ensure_uv()`` for why the ``__iter__`` override is unsafe there.
    boolfresh_bootstrapFpathr   freshr   '_UvResult'c                ^    t                                          | |pd          }||_        |S )N )super__new__r   )clsr   r    self	__class__s       r   r%   z_UvResult.__new__R   s+    wwsDJB//$r   c                L    t          t          |           pd | j        f          S N)iterr   r   )r'   s    r   __iter__z_UvResult.__iter__W   s%     c$ii'4$*>?@@@r   )F)r   r   r    r   r   r!   )__name__
__module____qualname____doc____annotations__r%   r,   __classcell__)r(   s   @r   r   r   6   sr          2       
A A A A A A Ar   r   c                 "   t                      } | r| S t                      }|j                            dd           t	          d|j         d           	 t          |           nE# t          $ r8}t                              d|           t	          d|            Y d}~dS d}~ww xY wt                      }|rEt          j
        |dgddd	
          j                                        }t	          d| d           nt	          d           |S )zQResolve the managed uv path, installing it if necessary (plain ``str``/``None``).T)parentsexist_oku!     → Installing managed uv into z ...zManaged uv install failed: %su$     ✗ Failed to install managed uv: N	--versionFcapture_outputtextchecku     ✓ Managed uv installed ()uA     ✗ Managed uv install appeared to succeed but binary not found)r   r   parentmkdirprint_install_uv	Exceptionloggerwarning
subprocessrunstdoutstrip)existingtargetexcresultversions        r   _ensure_uv_pathrL   ^   sF   ||H F
Mt444	
Afm
A
A
ABBBF   6<<<:S::;;;ttttt \\F 	S.[!	
 
 

  	 	7W7778888QRRRMs   A& &
B(0-B##B(c                 n    t                      } t          j                    dk    r| S t          |           S )u  Return the managed uv path, installing it first if necessary.

    On **POSIX** the result is a :class:`_UvResult` (a ``str`` subclass) that is
    both usable directly as the path *and* unpackable as
    ``(path, fresh_bootstrap)`` for older call sites parked on a 2-tuple
    release — see :class:`_UvResult` for the update-boundary rationale.

    On **Windows** we deliberately return a plain ``str``/``None`` instead.
    ``subprocess`` there serializes the argv via ``subprocess.list2cmdline``,
    which iterates every entry *as a string* (``for c in arg``). The dependency
    installer passes uv straight into the command list (``[uv_bin, "pip", ...]``),
    so a ``_UvResult`` — whose ``__iter__`` yields ``(path, fresh_bootstrap)``
    rather than characters — would inject the bool into the command line and
    crash the install with ``TypeError: sequence item 1: expected str instance,
    bool found``. A plain ``str`` matches the historical Windows contract and is
    subprocess-safe. (A single value cannot satisfy both 2-target unpacking and
    Windows char-iteration: both use the iterator protocol, with contradictory
    results.)

    On failure the result is falsy — never raises — so callers can fall back to
    pip gracefully.
    r	   )rL   r   r   r   )rJ   s    r   	ensure_uvrN      s7    . FI%% Vr   c                 L   t                      } | sdS t          j        | ddgddd          }|j        dk    rEt          j        | dgddd          j                                        }t          d	| d
           n&t                              d|j        |j	                   | S )u  Run ``uv self update`` on the managed uv binary.

    Call this during ``hermes update`` so the managed copy stays current.
    Returns the managed path on success, ``None`` if uv isn't available or
    the self-update fails (non-fatal — the old version still works).
    Nr'   updateTFr7   r   r6   u     ✓ Managed uv updated (r;   z!uv self update failed (rc=%d): %s)
r   rC   rD   
returncoderE   rF   r>   rA   debugstderr)rG   rJ   rK   s      r   update_managed_uvrT      s     ||H t^	68$	  F A.{#	
 
 

  	 	575556666 	8&:KV][[[Or   rH   Nonec                    t          j                    }i t          j        t	          | j                  t	          | j                  d}|dk    rt          |           dS t          |           dS )a  Bootstrap uv into *target* using the official standalone installer.

    Uses ``UV_UNMANAGED_INSTALL`` (POSIX) or ``UV_INSTALL_DIR`` (Windows)
    so the astral installer writes the binary directly into
    ``$HERMES_HOME/bin/`` instead of ``~/.local/bin/``.
    )UV_UNMANAGED_INSTALLUV_INSTALL_DIRr	   N)r   r   r   environr   r<   _install_uv_windows_install_uv_posix)rH   r   envs      r   r?   r?      s     _F
*
 !$FM 2 2fm,,  C C     #r   r\   dict[str, str]c                ~   t          j        dd          5 }|j        }ddd           n# 1 swxY w Y   	 t          j        dddd|gd	d	
           t          j        d|g| d	d	           	 t          j        |           dS # t          $ r Y dS w xY w# 	 t          j        |           w # t          $ r Y w w xY wxY w)zHDownload + sh the POSIX installer (two-stage to avoid curl|sh pitfalls).z.shF)suffixdeleteNcurlz-LsSfzhttps://astral.sh/uv/install.shz-oT)r:   r8   shr\   r:   r8   )tempfileNamedTemporaryFilenamerC   rD   r   unlinkOSError)r\   finstaller_paths      r   r[   r[      sI   		$E%	@	@	@  A                             W?~V	
 	
 	
 	

 	>"		
 	
 	
 	
	In%%%%% 	 	 	DD		In%%%% 	 	 	D	sM   +//6B .B 
BBB<B,+B<,
B96B<8B99B<c                D    d}t          j        dddd|g| dd           dS )	z Invoke the PowerShell installer.z*irm https://astral.sh/uv/install.ps1 | iex
powershellz-ExecutionPolicyBypassz-cTrc   N)rC   rD   )r\   cmds     r   rZ   rZ      sI     	5  N	)8T3?	     r   3.11uv_binr   venv_dirpython_versionr   c                    d S r*    )rp   rq   rr   s      r   rebuild_venvru      s    Dr   )r   r   )r   r   )rH   r   r   rU   )r\   r]   r   rU   )ro   )rp   r   rq   r   rr   r   r   r   )r0   
__future__r   loggingr   r   shutilrC   rd   pathlibr   typingr   hermes_constantsr   	getLoggerr-   rA   r   r   r   r   rL   rN   rT   r?   r[   rZ   ru   rt   r   r   <module>r}      s    # " " " " "  				                    , , , , , ,		8	$	$
 
 
 
   %A %A %A %A %A %A %A %AP   B  >   H   .   0
 
 
 
	 	 	 	 	 	 	r   