Ë
    âß²jsv  ã                  ó´  — U d Z ddlmZ ddlZddlZddlZddlmZ ddlm	Z	m
Z
mZmZmZ ddlmZ ddlmZ  ej$                  d«      Z eh d	£«      Zd
ZdZd(d„Zd)d„Zd*d„ZdZg Zded<   i Zded<   dZd+d„Z d,d„Z!d-d„Z"d.d„Z#d.d„Z$d/d„Z%d0d„Z&d1d„Z'	 	 	 	 	 	 d2d„Z(d3d„Z)d4d„Z*d5d „Z+	 d6	 	 	 	 	 	 	 	 	 d7d!„Z,d8d"„Z-d9d#„Z.	 	 	 	 	 	 d:d$„Z/ G d%„ d&«      Z0d5d'„Z1y);a  Single-use OAuth grant hygiene: strip cloned grants from profiles, heal forked grants.

Split out of ``hermes_cli/auth.py`` and re-exported there; origin helpers are imported lazily
inside each function so ``hermes_cli.auth.<name>`` patches still intercept (and no import cycle).
é    )ÚannotationsN)ÚPath)ÚAnyÚDictÚListÚOptionalÚTuple)Ú_decode_jwt_claims)Úfile_signaturezhermes_cli.auth>   Ú	anthropicú	xai-oauthúopenai-codex)ú.anthropic_oauth.json)r   r   c                ój  — t        | t        «      syt        | j                  d«      xs d«      j	                  «       j                  «       dk(  xsd t        t        | j                  d«      xs d«      j	                  «       «      xs- t        | j                  d«      xs d«      j                  d«      S )NFÚ	auth_typeÚ ÚoauthÚrefresh_tokenÚaccess_tokenz
sk-ant-oat)Ú
isinstanceÚdictÚstrÚgetÚstripÚlowerÚboolÚ
startswith)Úentrys    úA/home/ubuntu/.hermes/hermes-agent/hermes_cli/auth_oauth_grants.pyÚ_is_oauth_pool_payloadr    $   s•   € Ü�eœTÔ"Øô 	ˆE�I‰I�kÓ"Ò( bÓ)×/Ñ/Ó1×7Ñ7Ó9¸WÑDò 	IÜ”�E—I‘I˜oÓ.Ò4°"Ó5×;Ñ;Ó=Ó>ò	Iäˆu�y‰y˜Ó(Ò.¨BÓ/×:Ñ:¸<ÓHðJó    c                ó\   — t        | j                  d«      xs d«      j                  d«      S )NÚsourcer   Úhermes_pkce)r   r   Úendswith)Úrows    r   Ú_is_pkce_rowr'   /   s&   € Üˆs�w‰w�xÓ Ò& BÓ'×0Ñ0°Ó?Ð?r!   c                ó`  — ddl m}m} g g g dœ}t        | «      } t        D ]L  }| |z  }	 |j                  «       s|j                  «       r$|j                  «        |d   j                  |«       ŒN | dz  }|j                  «       s|S 	 dd	lm}  |«       dz  } |||«      s!|j                  «       r|j                  |«      r|S 	 t#        j$                  |j'                  d
¬«      «      }	t+        |	t,        «      s|S d}
|	j/                  d«      }t+        |t,        «      rŽt1        |«      D ]€  }|j/                  |«      }|t2        vst+        |t0        «      sŒ-|D �cg c]  }t5        |«      rŒ|‘Œ }}t7        |«      t7        |«      k7  sŒ`d}
|d   j                  |«       |r|||<   Œ~||= Œ‚ |	j/                  d«      }t+        |t,        «      rIt8        D ]@  }|j/                  |«      }t+        |t,        «      sŒ%|sŒ(||= |d   j                  |«       d}
ŒB |
s|S 	  ||	|¬«       |S # t        $ r t        j                  d|| d¬«       Y �Œ,w xY w# t         $ r |cY S w xY w# t        t"        j(                  f$ r d}	Y �Œ–w xY wc c}w # t         $ r t        j                  d|d¬«       Y |S w xY w)u  Remove forked single-use OAuth grants from a freshly cloned profile.

    Called after any path that copies credential files between profiles (``hermes profile create
    --clone-all``, the dashboard/TUI ``mirror_credentials`` flow). API-key pool rows are kept â€” a
    static key is safe to duplicate. Returns ``{"pool": [...provider ids], "providers": [...],
    "files": [...]}`` of what was stripped. Never raises: a clone must not fail because hygiene
    could not run â€” the caller logs the summary.
    r   )Ú
_same_pathÚ_save_auth_store)ÚpoolÚ	providersÚfilesr-   z"Could not remove cloned %s from %sT©Úexc_infoú	auth.json)Úget_default_hermes_rootz	utf-8-sig©ÚencodingNFÚcredential_poolr+   r,   ©Útarget_pathz6Failed to strip cloned single-use OAuth grants from %s)Úhermes_cli.authr)   r*   r   Ú SINGLE_USE_OAUTH_SINGLETON_FILESÚis_fileÚ
is_symlinkÚunlinkÚappendÚOSErrorÚloggerÚdebugÚhermes_constantsr1   ÚexistsÚsamefileÚ	ExceptionÚjsonÚloadsÚ	read_textÚJSONDecodeErrorr   r   r   ÚlistÚ!SINGLE_USE_REFRESH_POOL_PROVIDERSr    ÚlenÚ_DEVICE_CODE_BLOCK_PROVIDERS)Úprofile_dirr)   r*   ÚstrippedÚnameÚtargetÚ	auth_pathr1   Úroot_auth_pathÚstoreÚchangedr+   Úprovider_idÚentriesÚeÚkeptr,   Úblocks                     r   Ú$strip_cloned_single_use_oauth_grantsrY   3   sÇ  € ÷ =Ø(*¸ÀbÑI€HÜ�{Ó#€KÜ0ò aˆØ˜tÑ#ˆð	aØ�~‰~Ô 6×#4Ñ#4Ô#6Ø—‘”Ø˜Ñ!×(Ñ(¨Ô.øðað ˜kÑ)€IØ×ÑÔØˆð
Ý<Ù0Ó2°[Ñ@ˆÙ�i Ô0Ø×!Ñ!Ô#¨	×(:Ñ(:¸>Ô(JàˆOðÜ—
‘
˜9×.Ñ.¸Ð.ÓDÓEˆô �eœTÔ"ØˆØ€GØ�9‰9Ð&Ó'€DÜ�$œÔÜ ›:ò 	*ˆKØ—h‘h˜{Ó+ˆGØÔ#DÑDÜ% g¬tÔ4ØØ&ÖH˜!Ô.DÀQÕ.G’AÐHˆDÐHÜ�4‹yœC ›LÓ(Ø�Ø˜Ñ ×'Ñ'¨Ô4ÙØ(,�D˜Ò%ð ˜[Ñ)ð	*ð —	‘	˜+Ó&€IÜ�)œTÔ"ô 8ò 	ˆKØ—M‘M +Ó.ˆEÜ˜%¤Õ&ª5Ø˜kÐ*Ø˜Ñ%×,Ñ,¨[Ô9Ø‘ð	ñ Øˆð`Ù˜¨IÕ6ð €Oøôu ò 	aÜ�L‰LÐ=¸tÀ[Ð[_ˆL×`ð	aûô  ò ØŠðûô ”T×)Ñ)Ð*ò Ø‹ðüò Iøô. ò `Ü�‰ØDÀiÐZ^ð 	õ 	`à€Oð`úsT   ©AH'Â;I Ã%I! ÅJÅ+JÈ
J È'"IÉIÉIÉIÉ!J É?J Ê!J-Ê,J-)r   r   Ú
expires_atÚexpires_at_msÚlast_refreshú	List[str]Ú_oauth_heal_noticeszDict[str, Tuple[Any, ...]]Ú_oauth_heal_clean_markszoauth_heal_clean.jsonc                óR   — t        j                  t        j                  | «      «      S )zfThe fingerprint as it reads back from JSON (tuples become lists), so the on-disk compare
    is exact.)rD   rE   Údumps)Úfingerprints    r   Ú_json_shaperc   ”   s   € ô �:‰:”d—j‘j Ó-Ó.Ð.r!   c                 ód   — 	 ddl m}   | «       j                  dz  t        z  S # t        $ r Y yw xY w)z?Where the persisted clean marks live, or None when unavailable.r   ©Ú_auth_file_pathÚcacheN)r7   rf   ÚparentÚ_OAUTH_HEAL_CLEAN_MARK_FILENAMErC   re   s    r   Ú_oauth_heal_clean_mark_pathrj   š   s6   € ðÝ3áÓ ×'Ñ'¨'Ñ1Ô4SÑSÐSøÜò Ùðús   ‚ # £	/®/c                óò   — t        «       }|€y	 t        j                  |j                  d¬«      «      }t        |t        «      sy|j                  | «      }t        |t        «      r|S dS # t        $ r Y yw xY w)u$  The stored clean-mark fingerprint for ``provider_id``, or None.

    Pure cache read: any problem at all (absent, unreadable, corrupt, wrong shape) means "no
    mark", which falls through to the locked heal â€” the pre-existing behaviour. It must never
    raise into a credential path.
    Núutf-8r2   )	rj   rD   rE   rF   rC   r   r   r   rH   )rT   ÚpathÚdataÚstoreds       r   Ú!_persisted_oauth_heal_fingerprintrp   ¤   sw   € ô 'Ó(€DØ€|ØðÜ�z‰z˜$Ÿ.™.°'˜.Ó:Ó;ˆô �dœDÔ!ØØ�X‰X�kÓ"€FÜ ¬Ô-ˆ6Ð7°4Ð7øô ò Ùðús   �%A* Á*	A6Á5A6c                ó.  — t        «       }|€y	 ddlm} 	 t        j                  |j                  d¬«      «      }t        |t        «      si }|j                  «       D ��ci c](  \  }}t        |t        «      rt        |t        «      r||“Œ* }}}t        |«      }|j                  | «      |k(  ry||| <   ddlm}	  |	|j                   «        |||d¬«       y# t        $ r i }Y Œ«w xY wc c}}w # t        $ r t"        j%                  d	| d
