
    +j                        U d Z ddlmZ ddlZddlmZmZ ddlmZm	Z	  ej
                    Zi Zded<   d	ad
ed<   i Zded<   ddZddZddZddZdddd dZd!dZdS )"u  
LightClaw — multi-tenant API key routing.
Mirrors: src/config.ts (L67-169) — resolveEffectiveApiKey, setApiKeyMap,
setSessionApiKey, extractUinFromSessionKey, buildAuthHeaders.

Routing model
-------------
Two lookup tables are maintained at module scope:

    _global_api_key_map : { uin: apiKey }
        Built once at adapter startup from the configured API keys + the
        uin(s) returned by POST /cgi/ticket. Used by inbound handlers
        (which know the sender's uin directly).

    _session_key_to_api_key : { sessionKey.lower(): apiKey }
        Populated by the inbound handler when it starts processing a
        message. AI tool handlers (which only know ``ctx.sessionKey``)
        use this table to resolve the tenant's key without having to
        re-extract the uin.

Resolution priority (mirrors TS):
    1. sessionKey direct hit
    2. senderId direct hit
    3. uin extracted from sessionKey → global map
    4. _global_default_api_key
    )annotationsN)DictOptional   )CHANNEL_KEY	X_PRODUCTDict[str, str]_global_api_key_map str_global_default_api_key_session_key_to_api_keymappingdefault_api_keyreturnNonec                    t           5  t                                           | rt                              |            |pdaddd           dS # 1 swxY w Y   dS )u   Replace the uin→apiKey map and the default key.

    Called once by ``LightClawAdapter.connect()`` after identity
    resolution. *mapping* may be empty — in that case every lookup falls
    through to *default_api_key*.
    r   N)_lockr
   clearupdater   )r   r   s     ;/home/agentuser/.hermes/plugins/lightclawbot/src/tenancy.pyset_api_key_mapr   1   s     
 8 8!!### 	0&&w///"1"7R	8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8s   :AAAsession_keyapi_keyc                    | r|sdS t           5  |t          |                                 <   ddd           dS # 1 swxY w Y   dS )u   Record a ``sessionKey → apiKey`` association.

    Called by the inbound handler right before dispatching a message to
    the agent so that tools executing within that session can look up
    the tenant's API key via the same session key.
    N)r   r   lower)r   r   s     r   set_session_api_keyr   @   s      g 	 ? ?7> 1 1 3 34? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?s   8<<c                    | sdS t           5  t                              |                                 d           ddd           dS # 1 swxY w Y   dS )z@Best-effort cleanup; safe to call even if the key was never set.N)r   r   popr   )r   s    r   clear_session_api_keyr    M   s     	 ? ?##K$5$5$7$7>>>? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?s   .AAAOptional[str]c                   | sdS |                                  }dD ]Y}|                    |          }|dk    r<| |t          |          z   d         }|                    dd          d         }|pdc S Zt           d}|                    |                                           r| t          |          d         }|pdS dS )a  Extract the peer uin embedded in a session key.

    Supported shapes (lowercased for matching, returned case-preserving):
        * ``agent:<agentId>:<channel>:direct:<peerId>[...]``
        * ``agent:<agentId>:<channel>:dm:<peerId>[...]``
        * legacy ``<channel>:dm:<peerId>``

    Returns ``None`` when no uin segment can be located (e.g. the main
    session key ``agent:main:main``).
    N)z:direct::dm:r   :r   r#   )r   findlensplitr   
startswith)r   r   markeridxtailpeerlegacy_prefix	remainders           r   extract_uin_from_session_keyr/   Y   s      tE&    jj  !88sS[[0112D::c1%%a(D<4  #(((M++--.. !M 2 2 3 34	 D 4    )r   	sender_idr1   c                   t           5  | r<t                              |                                           }|r|cddd           S |r*t                              |          }|r|cddd           S | r;t          |           }|r*t                              |          }|r|cddd           S t          cddd           S # 1 swxY w Y   dS )zResolve the tenant's apiKey for a given context.

    Returns the default key (possibly empty string) when no specific
    match is found.  Callers should generally have at least one of
    *session_key* or *sender_id* populated.
    N)r   r   getr   r
   r/   r   )r   r1   hituins       r   resolve_effective_api_keyr6   }   s    
 ' ' 	)--k.?.?.A.ABBC ' ' ' ' ' ' ' '  	%)))44C ' ' ' ' ' ' ' '  	.{;;C )--c22 '' ' ' ' ' ' ' ', '-' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 's#   2CC30C0CC
Cc                     d| pd t           dS )zFBuild the standard auth headers used by /cgi/* and /drive/* endpoints.zBearer r   )authorizationz	x-product)r   )r   s    r   build_auth_headersr9      s%     37=b22  r0   )r   r	   r   r   r   r   )r   r   r   r   r   r   )r   r   r   r   )r   r   r   r!   )r   r!   r1   r!   r   r   )r   r   r   r	   )__doc__
__future__r   	threadingtypingr   r   configr   r   RLockr   r
   __annotations__r   r   r   r   r    r/   r6   r9    r0   r   <module>rB      sA    6 # " " " " "     ! ! ! ! ! ! ! ! * * * * * * * * 		&(  ( ( ( (!  ! ! ! !*,  , , , ,8 8 8 8
? 
? 
? 
?? ? ? ?   L "&#!' !' !' !' !' !'P     r0   