
    `gj                        U d Z ddlmZ ddlmZ  eh d      Zded<    ed       G d	 d
             ZddZ	ddZ
ddZddZy)u  Unified provider catalog — one source of truth for the provider universe.

The provider list shown by ``hermes model`` (CLI/TUI) and the desktop Settings
→ Providers tabs (Accounts + API keys) **must be the same set**.  Historically
they were not: the CLI picker read :data:`hermes_cli.models.CANONICAL_PROVIDERS`
(which auto-extends from ``plugins/model-providers/<name>/``), while the desktop
tabs read separate hand-maintained lists (``_OAUTH_PROVIDER_CATALOG``,
``OPTIONAL_ENV_VARS`` + ``PROVIDER_GROUPS``) that nobody kept in sync.  Every
provider added after those lists were written silently went missing from the
GUI — e.g. GitHub Copilot showing up only under "tools", or ``openai-api`` being
configurable from the CLI but not the desktop app.

This module fixes that at the root: it derives ONE descriptor per provider from
the same universe ``hermes model`` renders (``CANONICAL_PROVIDERS``), joining:

* ``auth_type`` / ``api_key_env_vars`` / ``base_url_env_var`` from
  :data:`hermes_cli.auth.PROVIDER_REGISTRY` (credential truth), and
* ``display_name`` / ``description`` / ``signup_url`` from the provider's
  :class:`providers.base.ProviderProfile` when one exists, falling back to the
  ``CANONICAL_PROVIDERS`` entry's ``label`` / ``tui_desc`` and the
  ``OPTIONAL_ENV_VARS`` signup URL otherwise (many profiles leave these blank,
  and four canonical providers have no profile at all — lmstudio, openai-api,
  tencent-tokenhub, xai-oauth — so the fallbacks are load-bearing).

Each descriptor is tagged with the ``tab`` it belongs on (``keys`` vs
``accounts``) based purely on how the provider authenticates.  The desktop
``/api/env`` and ``/api/providers/oauth`` endpoints derive their MEMBERSHIP from
this catalog; the old hand lists are demoted to presentation/override overlays
(bespoke OAuth flow + status resolvers, richer copy, icons, ordering) and no
longer decide which providers exist.

Parity contract (locked by tests): the union of the two tabs equals the
``CANONICAL_PROVIDERS`` universe, i.e. exactly what ``hermes model`` shows.
    )annotations)	dataclass>   copilotoauth_minimaxoauth_externalexternal_processoauth_device_codezfrozenset[str]_ACCOUNTS_AUTH_TYPEST)frozenc                  l    e Zd ZU dZded<   ded<   ded<   ded<   ded<   ded	<   ded
<   ded<   ded<   y)ProviderDescriptorzDOne provider, as seen by every surface (CLI picker + both GUI tabs).strsluglabeldescription	auth_typetabtuple[str, ...]api_key_env_varsbase_url_env_var
signup_urlintorderN)__name__
__module____qualname____doc____annotations__     N/root/.hermes/venv/lib/python3.12/site-packages/hermes_cli/provider_catalog.pyr   r   8   s5    N
IJN	H%%OJr    r   c                    | t         v rdS dS )zEReturn the desktop tab ("keys"|"accounts") a provider's auth maps to.accountskeys)r
   )r   s    r!   tab_for_auth_typer%   G   s    "&:::FFr    c                T    t        d | D              }t        d | D        d      }||fS )zASplit a profile's ``env_vars`` into (api_key_vars, base_url_var).c              3  h   K   | ]*  }|j                  d       r|j                  d      r'| , yw	_BASE_URL_URLNendswith.0vs     r!   	<genexpr>z"_split_env_vars.<locals>.<genexpr>N   s'     \qQZZ-D

SYHZ\s   222c              3  f   K   | ])  }|j                  d       s|j                  d      s&| + ywr(   r+   r-   s     r!   r0   z"_split_env_vars.<locals>.<genexpr>O   s&     Vq

;(?1::fCUVs   '11 )tuplenext)env_varsr$   bases      r!   _split_env_varsr7   L   s/    \H\\DVHVXZ[D:r    c                    ddl m}  	 ddlm} 	 ddlm}  |       D ci c]  }|j                  | }}	 ddlm	} 	 ddl
m} g }t        |       D ]  \  }}	|	j                  }
|j                  |
      }|j                  |
      }|j                  |
      }|rt        |dd      ndxs( |rt        |dd      ndxs |rt        |dd      ndxs d	}|r4t        |d
d      r't!        |j"                        }t        |dd      xs d}n6|r/t        |dd      r"t%        t!        |j&                              \  }}nd\  }}|rt        |dd      ndxs |	j(                  xs |
}|rt        |dd      ndxs |	j*                  xs |}|rt        |dd      ndxs d}|s/|r-|j                  |d         xs i }|j                  d      xs d}|j-                  t/        |
|||t1        |      ||||	              |S # t        $ r i }Y w xY wc c}w # t        $ r i }Y w xY w# t        $ r i }Y w xY w# t        $ r i }Y  w xY w)a  Return one descriptor per provider in the ``hermes model`` universe.

    Membership is :data:`CANONICAL_PROVIDERS` (the list the CLI/TUI picker
    renders, which auto-extends from provider plugins).  Auth + env come from
    ``PROVIDER_REGISTRY``; display metadata from ``ProviderProfile`` with
    canonical/env fallbacks so providers without a profile (or with blank
    profile metadata) still resolve sensibly.
    r   )CANONICAL_PROVIDERS)PROVIDER_REGISTRY)list_providers)OPTIONAL_ENV_VARS)HERMES_OVERLAYSr   r2   api_keyr   r   r   r5   )r   r2   display_namer   r   url)	r   r   r   r   r   r   r   r   r   )hermes_cli.modelsr9   hermes_cli.authr:   	Exception	providersr;   namehermes_cli.configr<   hermes_cli.providersr=   	enumerater   getgetattrr3   r   r7   r5   r   tui_descappendr   r%   )r9   r:   r;   pprofilesr<   r=   outr   entryr   cfgprofoverlayr   api_key_varsbase_url_varr   r   r   infos                        r!   provider_catalogrW   S   s    6
5,'5'78!AFFAI8878 %'C!"56 4
uzz##D)||D!!%%d+
 /2WS+r*r 26k2.B5<b1" 	 	 73 2B7 !5!56L"3(:B?E2LgdJ3)8t}}9M)N&L,)/&L, 37WT>2.B {{ 	 26WT="-2 ~~ 	
 :>gdL"52L"
l$((a9?RD%.BJ

'#%i0!-!-%
	
Q4
j J[   9 
    sT   G9 H HH H" H4 9HHH HH"H10H14IIc                 R    t               D  ci c]  } | j                  |  c} S c c} w )z'Convenience: the catalog keyed by slug.)rW   r   )ds    r!   provider_catalog_by_slugrZ      s!    /12!AFFAI222s   $N)r   r   returnr   )r5   r   r[   ztuple[tuple[str, ...], str])r[   zlist[ProviderDescriptor])r[   zdict[str, ProviderDescriptor])r   
__future__r   dataclassesr   	frozensetr
   r   r   r%   r7   rW   rZ   r   r    r!   <module>r_      sa   !F # ! (1( n  $  G
]@3r    