¬«       Y yw xY w)zÍRecord ``provider_id`` as clean for this exact fingerprint.

    Best-effort by design: a failed write only means the next process re-runs the heal, which is
    what it did before this cache existed.
    Nr   )Úatomic_json_writerl   r2   )Úmkdir_under_hermes_homei€  )Úmodez1%s: could not persist the forked-OAuth clean markTr.   )rj   Úutilsrr   rD   rE   rF   rC   r   r   Úitemsr   rH   rc   r   r@   rs   rh   r>   r?   )
rT   rb   rm   rr   ÚexistingÚkeyÚvalueÚmarksÚnew_markrs   s
             r   Ú_persist_oauth_heal_clean_markr|   ¸   s  € ô 'Ó(€DØ€|Øð]Ý+ð	Ü—z‘z $§.¡.¸' .Ó"BÓCˆHô ˜(¤DÔ)ØˆHà)1¯©Ó)9÷
Ù%˜3 Ü˜#œsÔ#¬
°5¼$Ô(?ð �‰Jð
ˆñ 
ô ˜{Ó+ˆØ�9‰9�[Ó! XÒ-ØØ%ˆˆkÑÝ<Ù §¡Ô,á˜$ ¨EÖ2øô ò 	ØŠHð	üó
øô ò ]Ü�‰Ø?ÀÐW[ð 	ö 	]ð]úsE   �C0 –%C »%C0 Á -C*Â"C0 Â0(C0 ÃC'Ã$C0 Ã&C'Ã'	C0 Ã0!DÄDc                ó.   — |t         | <   t        | |«       y)z0Stamp the clean mark both in memory and on disk.N)r_   r|   )rT   rb   s     r   Ú_mark_oauth_heal_cleanr~   Û   s   € à+6Ô˜KÑ(Ü" ;°Õ<r!   c                 óH   — ddl m}  t        | «      }| j                  «        |S )z¯Return (and clear) human-readable notes about heals run in this process.

    ``hermes auth list`` / ``hermes auth status`` print them so the user sees the consolidation.
    r   )r^   )r7   r^   rH   Úclear)r^   Únotess     r   Úconsume_oauth_heal_noticesr‚   á   s#   € õ
 4ÜÐ$Ó%€EØ×ÑÔØ€Lr!   c                óŒ  — ddl m} t        | t        «      sy| j	                  d«      | j	                  d«      fD ]ˆ  }t        |«      }|sŒ|j	                  d«      }t        |t        «      r|j	                  d«      nd}||j	                  d«      |j	                  d	«      fD ]  } ||«      sŒ|j                  «       c c S  ŒŠ y)
u  Stable account identity for an OAuth row when the token carries one.

    Codex / xAI access tokens are JWTs with ``sub`` / ``email`` / ``chatgpt_account_id`` claims;
    Anthropic ``sk-ant-oat`` tokens carry none (None â†’ lineage rests on id / token material).
    r   ©Ú_nonempty_strNr   Úid_tokenzhttps://api.openai.com/authÚchatgpt_account_idÚsubÚemail)r7   r…   r   r   r   r
   r   )r   r…   ÚtokenÚclaimsÚnestedÚaccountry   s          r   Ú_oauth_identityrŽ   ì   sµ   € õ .Ü�eœTÔ"ØØ—)‘)˜NÓ+¨U¯Y©Y°zÓ-BÐCò %ˆÜ# EÓ*ˆÙØØ—‘Ð9Ó:ˆÜ6@ÀÌÔ6N�&—*‘*Ð1Ô2ÐTXˆØ˜vŸz™z¨%Ó0°&·*±*¸WÓ2EÐFò 	%ˆEÙ˜UÕ#Ø—{‘{“}Ô$ñ	%ð%ð r!   c                ó  — ddl m} dD �cg c]  }| j                  |«      ‘Œ }}t        d„ t	        ||«      D «       d¬«      }|dk(  r5t        | j                  d«      «      j                  d«      } ||«      xs d}|S c c}w )	zûBest-effort 'how recently was this pair issued' score (epoch seconds).

    A rotation always issues a later-expiring access token, so ``expires_at`` ordering identifies
    the live copy; ``last_refresh`` and the JWT ``exp`` claim are fallbacks.
    r   )Ú_parse_absolute_timestamp)r[   rZ   r\   c              3  ó&   K  — | ]	  }|sŒ|–— Œ y ­w©N© )Ú.0Útss     r   ú	<genexpr>z#_oauth_freshness.<locals>.<genexpr>	  s   è ø€ ÒJ�rÂr”ÑJùs   ‚Šg        )Údefaultr   Úexp)Úagent.credential_poolr�   r   ÚmaxÚmapr
   )r   r�   ÚkÚstampsÚbestr˜   s         r   Ú_oauth_freshnessrŸ     s}   € õ @Ø$SÖT˜qˆe�i‰i˜�lÐT€FÐTÜÑJœSÐ!:¸FÓCÔJÐTWÔX€DØˆs‚{Ü  §¡¨>Ó!:Ó;×?Ñ?ÀÓFˆÙ(¨Ó-Ò4°ˆØ€Kùò Us   ‹A?c                ój  — ddl m} t        |«      D ��cg c]  \  }}t        |«      sŒ|‘Œ }}}|sy| j	                  d«      }|D ]   }|sŒ||   j	                  d«      |k(  sŒ|c S  dD ]@  }| j	                  |«      } ||«      sŒ|D ]  }||   j	                  |«      |k(  sŒ|c c S  ŒB yc c}}w )zòIndex of the root OAuth row that shares a grant lineage with *profile_row*.

    Only a copied row ID or shared token material establishes lineage. The same
    account/client can issue multiple independent grants; identity is not proof.
    r   r„   NÚid)r   r   )r7   r…   Ú	enumerater    r   )	Úprofile_rowÚ	root_rowsr…   ÚiÚrÚ
candidatesÚpidrx   Úp_vals	            r   Ú_find_root_counterpartrª     sÌ   € õ .Ü )¨)Ó 4×R™˜˜1Ô8NÈqÕ8Q’!ÐR€JÑRÙØØ
�/‰/˜$Ó
€CØò ˆÚ�9˜Q‘<×#Ñ# DÓ)¨SÓ0ØŠHðð 1ò ˆØ—‘ Ó$ˆÙ˜UÔ#ØØò 	ˆAØ˜‰|×Ñ Ó$¨Ó-Ø”ñ	ð	ð ùó Ss
   •B/©B/c                óÞ   — ddl m} t        | «      }t        D ].  }|j	                  |«      �	||   ||<   Œ|j                  |d«       Œ0 |j                  t        j                  |«      «       |S )zGReturn *target* carrying *winner*'s token pair, status markers cleared.r   )Ú_POOL_STATUS_FIELDSN)r7   r¬   r   Ú_OAUTH_TOKEN_FIELDSr   ÚpopÚupdateÚfromkeys)rO   Úwinnerr¬   Úmergedrx   s        r   Ú_adopt_oauth_materialr³   )  sc   € å3Ü�&‹\€FÜ"ò "ˆØ�:‰:�c‹?Ð&Ø  ™+ˆF�3ŠKà�J‰J�s˜DÕ!ð	"ð
 ‡M�M”$—-‘-Ð 3Ó4Ô5Ø€Mr!   c                óX  — 	 t        j                  | j                  d¬«      «      }t        |t        «      r,t        |j                  d«      xs d«      j                  «       sy|j                  d«      |j                  d«      |j                  d«      dœS # t        t        f$ r Y yw xY w)	zDRead a ``.anthropic_oauth.json`` as a pool-row-shaped dict, or None.rl   r2   NÚaccessTokenr   ÚrefreshTokenÚ	expiresAt)r   r   r[   )
rD   rE   rF   r=   Ú
ValueErrorr   r   r   r   r   )rm   rn   s     r   Ú_singleton_as_rowr¹   6  s”   € ðÜ�z‰z˜$Ÿ.™.°'˜.Ó:Ó;ˆô �dœDÔ!¬¨T¯X©X°mÓ-DÒ-JÈÓ)K×)QÑ)QÔ)SØàŸ™ Ó/ØŸ™ .Ó1ØŸ™ +Ó.ñ0ð 0øô	 ”ZÐ ò Ùðús   ‚%B ÂB)Â(B)c                óz   — | t         vry	 t        | «      S # t        $ r t        j	                  d| d¬«       Y yw xY w)a¼  One-time heal for installs that ALREADY forked a single-use grant into a profile.

    Forked copies are one credential with several owners: whichever profile rotated last holds the
    only live refresh token and every other copy (root included) is spent. Runs at profile
    ``load_pool()`` time for ``SINGLE_USE_REFRESH_POOL_PROVIDERS``: finds profile rows sharing
    LINEAGE with a root row (same pool id or shared token material), keeps the
    freshest rotation, writes it into ROOT when root's is older, and strips the profile's copy so
    the profile borrows root from then on. Idempotent; never touches API-key rows; never deletes a
    row with no root counterpart (an independent ``hermes -p <p> auth add`` grant, or the only
    surviving copy); reads only the two auth.json files the root fallback already reads. Returns
    ``{"adopted", "stripped_ids", "files", "providers_block"}`` when something healed, else None.
    Never raises.
    Nz%s: forked-OAuth heal skippedTr.   )rI   Ú$_heal_forked_single_use_oauth_grantsrC   r>   r?   )rT   s    r   Ú#heal_forked_single_use_oauth_grantsr¼   D  sE   € ð Ô;Ñ;ØðÜ3°KÓ@Ð@øÜò Ü�‰Ð4°kÈDˆÔQÙðús   ‹
 –!:¹:c                óÌ  ‡
‡— | j                  d«      |j                  d«      }}t        |t        «      rt        |t        «      sy|j                  |«      |j                  |«      }}t        |t        «      r|rt        |t        «      r|sydd„} ||«       ||«      cŠ
Š|st        ˆ
ˆfd„dD «       «      syt	        ‰
«      t	        ‰«      kD  }	|	rt        |«      ||<   ||= |	S )a^  Consolidate a forked ``providers.<id>`` device-code block into root.

    *lineage_proven* carries the pool-row verdict: a profile row that matched root by copied id
    or shared tokens proves the fork even after both sides rotated past token equality. Root's
    ``load_pool()`` re-seeds its ``device_code`` row FROM this block, so leaving root's block on
    the spent pair would undo the pool-row heal on the next root load.

    Returns None when nothing matched, False when the profile copy was dropped (root already
    newest), True when the profile copy was fresher and was adopted into root.
    r,   Nc                ó”   — t        | j                  d«      t        «      r| j                  d«      ni }i |¥d| j                  d«      i¥S )NÚtokensr\   )r   r   r   )rX   r¿   s     r   Ú_flatz*_heal_forked_provider_block.<locals>._flato  s@   € Ü(2°5·9±9¸XÓ3FÌÔ(M�—‘˜8Ô$ÐSUˆØD�&ÐD˜.¨%¯)©)°NÓ*CÑDÐDr!   c              3  óŠ   •K  — | ]:  }‰j                  |«      xr# ‰j                  |«      ‰j                  |«      k(  –— Œ< y ­wr’   )r   )r”   rx   Úp_flatÚr_flats     €€r   r–   z._heal_forked_provider_block.<locals>.<genexpr>w  s>   øè ø€ ò &àð 	�
‰
�3‹Ò>˜FŸJ™J s›O¨v¯z©z¸#«Ñ>Ó>ñ&ùs   ƒA A)r   r   )rX   úDict[str, Any]ÚreturnrÄ   )r   r   r   ÚanyrŸ   )Úprofile_storeÚ
root_storerT   Úlineage_provenÚp_providersÚr_providersÚp_blockÚr_blockrÀ   ÚadoptedrÂ   rÃ   s             @@r   Ú_heal_forked_provider_blockrÏ   [  sÚ   ù€ ð  -×0Ñ0°Ó=¸z¿~¹~ÈkÓ?Z�€KÜ�{¤DÔ)¬j¸ÄdÔ.KØØ"—‘ {Ó3°[·_±_À[Ó5QˆW€GÜ�w¤Ô%©'´jÀÌ$Ô6OÑT[ØóEñ ˜7“^¡U¨7£^€N€FˆFñ ¤#ô &à4ô&ô #ð Ü˜vÓ&Ô)9¸&Ó)AÑA€GÙÜ#'¨£=ˆ�KÑ Ø�KÐ Ø€Nr!   c                óh   — 	 | �| j                  «       nd}|�t        |«      S dS # t        $ r Y yw xY w)u    File signature from ONE stat, or None when absent â€” a torn pair from two stats could
    leave a persisted clean mark matching a store rewritten between them.N)Ústatr=   r   )ÚpÚsts     r   Ú	_stat_sigrÔ   ƒ  sB   € ðØ˜ˆQ�V‰VŒX¨Dˆð "$ Œ>˜"ÓÐ9°TÐ9øô ò Ùðús   ‚% ¥	1°1c                ó˜   — | j                  d«      }t        |t        «      r|j                  |«      nd}|t        |t        «      r|fS g fS )uY   ``(store["credential_pool"], its provider_id rows-or-[])`` â€” pool may be None/non-dict.r4   N)r   r   r   rH   )rR   rT   r+   Úrowss       r   Ú
_pool_rowsr×   �  sF   € à�9‰9Ð&Ó'€DÜ$.¨t´TÔ$:ˆ4�8‰8�KÔ À€DØœ D¬$Ô/�Ð7Ð7°RÐ7Ð7r!   c                óP   — t        |«      t        | «      kD  }|rt        | |«      S dS )zQ*target* carrying *candidate*'s pair when the candidate rotated later, else None.N)rŸ   r³   )rO   Ú	candidateÚfreshers      r   Ú_adopt_if_fresherrÛ   ”  s.   € ô ˜yÓ)Ô,<¸VÓ,DÑD€GÙ7>Ô  ¨Ó3ÐHÀDÐHr!   c                  ób   — e Zd ZdZdd„Zdd„Zdd„Zdd„Zdd„Zdd„Z	dd„Z
edd	„«       Zdd
„Zy)Ú	_HealPasszGOne heal pass over a profile store vs the root store for *provider_id*.c                ó˜  — ||c| _         | _        || _        dg g ddœ| _        dx| _        | _        d| _        t        ||«      \  | _        | _	        t        ||«      \  | _
        | _        | j                  D �cg c]  }t        |«      sŒ|‘Œ c}| _        || _        |�!|j                  «       rt!        |«      | _        y d | _        y c c}w )NF)rÎ   Ústripped_idsr-   Úproviders_block)rÇ   rÈ   rT   ÚsummaryÚprofile_changedÚroot_changedrÉ   r×   Úp_poolÚp_rowsÚr_poolÚr_rowsr    Úr_oauthÚroot_singletonrA   r¹   Úroot_singleton_row)ÚselfrÇ   rÈ   rT   ré   r¦   s         r   Ú__init__z_HealPass.__init__ž  sÍ   € Ø.;¸ZÐ+ˆÔ˜DœOØ&ˆÔà¨b¸2ÐRWñ(YˆŒà38Ð8ˆÔ˜tÔ0Ø#ˆÔÜ#-¨m¸[Ó#IÑ ˆŒ�T”[Ü#-¨j¸+Ó#FÑ ˆŒ�T”[Ø#'§;¡;ÖL˜aÔ2HÈÕ2KšÒLˆŒØ,ˆÔð Ð)¨n×.CÑ.CÔ.Eô ˜nÓ-ð 	ÕàKOð 	Õùò Ms   Á8CÂ	Cc                ó†   — t        | j                  |   |«      }|�&|| j                  |<   dx| _        | j                  d<   y y ©NTrÎ   )rÛ   rç   rã   rá   )rë   Úidxr&   r²   s       r   Ú_adopt_root_rowz_HealPass._adopt_root_row­  sF   € Ü" 4§;¡;¨sÑ#3°SÓ9ˆØÐØ%ˆD�K‰K˜ÑØ:>Ð>ˆDÔ §¡¨YÒ 7ð r!   c                ób   — t        | j                  |«      }|�|| _        d| j                  d<   y y rî   )rÛ   rê   rá   )rë   r&   r²   s      r   Ú_adopt_root_singletonz_HealPass._adopt_root_singleton³  s5   € Ü" 4×#:Ñ#:¸CÓ@ˆØÐØ&,ˆDÔ#Ø&*ˆD�L‰L˜Ò#ð r!   c                ó’  — g }| j                   D ]Ì  }t        |«      s|j                  |«       Œ t        || j                  «      }|�d| _        | j                  ||«       nGt        |«      r*| j                  �| j                  s| j                  |«       n|j                  |«       Œ™| j                  d   j                  |j                  d«      «       d| _        ŒÎ | j                  r^t        | j                  t         «      rC|r|| j                  | j"                  <   y | j                  j%                  | j"                  d «       y y y )NTrß   r¡   )rå   r    r<   rª   rç   rÉ   rð   r'   rê   rè   rò   rá   r   râ   r   rä   r   rT   r®   )rë   Ú	kept_rowsr&   Ú	match_idxs       r   Úheal_pool_rowsz_HealPass.heal_pool_rows¹  s  € Ø!ˆ	Ø—;‘;ò 	(ˆCÜ)¨#Ô.Ø× Ñ  Ô%ØÜ.¨s°D·K±KÓ@ˆIØÐ$Ø&*�Ô#Ø×$Ñ$ Y°Õ4ô ˜cÔ" t×'>Ñ'>Ð'JÐSW×S_ÒS_Ø×*Ñ*¨3Õ/ð × Ñ  Ô%ØØ�L‰L˜Ñ(×/Ñ/°·±¸³Ô>Ø#'ˆDÕ ð%	(ð& ×Ò¤J¨t¯{©{¼DÔ$AÙØ09�—‘˜D×,Ñ,Ò-à—‘—‘ × 0Ñ 0°$Õ7ð	 %BÐr!   c                óú   — | j                   t        vry t        | j                  | j                  | j                   | j
                  «      }|�0dx| _        | j                  d<   |rdx| _        | j                  d<   y y y )NTrà   rÎ   )	rT   rK   rÏ   rÇ   rÈ   rÉ   râ   rá   rã   )rë   Úblock_results     r   Úheal_provider_blockz_HealPass.heal_provider_blockÔ  s~   € Ø×ÑÔ#?Ñ?ØÜ2Ø×Ñ §¡°×1AÑ1AÀ4×CVÑCVóXˆàÐ#ØEIÐIˆDÔ  4§<¡<Ð0AÑ#BÙØ>BÐB�Ô! D§L¡L°Ò$;ð ð $r!   c                óP  — |�|j                  «       sy ddlm} | j                  � ||| j                  «      ry t	        |«      }t        | j                  «      xs | j                  d u}|�|sy | j                  �| j                  |«       n:t        d„ t        | j                  «      D «       d «      }|�| j                  ||«       	 |j                  «        | j                  d   j                  |j                   «       y # t"        $ r t$        j'                  d|d¬«       Y y w xY w)Nr   )Ú_is_same_auth_storec              3  óR   K  — | ]  \  }}t        |«      rt        |«      r|–— Œ! y ­wr’   )r    r'   ©r”   r¥   r¦   s      r   r–   z3_HealPass.heal_profile_singleton.<locals>.<genexpr>ñ  s+   è ø€ ò C‘t�q˜!Ü*¨1Ô-´,¸q´/ô ñ Cùs   ‚%'r-   zcould not remove %sTr.   )rA   r7   rû   ré   r¹   r   rè   rê   rò   Únextr¢   rç   rð   r;   rá   r<   rN   r=   r>   r?   )rë   Úprofile_singletonrû   Úp_singleÚroot_has_grantrï   s         r   Úheal_profile_singletonz _HealPass.heal_profile_singletonÞ  s  € ØÐ$Ð,=×,DÑ,DÔ,FØÝ7Ø×ÑÐ*Ñ/BÐCTÐVZ×ViÑViÔ/jØä$Ð%6Ó7ˆÜ˜dŸl™lÓ+ÒR¨t×/FÑ/FÈdÐ/Rˆð Ð¡>ØØ×"Ñ"Ð.Ø×&Ñ& xÕ0ô ñCœy¨¯©Ó5ô CØDHóJˆCð ˆØ×$Ñ$ S¨(Ô3ð	RØ×$Ñ$Ô&Ø�L‰L˜Ñ!×(Ñ(Ð):×)?Ñ)?Õ@øÜò 	RÜ�L‰LÐ.Ð0AÈDˆLÖQð	Rús   Ã8D Ä!D%Ä$D%c                ó¦  — | j                   d   r| j                  �| j                  €yt        d„ t	        | j
                  «      D «       d«      }|€y| j
                  |   }t        |«      }t        | j                  «      }||kD  rt        | j                  |«      | _        y||kD  r+t        || j                  «      | j
                  |<   d| _        yy)a
  Keep root's singleton and its ``hermes_pkce``-seeded pool row in step.

        Root's next load_pool() re-seeds that row FROM the singleton file, so a stale file would
        resurrect the spent pair (and a stale row would be overwritten by a fresh file).
        rÎ   Nc              3  ód   K  — | ](  \  }}t        |«      r|j                  d «      dk(  r|–— Œ* y­w)r#   r$   N)r    r   rý   s      r   r–   z>_HealPass.sync_root_singleton_with_pkce_row.<locals>.<genexpr>  s4   è ø€ ò P‘4�1�aÜ& qÔ)¨a¯e©e°H«oÀÒ.Nô ñ Pùs   ‚.0T)	rá   ré   rê   rþ   r¢   rç   rŸ   r³   rã   )rë   Úpkce_idxÚpkce_rowÚ	row_freshÚsingle_freshs        r   Ú!sync_root_singleton_with_pkce_rowz+_HealPass.sync_root_singleton_with_pkce_rowû  sÐ   € ð �L‰L˜Ò#¨×(;Ñ(;Ð(GØ×'Ñ'Ð3ØÜñPœ9 T§[¡[Ó1ô PØQUóWˆð ÐØØ—;‘;˜xÑ(ˆÜ$ XÓ.ˆ	Ü'¨×(?Ñ(?Ó@ˆØ�|Ò#Ü&;¸D×<SÑ<SÐU]Ó&^ˆDÕ#Ø˜IÒ%Ü$9¸(ÀD×D[ÑD[Ó$\ˆD�K‰K˜Ñ!Ø $ˆDÕð &r!   c                ój   — t        | j                  xs | j                  xs | j                  d   «      S )NrÎ   )r   râ   rã   rá   )rë   s    r   Údirtyz_HealPass.dirty  s,   € ä�D×(Ñ(ÒX¨D×,=Ñ,=ÒXÀÇÁÈiÑAXÓYÐYr!   c           	     ó,  — | j                   }d|d   › �|d   fd| j                  › d�|d   fdj                  |d   «      |d   ffD ��cg c]	  \  }}|r|‘Œ }}}|d   rd	nd
}d|› d| j                  › ddj                  |«      xs d› d|› d�	S c c}}w )Nz
pool rows rß   z
providers.z blockrà   z, r-   rÎ   z,profile copy was the live pair; root updatedz.root copy already newest; profile copy droppedzprofile z: consolidated forked z OAuth grant (z; zno-opu   ) into the root grant â€” z3; this profile now borrows the root grant (#100339))rá   rT   Újoin)rë   Úprofile_namerá   ÚbitÚpresentÚlog_bitsÚverdicts          r   Únoticez_HealPass.notice  sò   € Ø—,‘,ˆà˜' .Ñ1Ð2Ð3°W¸^Ñ5LÐMØ˜$×*Ñ*Ð+¨6Ð2°GÐ<MÑ4NÐOØ�Y‰Y�w˜wÑ'Ó(¨'°'Ñ*:Ð;ð-=÷ I™L˜C ñ AHò ð Iˆñ Ið �yÒ!ñ ;Ø'Wð 	ð �|�nÐ$:¸4×;KÑ;KÐ:Lð MØ—	‘	˜(Ó#Ò. wÐ/Ð/IÈ'Èð S@ðAð	BùóIs   Á
BN)ré   úOptional[Path])rï   Úintr&   rÄ   rÅ   ÚNone)r&   rÄ   rÅ   r  )rÅ   r  )rÿ   r  rÅ   r  )rÅ   r   )r  r   rÅ   r   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__rì   rð   rò   rö   rù   r  r	  Úpropertyr  r  r“   r!   r   rÝ   rÝ   ›  sG   „ ÙQóQó?ó+ó8ó6CóRó:%ð0 òZó ðZôBr!   rÝ   c           	     óì  — ddl m}m}m}m}m}m}m}m}m	}	  |«       }
|
€y t        j                  j                  d«      r;t        j                  j                  dd«      }|r ||
t        |«      dz  dz  «      ry  |«       }|j                  }| dk(  }|r|d	z  nd }|r|
j                  d	z  nd }t        |«      t!        |«      t!        |«      t        |
«      t!        |
«      t!        |«      f}|j                  | «      |k(  ry t#        | «      t%        |«      k(  r||| <   y |d
   €|d   €t'        | |«       y  |||
«      r$t'        | |«       t(        j+                  d| |«       y  |«       5  |j-                  «       r ||«      ndi i} ||
¬«      5  |
j-                  «       r ||
«      ndi i}t/        ||| |«      }|j1                  «        |j3                  «        |j5                  |«       |j6                  st'        | |«       	 d d d «       d d d «       y |j9                  «        |j:                  }|j<                  rOt?        |j@                  tB        «      r|jD                  |j@                  | <   n| |jD                  i|d<    |	||
¬«       |jF                  }|d   rG|�E|�Cddl$m%}  ||j                  d«      xs d|j                  d«      |j                  d«      |¬«       |jL                  r|j-                  «       r
 |	||¬«       d d d «       d d d «       jO                  |jP                  «      }t(        jS                  |«       |jU                  |«       S # 1 sw Y   ŒTxY w# 1 sw Y   ŒXxY w)Nr   )	rf   Ú_auth_store_lockÚ_global_auth_file_pathÚ_load_auth_storerû   r_   r^   r)   r*   ÚPYTEST_CURRENT_TESTÚHOMEr   z.hermesr0   r   r   é   é   z3%s: forked-OAuth heal skipped, %s is the root storer,   r5   r4   rÎ   )Ú_write_hermes_oauth_credentialsr   r   r[   )rO   )+r7   rf   r  r  r  rû   r_   r^   r)   r*   ÚosÚenvironr   r   rh   r   rÔ   rp   rc   r~   r>   r?   rA   rÝ   rö   rù   r  r  r	  rá   rã   r   ræ   r   rç   rê   Úagent.anthropic_credentialsr$  râ   r  rN   Úinfor<   )rT   rf   r  r  r  rû   r_   r^   r)   r*   Ú	root_pathÚreal_home_envÚprofile_pathÚprofile_homeÚis_anthropicrÿ   ré   rb   rÇ   rÈ   Úrunrá   Úsingleton_rowr$  Úmessages                            r   r»   r»   &  sS  € ÷÷ õ ñ 'Ó(€IØÐØÜ	‡z�z‡~�~Ð+Ô,ô Ÿ
™
Ÿ™ v¨rÓ2ˆÙ™Z¨	´4¸Ó3FÈÑ3RÐU`Ñ3`ÔaØÙ"Ó$€LØ×&Ñ&€LØ +Ñ-€LÙBN˜Ð'>Ò>ÐTXÐÙCO�Y×%Ñ%Ð(?Ò?ÐUY€Nô 	ˆLÓœ9 \Ó2´IÐ>OÓ4PÜˆI‹œ	 )Ó,¬i¸Ó.Gð€Kð ×"Ñ" ;Ó/°;Ò>Øô )¨Ó5¼À[Ó9QÒQØ/:Ð Ñ,ØØ�1�~Ð +¨a¡.Ð"8Ü˜{¨KÔ8ØÙ˜<¨Ô3ô
 	˜{¨KÔ8ä�‰ÐJÈKÐYeÔfØñ 
Ó	ñ Jà.:×.AÑ.AÔ.CÑ˜\Ô*È+ÐWYÐIZð 	á¨)Ô4ñ 	JØ8A×8HÑ8HÔ8JÑ)¨)Ô4ÐQ\Ð^`ÐPaˆJÜ˜M¨:°{ÀNÓSˆCØ×ÑÔ Ø×#Ñ#Ô%Ø×&Ñ&Ð'8Ô9Ø—9’9Ü& {°KÔ@Ø÷	J÷Jð Jð ×1Ñ1Ô3Ø—k‘kˆGØ×ÒÜ˜cŸj™j¬$Ô/Ø.1¯j©j�C—J‘J˜{Ò+à5@À#Ç*Á*Ð4M�JÐ0Ñ1Ù  ¸ÕCØ×2Ñ2ˆMØ�yÒ! nÐ&@À]ÐE^ÝWÙ/Ø!×%Ñ% nÓ5Ò;¸¸]×=NÑ=NÈÓ=_Ø!×%Ñ% oÓ6¸~õOð ×"Ò" |×':Ñ':Ô'<Ù  ¸LÕI÷1	J÷Jð8 �j‰j˜×*Ñ*Ó+€GÜ
‡K�K�ÔØ×Ñ˜wÔ'Ø€N÷9	Jð 	Jú÷Jð Jús2   Å*&M*ÆA5MÈM*ÈC5MÌM*ÍM'	Í#M*Í*M3)r   r   rÅ   r   )r&   rÄ   rÅ   r   )rL   r   rÅ   rÄ   )rb   ÚtuplerÅ   rH   )rÅ   r  )rT   r   rÅ   zOptional[list])rT   r   rb   r1  rÅ   r  )rÅ   r]   )r   rÄ   rÅ   zOptional[str])r   rÄ   rÅ   Úfloat)r£   rÄ   r¤   zList[Dict[str, Any]]rÅ   zOptional[int])rO   rÄ   r±   rÄ   rÅ   rÄ   )rm   r   rÅ   úOptional[Dict[str, Any]])rT   r   rÅ   r3  )F)
rÇ   rÄ   rÈ   rÄ   rT   r   rÉ   r   rÅ   zOptional[bool])rÒ   r  rÅ   z#Optional[Tuple[int, int, int, int]])rR   rÄ   rT   r   rÅ   zTuple[Any, List[Any]])rO   rÄ   rÙ   rÄ   rÅ   r3  )2r  Ú
__future__r   ÚloggingrD   r%  Úpathlibr   Útypingr   r   r   r   r	   Úhermes_cli.auth_constantsr
   ru   r   Ú	getLoggerr>   Ú	frozensetrI   r8   rK   r    r'   rY   r­   r^   Ú__annotations__r_   ri   rc   rj   rp   r|   r~   r‚   rŽ   rŸ   rª   r³   r¹   r¼   rÏ   rÔ   r×   rÛ   rÝ   r»   r“   r!   r   ú<module>r<     sb  ðòõ #ã Û Û 	Ý ß 3Õ 3Ý 8Ý  ð 
ˆ×	Ñ	Ð,Ó	-€ñ %.Ò.XÓ$YÐ !ð $>Ð  ð  =Ð óJó@óLð^TÐ ð "$Ð �YÓ #ð 79Ð Ð3Ó 8ð #:Ð ó/óó8ó( ]óF=óóó*ðØðØ,@ðØERóó2
ó0óð2 !ð%Ø!ð%Ø/=ð%ØLOð%àð%à%3ó%óP:ó8ðIØðIØ'5ðIØ:RóI÷HBñ HBôVYr!   