+
    kHàjy  ã                  óÎ  € R t ^ RIHt ^ RIt^ RIt^ RIt^ RIt^ RIt^ RIt^ RI	t	^ RI
Ht ^ RIHt ^ RIHt ^ RIHtHtHtHtHtHt  ^ RIt^ RIHt ^ RIHt ^ RIt]P>                  ! ] 4      t! ! R	 R
]"4      t#Rt$Rt%Rt&Rt'Rt(^t)Rt*]PV                  ! R4      t,R R lt-R R lt.R R lt/R R lt0R R lt1R R lt2R R lt3R  R! lt4R" R# lt5R$ R% lt6R& R' lt7R(RR)R/R* R+ llt8R,RR-RR.RR(R/R/ R0 llt9R1 R2 lt:R3 R4 lt;R5 R6 lt<R7 R8 lt=R9 R: lt>RœR< R= llt?R>R?/R@ RA llt@R>R;/RB RC lltARD RE ltBRF RG ltCRH RI ltD] ! RJ RK4      4       tE]! RLRM7       ! RN RO4      4       tFRP]$RQ]%/RR RS lltGRP]$RTRRURRVR/RW RX lltHRYR/RZ R[ lltIR\ R] ltJR^ R_ ltKR` Ra ltLRb Rc ltMRP]$RdRLReR/Rf Rg lltNRh Ri ltORj Rk ltPRl Rm ltQRn Ro ltRRp Rq ltSR)](RrRs/Rt Ru lltTRv Rw ltURx Ry ltVRz R{ ltWR| R} ltXR~RRRR€RR�R‚/Rƒ R„ lltYR~RRRR€R/R… R† lltZR‡ Rˆ lt[R‰ RŠ lt\R‹ RŒ lt]R� RŽ lt^R� R� lt_R‘ R’ lt`R“R”/R• R– lltaR—RL/R˜ R™ lltb]c3Rš R› lltdR#   ] d    Rt ELi ; i)�aM  Photon Dashboard API client + device-code login flow (pure Python, no spectrum-ts).

Management calls hit ``https://app.photon.codes/api/...`` (OAuth 2.0 device flow, Bearer)
like the official CLI. The dashboard project ``id`` *is* the Spectrum Cloud project id and
Spectrum is always provisioned at create-time; the sidecar authenticates with
``(id, projectSecret)``. Storage: runtime SDK creds -> ``~/.hermes/.env``; management
metadata -> ``auth.json`` under ``credential_pool.photon`` (device token), ``photon_project``
(ids + secret for offline status) and ``photon_user`` (numbers).
)ÚannotationsN)Ú	b64encode)Ú	dataclass)ÚPath)ÚAnyÚCallableÚDictÚListÚOptionalÚTuple)Úget_scoped_secret©Úget_hermes_homec                  ó   € ] tR t^$tRtRtR# )ÚPhotonDashboardAuthErrorzERaised when Photon rejects a device-flow token for the dashboard API.© N)Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__Ú__static_attributes__r   ó    ÚB/home/ubuntu/.hermes/hermes-agent/plugins/platforms/photon/auth.pyr   r   $   s   † ÝOr   r   z
photon-clizopenid profile emailzhttps://app.photon.codeszhttps://spectrum.photon.codeszHermes Agenti  z^\+[1-9]\d{6,14}$c               ó   € V ^8„  d   QhRR/# )é   Úreturnr   r   )Úformats   "r   Ú__annotate__r   6   s   € ÷ +ñ +˜ñ +r   c                 ó$   € \        4       R,          # )zJThe active profile's ``auth.json`` (shared with the rest of hermes-agent).z	auth.jsonr   r   r   r   Ú_auth_json_pathr    6   s   € äÓ˜{Õ*Ð*r   c               ó   € V ^8„  d   QhRR/# )r   r   údict[str, Any]r   )r   s   "r   r   r   ;   s   € ÷ 	ñ 	�Nñ 	r   c                 ó|  € \        4       p V P                  4       '       g   / #  V P                  R RR7      ;_uu_ 4       p\        P                  ! V4      ;'       g    / uuRRR4       #   + '       g   i     R# ; i  \
        \        P                  3 d#   p\        P                  RY4       / u Rp?# Rp?ii ; i)Úrz	utf-8-sig©ÚencodingNzphoton: could not read %s: %s)	r    ÚexistsÚopenÚjsonÚloadÚOSErrorÚJSONDecodeErrorÚloggerÚwarning)ÚpathÚfhÚes      r   Ú
_load_authr2   ;   s„   € ÜÓ€DØ�;‰;�=Š=Øˆ	ðØ�Y‰Y�s [ˆY×1Ô1°RÜ—9’9˜R“=×&Ð& B÷ 2×1×1Ó1ûä”T×)Ñ)Ð*ô Ü�‰Ð6¸Ô@Ø�	ûðús:   ¤A> ¿ A*Á
A> Á*A;	Á5A> Á;A> Á>B;ÂB6Â0B;Â6B;c               ó    € V ^8„  d   QhRRRR/# )r   Údatar"   r   ÚNoner   )r   s   "r   r   r   G   s   € ÷ ñ �^ð ¨ñ r   c           	     óR  € \        4       pVP                  P                  R R R7       VP                  VP                   R\
        P                  ! 4        R\        P                  ! 4       P                   24      p\
        P                  ! \        V4      \
        P                  \
        P                  ,          \
        P                  ,          \        P                   \        P"                  ,          4      p \
        P$                  ! VRRR7      p T;_uu_ 4        \2        P4                  ! Y^R R7       TP7                  4        \
        P8                  ! TP;                  4       4       RRR4       TP=                  T4       R#   \&         d¦    \(        P*                  ! \,        4      ;_uu_ 4        \
        P.                  ! T4       RRR4       M  + '       g   i     M; i\(        P*                  ! \,        4      ;_uu_ 4        TP1                  4        RRR4       h   + '       g   i     h ; ii ; i  + '       g   i     LÖ; i  \&         dQ    \(        P*                  ! \,        4      ;_uu_ 4        TP1                  4        RRR4       h   + '       g   i     h ; ii ; i)	T)ÚparentsÚexist_okz.tmp.Ú.Úwzutf-8r%   N)ÚindentÚ	sort_keys)r    ÚparentÚmkdirÚ	with_nameÚnameÚosÚgetpidÚuuidÚuuid4Úhexr(   ÚstrÚO_WRONLYÚO_CREATÚO_EXCLÚstatÚS_IRUSRÚS_IWUSRÚfdopenÚBaseExceptionÚ
contextlibÚsuppressr+   ÚcloseÚunlinkr)   ÚdumpÚflushÚfsyncÚfilenoÚreplace)r4   r/   ÚtmpÚfdr0   s   &    r   Ú
_save_authrZ   G   s¤  € ÜÓ€DØ‡K�K×Ñ˜d¨TÐÔ2ð �.‰.˜DŸI™I˜; e¬B¯IªI«K¨=¸¼$¿*º*»,×:JÑ:JÐ9KÐLÓ
M€CÜ	�Š”�S“œ2Ÿ;™;¬¯©Õ3´b·i±iÕ?ÄÇÁÔPT×P\ÑP\ÕA\Ó	]€BðÜ�YŠY�r˜3¨Ô1ˆð	ßŠRÜ�IŠI�d q°DÕ9Ø�H‰HŒJÜ�HŠH�R—Y‘Y“[Ô!÷ ð 	�‰�DÖøô ô Ü× Ò ¤×)Õ)Ü�HŠH�RŒL÷ *×)×)Ð)úä× Ò ¤×)Õ)Ø�J‰JŒL÷ *à÷ *Ö)àúðú÷ �Rûô
 ô Ü× Ò ¤×)Õ)Ø�J‰JŒL÷ *à÷ *Ö)àúðúsy   Ã8F Ä
I ÄAH8Å*I Æ-H5Æ2GÇ		H5ÇG"Ç*H5ÈH!È	H5È!H1È,	H5È8I	ÉI É-J&É8JÊ		J&ÊJ"Ê	J&c               ó$   € V ^8„  d   QhRRRRRR/# )r   Úauthr"   ÚkeyrF   r   r   r   )r   s   "r   r   r   b   s&   € ÷ @ñ @�nð @¨3ð @°3ñ @r   c                óª   € V P                  R/ 4      P                  V4      ;'       g    . p\        V\        4      '       d   V'       d
   V^ ,          # R# )z;First entry of ``credential_pool.<key>`` (a list), or None.Úcredential_poolN)ÚgetÚ
isinstanceÚlist)r\   r]   Úpools   && r   Ú_pool_firstrd   b   sC   € à�8‰8Ð% rÓ*×.Ñ.¨sÓ3×9Ð9°r€DÜ  ¤t×,Ò,·ˆ4��7Ð?¸4Ð?r   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r]   rF   Úrecordr"   r   r5   r   )r   s   "r   r   r   h   s!   € ÷ ñ ˜Cð ¨ð ¸Dñ r   c                óÀ   € ^ RI Hp V! 4       ;_uu_ 4        \        4       pV.VP                  R/ 4      V &   \	        V4       RRR4       R#   + '       g   i     R# ; i)zQReplace ``credential_pool.<key>`` with ``[record]`` under the cross-process lock.)Ú_auth_store_lockr_   N)Úhermes_cli.authrh   r2   Ú
setdefaultrZ   )r]   rf   rh   r\   s   &&  r   Ú_store_pool_recordrk   h   sD   € å0Ù	×	Õ	Ü‹|ˆØ7=°hˆ�‰Ð)¨2Ó.¨sÑ3Ü�4Ô÷ 
×	×	Ò	ús   –,AÁA	c               ó   € V ^8„  d   QhRR/# ©r   r   úOptional[str]r   )r   s   "r   r   r   q   s   € ÷ )ñ )˜=ñ )r   c                 ó6  € \        4       p \        V R4      ;'       g    / pV P                  R/ 4      P                  R/ 4      pVP                  R4      ;'       g+    VP                  R4      ;'       g    VP                  R4      pV'       d   \        V4      # R# )zFReturn the device-flow bearer token stored by ``login()`` or ``None``.ÚphotonÚ	providersÚaccess_tokenÚtokenN)r2   rd   r`   rF   )r\   ÚentryÚlegacyrs   s       r   Úload_photon_tokenrv   q   s{   € ä‹<€DÜ˜˜hÓ'×-Ð-¨2€EØ�X‰X�k 2Ó&×*Ñ*¨8°RÓ8€FØ�I‰I�nÓ%×YÐY¨¯©°7Ó);×YÐY¸v¿z¹zÈ.Ó?Y€EßŒ3ˆu‹:Ð( DÐ(r   c               ó    € V ^8„  d   QhRRRR/# )r   rs   rF   r   r5   r   )r   s   "r   r   r   z   s   € ÷ Yñ Y˜cð Y dñ Yr   c           
     ó^   € \        RRV R\        \        P                  ! 4       4      /4       R# )zBPersist a dashboard bearer token under ``credential_pool.photon``.rp   rr   Ú	issued_atN)rk   ÚintÚtime©rs   s   &r   Ústore_photon_tokenr}   z   s    € ä�x .°%¸ÄcÌ$Ï)Ê)Ë+ÓFVÐ!WÖXr   c               ó   € V ^8„  d   QhRR/# ©r   r   r5   r   )r   s   "r   r   r      s   € ÷ ñ ˜Dñ r   c                 ó  € \        4       p V P                  R/ 4      pVP                  R. 4      p\        V\        4      '       d   V'       d   . VR&   \	        V 4       V P                  R/ 4      pRV9   d   / VR&   \	        V 4       R# R# )zDRemove any stored Photon dashboard token (before re-authentication).r_   rp   rq   N)r2   r`   ra   rb   rZ   )r\   rc   rp   rq   s       r   Úclear_photon_tokenr�      sy   € ä‹<€DØ�8‰8Ð% rÓ*€DØ�X‰X�h Ó#€FÜ�&œ$×Ò§FØˆˆX‰Ü�4ÔØ—‘˜ bÓ)€IØ�9ÔØ ˆ	�(ÑÜ�4Öñ r   c               ó    € V ^8„  d   QhRRRR/# )r   rs   rF   r   Úboolr   )r   s   "r   r   r   �   s   € ÷ ñ  Cð ¨Dñ r   c                óp   € V '       g   R#  \        V 4       R#   \         d     R# \         d     R# i ; i)zžTrue if the dashboard API accepts the token; a definitive rejection is stale,
transient failures (network, 5xx) count as valid so they don't force a re-login.FT)Úvalidate_photon_tokenr   Ú	Exceptionr|   s   &r   Úcheck_photon_token_validr‡   �   s>   € ÷ ÙðÜ˜eÔ$ñ
 øô	 $ô ÚÜô ØÙðús   Œ ™5§5°5´5c               ó   € V ^8„  d   QhRR/# ©r   r   ú#tuple[Optional[str], Optional[str]]r   )r   s   "r   r   r   ›   s   € ÷ yñ yÐ"Eñ yr   c                 ó(  € \        R4      p \        R4      pV '       d   V'       d   W3# \        \        4       R4      pVf   W3# T ;'       g+    VP                  R4      ;'       g    VP                  R4      T;'       g    VP                  R4      3# )z�Runtime SDK creds ``(spectrum_project_id, project_secret)``: process env wins
(``.env`` is loaded at gateway startup), then ``auth.json`` for offline/status.ÚPHOTON_PROJECT_IDÚPHOTON_PROJECT_SECRETÚphoton_projectÚspectrum_project_idÚ
project_idÚproject_secret©Ú_get_scoped_secretrd   r2   r`   )Úenv_idÚenv_secrt   s      r   Úload_project_credentialsr–   ›   s‡   € ô  Ð 3Ó4€FÜ Ð!8Ó9€Gß—'ØˆÐÜœ
›Ð&6Ó7€EØ‚}ØˆÐà×PÐP�U—Y‘YÐ4Ó5×PÐP¸¿¹À<Ó9PÐRY×RxÐRxÐ]b×]fÑ]fÐgwÓ]xÐxÐxr   c               ó   € V ^8„  d   QhRR/# rm   r   )r   s   "r   r   r   ©   s   € ÷ lñ l =ñ lr   c                 óî   € \        R4      p V '       d   V # \        \        4       R4      ;'       g    / pVP                  R4      ;'       g+    VP                  R4      ;'       g    VP                  R4      # )u™   Project id for management API calls â€” prefers ``spectrum_project_id`` (on
pre-backfill installs the old ``dashboard_project_id`` is diverged and 404s).ÚPHOTON_DASHBOARD_PROJECT_IDrŽ   r�   Údashboard_project_idr�   r’   )r”   rt   s     r   Úload_dashboard_project_idr›   ©   se   € ô  Ð =Ó>€FßØˆÜœ
›Ð&6Ó7×=Ð=¸2€EØ�9‰9Ð*Ó+×kÐk¨u¯y©yÐ9OÓ/P×kÐkÐTY×T]ÑT]Ð^jÓTkÐkr   rš   r@   c          
     ó,   € V ^8„  d   QhRRRRRRRRRR/# )	r   r�   rF   r‘   rš   rn   r@   r   r5   r   )r   s   "r   r   r   ³   s4   € ÷ >ñ >Øð>Ø14ð>à'ð>à6Cð>àOSñ>r   c           	     óò   € RV RVR\        \        P                  ! 4       4      /pTP                  RV3RV33 UUu/ uF  w  rVV'       g   K  WVbK  	  upp4       \        RV4       \	        W4       R# u uppi )zWPersist project credentials to both .env (runtime) and auth.json (mgmt/offline status).r�   r‘   ry   rš   r@   rŽ   N)rz   r{   Úupdaterk   Ú_persist_runtime_env)r�   r‘   rš   r@   rf   ÚkÚvs   $$$$   r   Ústore_project_credentialsr¢   ³   s   € ð
 	Ð2Ð4DÀnÐVaÔcfÔgk×gpÒgpÓgrÓcsðu€Fà
‡M�MÐ&<Ð>RÐ%SÐV\Ð^bÐUcÑ$dÔjÑ$d™D˜AÔhi”4�1’4Ñ$dÒjÔkÜÐ'¨Ô0ÜÐ,Ö=ùó ks   »A3
ÁA3
Úphone_numberÚassigned_phone_numberÚuser_idc          
     ó,   € V ^8„  d   QhRRRRRRRRRR/# )r   r£   rn   r¤   r¥   rš   r   r5   r   )r   s   "r   r   r   ¾   s4   € ÷ 
.ñ 
.Ø"ð
.ØBOð
.àð
.à9Fð
.àRVñ
.r   c                ó  € V '       g   V'       g   R# R\        \        P                  ! 4       4      /pTP                  RV 3RV3RV3RV33 UUu/ uF  w  rVV'       g   K  WVbK  	  upp4       \        RV4       R# u uppi )zEPersist non-secret Photon user numbers for offline ``status`` output.Nry   r£   r¤   r¥   rš   Úphoton_user)rz   r{   rž   rk   )r£   r¤   r¥   rš   rf   r    r¡   s   $$$$   r   Ústore_user_numbersr©   ¾   s’   € ÷ × 5ÙØ)¬3¬t¯yªy«{Ó+;Ð<€FØ
‡M�MØ	˜Ð&Ð)@ÐBWÐ(XØ	�GÐÐ5Ð7KÐLñ%Nô Tñ %N™D˜AäQRô �1’4ñ %Nò Tô Uô �} fÖ-ùóTs   ÁA<
Á A<
c               ó$   € V ^8„  d   QhRRRRRR/# )r   r�   rF   r‘   r   r5   r   )r   s   "r   r   r   Ë   s&   € ÷ Oñ O¨cð OÀ3ð OÈ4ñ Or   c                óæ   €  ^ RI Hp  T! RT 4       T! RT4       R#   \         d    \        P	                  R4        R# i ; i  \
         d"   p\        P	                  RT4        Rp?R# Rp?ii ; i)uv   Write the SDK creds to ``~/.hermes/.env`` (secret never bound to a printable local
in a caller â€” CodeQL clean flow).)Úsave_env_valueu=   photon: hermes_cli.config unavailable â€” skipping .env writeNrŒ   r�   z1photon: could not write project creds to .env: %s)Úhermes_cli.configr¬   ÚImportErrorr-   r.   r†   )r�   r‘   r¬   r1   s   &&  r   rŸ   rŸ   Ë   sm   € ðÝ4ðOÙÐ*Ð,?Ô@ÙÐ.°Ö?øô ô Ü�‰ÐVÔWÚðûô ô OÜ�‰ÐJÈA×NÒNûðOús&   ‚ ‰A � AÁ AÁA0ÁA+Á+A0c               ó   € V ^8„  d   QhRR/# ©r   r   rF   r   )r   s   "r   r   r   Ü   s   € ÷ Vñ V˜ñ Vr   c                 óh   € \         P                  ! R 4      ;'       g    \        P                  R4      # )ÚPHOTON_DASHBOARD_HOSTÚ/)rA   ÚgetenvÚDEFAULT_DASHBOARD_HOSTÚrstripr   r   r   Ú_dashboard_hostr·   Ü   s'   € Ü�IŠIÐ-Ó.×HÐHÔ2H×PÑPÐQTÓUÐUr   c               ó   € V ^8„  d   QhRR/# r°   r   )r   s   "r   r   r   à   s   € ÷ Tñ T˜ñ Tr   c                 óh   € \         P                  ! R 4      ;'       g    \        P                  R4      # )ÚPHOTON_SPECTRUM_HOSTr³   )rA   r´   ÚDEFAULT_SPECTRUM_HOSTr¶   r   r   r   Ú_spectrum_hostr¼   à   s&   € Ü�IŠIÐ,Ó-×FÐFÔ1F×NÑNÈsÓSÐSr   c               ó    € V ^8„  d   QhRRRR/# )r   rs   rF   r   údict[str, str]r   )r   s   "r   r   r   ä   s   € ÷ 0ñ 0�3ð 0˜>ñ 0r   c                ó   € R RV  2/# )ÚAuthorizationzBearer r   r|   s   &r   Ú_bearerrÁ   ä   s   € Ø˜w u gÐ.Ð/Ð/r   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r�   rF   r‘   r   r¾   r   )r   s   "r   r   r   è   s!   € ÷ /ñ /�sð /¨Cð /°Nñ /r   c                ój   € \        V  R V 2P                  4       4      P                  R4      pRRV 2/# )Ú:ÚasciirÀ   zBasic )r   ÚencodeÚdecode)r�   r‘   rs   s   && r   Ú_basicrÈ   è   s>   € Ü˜˜ A nÐ%5Ð6×=Ñ=Ó?Ó@×GÑGÈÓP€EØ˜v e WÐ-Ð.Ð.r   Ú c               ó    € V ^8„  d   QhRRRR/# )r   ÚwhatrF   r   r5   r   )r   s   "r   r   r   í   s   € ÷ Bñ B˜ð B dñ Br   c                ó2   € \         f   \        RV  24      hR # )Nzhttpx is required for Photon)ÚhttpxÚRuntimeError©rË   s   &r   Ú_require_httpxrÐ   í   s   € Ü‚}ÜÐ9¸$¸Ð@ÓAÐAñ r   rË   ú device loginc               ó(   € V ^8„  d   QhRRRRRRRR/# )r   r/   rF   rs   rË   r   r   r   )r   s   "r   r   r   ò   s.   € ÷ Yñ Y˜ð Y Sð Y°3ð YÈSñ Yr   c               ót   € \        V4       \        P                  ! \        4        V  2\	        V4      R R7      # )ç      >@©ÚheadersÚtimeout)rÐ   rÍ   r`   r·   rÁ   )r/   rs   rË   s   &&$r   Ú_dashboard_getrØ   ò   s/   € Ü�4ÔÜ�9Š9œÓ)Ð*¨4¨&Ð1¼7À5»>ÐSWÔXÐXr   c          
     ó,   € V ^8„  d   QhRRRRRRRRRR/# )	r   r/   rF   Úbodyr"   rs   rË   r   r   r   )r   s   "r   r   r   ÷   s0   € ÷ ñ ˜#ð  ^ð ¸Cð È#ð ÐWZñ r   c               óÊ   € \        V4       \        P                  ! \        4        V  2V\	        V4      RR7      pVP                  4        VP                  4       ;'       g    / # )zFPOST to the dashboard, raise for HTTP errors, return the decoded body.rÔ   ©r)   rÖ   r×   )rÐ   rÍ   Úpostr·   rÁ   Úraise_for_statusr)   )r/   rÚ   rs   rË   Úresps   &&&$ r   Ú_dashboard_postrà   ÷   sP   € ä�4ÔÜ�:Š:œÓ*Ð+¨D¨6Ð2¸ÄwÈuÃ~Ð_cÔd€DØ×ÑÔØ�9‰9‹;×Ð˜"Ðr   c               ó    € V ^8„  d   QhRRRR/# )r   rß   r   r   rF   r   )r   s   "r   r   r   ÿ   s   € ÷ 
6ñ 
6 ð 
6¨ñ 
6r   c                óÆ  € R p\         P                  ! \        4      ;_uu_ 4        V P                  4       pR R R 4       \	        V\
        4      '       dS   R F-  pVP                  V4      '       g   K  \        W,          4      u # 	  \        P                  ! VRR7      R,          # \        V RR4      ;'       g    RpV'       d
   VR,          # R#   + '       g   i     L¢; i)NT)r<   :Niô  NÚtextrÉ   zno response body)ÚerrorÚmessageÚdetail)
rO   rP   r†   r)   ra   Údictr`   rF   ÚdumpsÚgetattr)rß   r4   r]   rã   s   &   r   Ú_response_error_detailrê   ÿ   s¡   € Ø€DÜ	×	Ò	œY×	'Õ	'Ø�y‰y‹{ˆ÷ 
(ä�$œ×ÒÛ1ˆCØ�x‰x˜�}Œ}Ü˜4�9“~Ò%ñ 2ô �zŠz˜$¨$Ô/°Õ5Ð5Ü�4˜ Ó$×*Ð*¨€Dßˆ4��:Ð5Ð#5Ð5÷ 
(×	'ús   ¥CÃC 	c               ó$   € V ^8„  d   QhRRRRRR/# )r   rß   r   ÚactionrF   r   r5   r   )r   s   "r   r   r     s&   € ÷ eñ e˜Cð e¨ð e°ñ er   c           
     óh   € \        V R ^È4      pVR8¼  d   \        RV RV R\        V 4       24      hR# )Ústatus_codeé�  úPhoton z failed: HTTP z: N)ré   rÎ   rê   )rß   rì   Ústatuss   && r   Ú_raise_for_statusrò     sB   € Ü�T˜=¨#Ó.€FØ�„}Ü˜W V H¨N¸6¸(À"ÔE[Ð\`ÓEaÐDbÐcÓdÐdñ r   c               ó    € V ^8„  d   QhRRRR/# )r   ÚfnzCallable[[], None]r   r5   r   )r   s   "r   r   r     s   € ÷ ñ Ð ð  Tñ r   c                ó’   € \         P                  ! \        4      ;_uu_ 4        V ! 4        R R R 4       R #   + '       g   i     R # ; i©N)rO   rP   r†   )rô   s   &r   Ú_safer÷     s'   € Ü	×	Ò	œY×	'Õ	'Ù
Œ÷ 
(×	'×	'Ò	'ús	   £5µA	c                  óT   € ] tR tRt$ R]R&   R]R&   R]R&   R]R&   R]R	&   R]R
&   RtR# )Ú
DeviceCodei  rF   Údevice_codeÚ	user_codeÚverification_urirn   Úverification_uri_completerz   Ú
expires_inÚintervalr   N)r   r   r   r   Ú__annotations__r   r   r   r   rù   rù     s#   ‡ àÓØƒNØÓØ,Ó,ØƒOØ‡Mr   rù   T)Úfrozenc                  ó0   € ] tR tRt$ RtR]R&   R]R&   RtR# )Ú_DeviceTokenCandidatei#  z<A token-like value extracted from the device-token response.rF   Úsourcers   r   N)r   r   r   r   r   r   r   r   r   r   r  r  #  s   ‡ áFØƒKØ‡Jr   r  Ú	client_idÚscopec               ó$   € V ^8„  d   QhRRRRRR/# )r   r  rF   r  rn   r   rù   r   )r   s   "r   r   r   *  s)   € ÷ Eñ EØðEØ2?ðEØT^ñEr   c                óÂ  € \        R4       RV /pV'       d   WR&   \        P                  ! \        4        R2VRR7      pVP	                  4        VP                  4       p\        VR,          VR,          VR	,          VP                  R
4      \        VP                  R4      ;'       g    \        4      \        VP                  R4      ;'       g    \        4      R7      # )zBPOST ``/api/auth/device/code`` and return the device + user codes.rÑ   r  r  z/api/auth/device/coderÔ   ©r)   r×   rú   rû   rü   rý   rþ   rÿ   )rú   rû   rü   rý   rþ   rÿ   )rÐ   rÍ   rÝ   r·   rÞ   r)   rù   r`   rz   ÚDEFAULT_POLL_TIMEOUTÚDEFAULT_POLL_INTERVAL)r  r  rÚ   rß   r4   s   $$   r   Úrequest_device_coder  *  s¾   € ô �?Ô#Ø'¨Ð3€DßØˆW‰Ü�:Š:œÓ*Ð+Ð+@ÐAÈÐVZÔ[€DØ×ÑÔØ�9‰9‹;€DÜØ˜Õ'°4¸Õ3DØÐ0Õ1Ø"&§(¡(Ð+FÓ"GÜ�t—x‘x Ó-×EÐEÔ1EÓFÜ�T—X‘X˜jÓ)×BÐBÔ-BÓCôEð Er   r×   rÿ   Ú
on_pendingc               ó0   € V ^8„  d   QhRRRRRRRRRR	R
R/# )r   Úcoderù   r  rF   r×   zOptional[int]rÿ   r  zOptional[Callable[[], None]]r   r   )r   s   "r   r   r   <  s<   € ÷ 48ñ 48Ø
ð48Ø$'ð48ØFSð48àð48à0Lð48àX[ñ48r   c               óŒ  a€ \        R4       \        4        R2p\        P                  ! 4       T;'       g    V P                  ;'       g    \        ,           pVe   TMV P
                  ;'       g    \        pRRRV P                  RV/pR V3R	 llp	\        P                  ! 4       V8  Ed,   \        P                  ! V4        \        P                  ! WXR
R7      p
T
P                  ^È8X  d¥   / p\        P                   ! \"        \$        4      ;_uu_ 4        T
P'                  4       ;'       g    / pRRR4       \)        T\*        4      '       d   TM/ p\-        T\/        T
R/ 4      R7      pT'       g   \1        R4      hT^ ,          P2                  # T
P                  R8X  d   T^
,          pT	! 4        EK#  T
P                  R8X  dÞ   / p\        P                   ! \&        P4                  4      ;_uu_ 4        T
P'                  4       ;'       g    / pRRR4       TP7                  R4      ;'       g    TP7                  R4      ;'       g    RpTR9   d   TR8X  d
   T^,          pT	! 4        EKÚ  TR9   d   \1        RT 24      h\1        RT;'       g    T
P8                   24      h\        P                  RT
P                  T
P8                  R,          4       EKF  \;        R4      h  \        P                   d#   p\        P                  RT4        Rp?EKƒ  Rp?ii ; i  + '       g   i     ELõ; i  + '       g   i     EL+; i)zÐPoll ``/api/auth/device/token`` until approved (official-CLI semantics: sleep first;
``authorization_pending`` keeps the interval, ``slow_down`` +5s, HTTP 429 +10s,
``access_denied``/``expired_token`` abort).rÑ   z/api/auth/device/tokenNÚ
grant_typez,urn:ietf:params:oauth:grant-type:device_coderú   r  c               ó   € V ^8„  d   QhRR/# r   r   )r   s   "r   r   Ú$poll_for_token.<locals>.__annotate__I  s   € ÷ ñ �dñ r   c                 ó2   <€ S '       d   \        S 4       R # R # rö   )r÷   )r  s   €r   Ú_pendingÚ poll_for_token.<locals>._pendingI  s   ø€ ßÜ�*Öñ r   rÔ   r	  z$photon: device-token poll failed: %srÖ   )rÖ   z“Photon returned 200 but no token candidate in the device-token response (expected access_token, data.access_token, accessToken, or set-auth-token).i­  rï   rä   rå   rÉ   Ú	slow_downzPhoton login failed: zPhoton device token error: z-photon: device-token unexpected status %s: %s:NéÈ   NzPhoton device login timed out)Úauthorization_pendingr  )Úexpired_tokenÚaccess_denied)rÐ   r·   r{   rþ   r
  rÿ   r  rú   ÚsleeprÍ   rÝ   ÚRequestErrorr-   r.   rî   rO   rP   Ú	TypeErrorÚ
ValueErrorr)   ra   rç   Ú!_device_response_token_candidatesré   rÎ   rs   r,   r`   rã   ÚTimeoutError)r  r  r×   rÿ   r  ÚurlÚdeadliner  Úgrantr  rß   r1   rÚ   Ú
candidatesÚerrs   &$$$d          r   Úpoll_for_tokenr'  <  s¢  ø€ ô �?Ô#ÜÓÐÐ5Ð
6€CÜ�yŠy‹{˜g×PÐP¨¯©×PÐPÔ<PÕQ€HØ Ò,‰H°4·=±=×3YÐ3YÔDY€EØÐIÈ=ÐZ^×ZjÑZjØ˜)ð%€E÷ð ô �)Š)‹+˜Õ
 Ü�
Š
�5Ôð	Ü—:’:˜c°tÔ<ˆDð ×Ñ˜sÔ"ØˆDÜ×$Ò$¤Y´
×;Õ;Ø—y‘y“{×(Ð( b�÷ <ä% d¬D×1Ò1‘4°rˆDÜ:¸4ÌÐQUÐW`ÐbdÓIeÔfˆJßÜ"ðbócð cð ˜a•=×&Ñ&Ð&Ø×Ñ˜sÔ"Ø�R�KˆEÙŒJÚØ×Ñ˜sÔ"ØˆDÜ×$Ò$¤T×%9Ñ%9×:Õ:Ø—y‘y“{×(Ð( b�÷ ;à—(‘(˜7Ó#×@Ð@ t§x¡x°	Ó':×@Ð@¸bˆCØÐ<Ô<Ø˜+Ô%Ø˜Q•J�EÙ”
ÚØÐ8Ô8Ü"Ð%:¸3¸%Ð#@ÓAÐAÜÐ!<¸S×=MÐ=MÀDÇIÁIÐ<NÐOÓPÐPÜ�‰ÐFÈ×HXÑHXÐZ^×ZcÑZcÐdhÕZi×jÜ
Ð6Ó
7Ð7øôA ×!Ñ!ô 	Ü�N‰NÐAÀ1ÔEÞûð	ú÷
 <×;Ð;ú÷ ;×:Ð:ús0   ÃK$ ÄLÇ5L2Ë$LË9LÌLÌL/	Ì2M	rÖ   c               ó$   € V ^8„  d   QhRRRRRR/# )r   rÚ   r"   rÖ   úOptional[Any]r   rb   r   )r   s   "r   r   r   s  s"   € ÷ ñ ¨Nð Èð Ðaeñ r   c          
     óx  € \        V P                  R4      \        4      '       d   V P                  R4      M/ p\        V P                  R4      \        4      '       d   V P                  R4      M/ pRV P                  R4      3RV P                  R4      3RVP                  R4      3RVP                  R4      3RVP                  R4      3R\        VR4      33p. p\	        4       pV FN  w  rx\        V4      p	V	'       g   K  W–9  g   K"  VP                  V	4       VP                  \        WyR	7      4       KP  	  V# )
uÈ   De-duplicated token candidates from a device-token response â€” Photon has returned
tokens under several keys across versions plus the ``set-auth-token`` header, so
collect every shape for validation.Úsessionr4   rr   ÚaccessTokenzsession.access_tokenzdata.access_tokenzdata.accessTokenzset-auth-token©r  rs   )	ra   r`   rç   Ú_header_valueÚsetÚ_clean_bearer_tokenÚaddÚappendr  )
rÚ   rÖ   r+  r4   Úrawr%  Úseenr  Úvaluers   s
   &$        r   r   r   s  s  € ô &0°·±¸Ó0CÄT×%JÒ%Jˆd�h‰h�yÔ!ÐPR€GÜ)¨$¯(©(°6Ó*:¼D×AÒAˆ4�8‰8�FÔÀr€Dà	˜Ÿ™ .Ó1Ð2°]ÀDÇHÁHÈ]ÓD[Ð4\Ø	 §¡¨^Ó!<Ð=Ø	˜dŸh™h ~Ó6Ð7Ð:LÈdÏhÉhÐWdÓNeÐ9fØ	œ=¨Ð2BÓCÐDð	F€Cð
 €JÜ“€DÛ‰ˆÜ# EÓ*ˆß‰5�UÖ&Ø�H‰H�UŒOØ×ÑÔ3¸6ÔOÖPñ	 ð
 Ðr   c               ó    € V ^8„  d   QhRRRR/# )r   r5  r   r   rn   r   )r   s   "r   r   r   ˆ  s   € ÷ Yñ Y˜sð Y }ñ Yr   c                óâ   € \        V \        4      '       g   R # V P                  4       pVP                  4       P	                  R4      '       d   VR,          P                  4       MT;'       g    R # )Nzbearer :é   NN)ra   rF   ÚstripÚlowerÚ
startswith)r5  rs   s   & r   r0  r0  ˆ  sO   € Ü�eœS×!Ò!ÙØ�K‰K‹M€EØ!&§¡£×!9Ñ!9¸)×!DÒ!DˆE�"�I�O‰OÔÈ%×XÐXÐTXÐXr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   rÖ   r)  r@   rF   r   rn   r   )r   s   "r   r   r   �  s!   € ÷ ñ ˜=ð °ð ¸ñ r   c                óZ  € V '       g   R # \         P                  ! \        4      ;_uu_ 4        V P                  V4      pV'       d   \	        V4      uuR R R 4       #  R R R 4       \         P                  ! \
        \        4      ;_uu_ 4        \        V 4      P                  4        FR  w  r2\	        V4      P                  4       VP                  4       8X  g   K3  V'       g   K=  \	        V4      u uuR R R 4       # 	  R R R 4       R #   + '       g   i     L±; i  + '       g   i     R # ; irö   )
rO   rP   ÚAttributeErrorr`   rF   r  r  rç   Úitemsr:  )rÖ   r@   r5  r]   s   &&  r   r.  r.  �  s½   € ßÙÜ	×	Ò	œ^×	,Õ	,Ø—‘˜DÓ!ˆßÜ�u“:÷ 
-Ò	,à÷ 
-ô 
×	Ò	œY¬
×	3Õ	3Ü˜w›-×-Ñ-Ö/‰JˆCÜ�3‹x�~‰~Ó 4§:¡:£<Ö/·E±EÜ˜5“zÑ!÷ 
4Ò	3Ù/÷ 
4ñ ÷ 
-×	,ú÷ 
4Ö	3ñ ús*   ­$DÂADÃDÃ#DÃ:DÄD	ÄD*	c               ó    € V ^8„  d   QhRRRR/# )r   rs   rF   r   r"   r   )r   s   "r   r   r   �  s   € ÷ ñ  ð ¨ñ r   c                óþ   a € R V 3R llpV! RR4      P                  4       p\        V\        4      '       d   VP                  R4      MRp\        V\        4      '       d	   V'       g   \	        R4      hV! RR	4       V# )
uË   Verify a device-flow token against ``/api/auth/get-session`` AND ``/api/projects/`` â€”
the device flow can mint tokens that pass the session lookup but are rejected by the
project APIs setup depends on.c               ó$   € V ^8„  d   QhRRRRRR/# )r   r/   rF   Úrejectedr   r   r   )r   s   "r   r   Ú+validate_photon_token.<locals>.__annotate__¡  s!   € ÷ ñ �3ð  #ð ¨#ñ r   c                óx   <€ \        V S4      pVP                  R9   d   \        V4      hVP                  4        V# )é‘  )rF  i“  )rØ   rî   r   rÞ   )r/   rC  rß   rs   s   && €r   Ú_getÚ#validate_photon_token.<locals>._get¡  s9   ø€ Ü˜d EÓ*ˆØ×Ñ˜zÔ)Ü*¨8Ó4Ð4Ø×ÑÔØˆr   z/api/auth/get-sessionzKPhoton issued a device token, but the dashboard session lookup rejected it.ÚuserNzTPhoton issued a device token, but the dashboard session lookup did not recognize it.ú/api/projects/zXPhoton device token was accepted for the session lookup but rejected by the project API.)r)   ra   rç   r`   r   )rs   rG  r4   rI  s   f   r   r…   r…   �  sz   ø€ ÷ð ñ Ð'Ø]ó_ß_cÑ_cÓ_eð 	ä)¨$´×5Ò5ˆ4�8‰8�FÔ¸4€DÜ�dœD×!Ò!¯Ü&Øbódð 	dáÐ	ÐuÔvØ€Kr   c               ó    € V ^8„  d   QhRRRR/# )r   r%  rb   r   rF   r   )r   s   "r   r   r   ±  s   € ÷ Iñ I¨4ð I°Cñ Ir   c                óŠ  € V '       g   \        R4      hRpRpV  F%  p \        VP                  4       VP                  u # 	  Ve5   RP                  R V  4       4      ;'       g    Rp\        V RV R24      VhVe   Vh\        R4      h  \         d,   pTp\	        T\
        4      '       d	   Tp Rp?K�   Rp?K£  Rp?ii ; i)	zBReturn the first candidate token that passes dashboard validation.zHPhoton returned 200 but no token candidate in the device-token response.Nz, c              3  ó8   "  € T F  qP                   x € K  	  R # 5irö   )r  )Ú.0Úcs   & r   Ú	<genexpr>Ú-_validated_dashboard_token.<locals>.<genexpr>À  s   é € Ð9©j¨ŸHžH«jùs   ‚Únonez@ Device login returned no project-valid dashboard token (tried: z).z.Photon did not return a usable dashboard token)rÎ   r…   rs   r†   ra   r   Újoin)r%  Údashboard_errorÚ
last_errorÚ	candidateÚexcÚsourcess   &     r   Ú_validated_dashboard_tokenrY  ±  sÒ   € çÜÐeÓfÐfØ:>€OØ*.€JÛˆ	ð	&Ü! )§/¡/Ô2Ø—?‘?Ò"ñ  ð Ò"Ø—)‘)Ñ9©jÓ9Ó9×CÐC¸VˆÜ&ØÐÐ_Ð`gÐ_hÐhjÐkó
àð	ð ÒØÐÜ
ÐGÓ
HÐHøô ô 	&ØˆJÜ˜#Ô7×8Ò8Ø"%–õ 9ûð	&ús   ž BÂCÂB=Â=CÚopen_browserÚon_user_codec               ó(   € V ^8„  d   QhRRRRRRRR/# )r   r  rF   rZ  rƒ   r[  z&Optional[Callable[[DeviceCode], None]]r   r   )r   s   "r   r   r   É  s,   € ÷ ñ ØðØ9=ðà8ðàDGñr   c                ó¦  aa€ \        V R7      oS'       d   \        VV3R l4       V'       da   \        P                  ! \        4      ;_uu_ 4        ^ RIpTP                  SP                  ;'       g    SP                  ^R7       RRR4       \        SV R7      p\        \        RVR7      .4      p\        V4       V#   + '       g   i     LA; i)z©Run the full device-code login flow, validate the token against the dashboard API
before persisting it, and return it. ``on_user_code`` receives the :class:`DeviceCode`.)r  c                 ó   <€ S! S 4      # rö   r   )r  r[  s   €€r   Ú<lambda>Ú#login_device_flow.<locals>.<lambda>Ð  s
   ø€ ‘l 4Ô(r   N)ÚnewÚpollr-  )r  r÷   rO   rP   r†   Ú
webbrowserr(   rý   rü   r'  rY  r  r}   )r  rZ  r[  rc  Úfirst_tokenrs   r  s   $$d   @r   Úlogin_device_flowre  É  s–   ù€ ô
 ¨Ô3€DßÜÕ(Ô)ßÜ× Ò ¤×+Õ+ÛØ�O‰O˜D×:Ñ:×SÐS¸d×>SÑ>SÐYZˆOÔ[÷ ,ô ! °Ô;€KÜ&Ô(=ÀVÐS^Ô(_Ð'`Óa€EÜ�uÔØ€L÷ ,×+ús   Á6C Ã C	c               ó    € V ^8„  d   QhRRRR/# )r   r4   r   r   úlist[dict[str, Any]]r   )r   s   "r   r   r   Ý  s   € ÷ ñ �sð Ð3ñ r   c                ó8  a€ \        V \        4      '       d   V # \        V \        4      '       dk   R Fd  pV P                  V4      o\        S\        4      '       d   Su # \        S\        4      '       g   KF  \	        V3R lR 4       R4      pVf   Kb  Vu # 	  . # )r4   c              3  ó„   <"  € T F5  p\        SP                  V4      \        4      '       g   K*  SV,          x € K7  	  R # 5irö   )ra   r`   rb   )rN  r    Úinners   & €r   rP  Ú_unwrap_list.<locals>.<genexpr>æ  s3   øé € ð BÑ1X¨AÜ",¨U¯Y©Y°q«\¼4×"@ô  (˜u QŸxšxÓ1Xùs
   ƒ'A ¯A N)r4   ÚprojectsÚusersÚlinesr?  )rl  rm  rn  r?  )ra   rb   rç   r`   Únext)r4   r]   Únestedrj  s   &  @r   Ú_unwrap_listrq  Ý  sˆ   ø€ Ü�$œ×ÒØˆÜ�$œ×ÒÛBˆCØ—H‘H˜S“MˆEÜ˜%¤×&Ò&Ø’Ü˜%¤×&Ô&Üô BÑ1Xó BØCGóI�àÔ%Ø!’Mñ Cð €Ir   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r/   rF   rs   r   rg  r   )r   s   "r   r   r   í  s"   € ÷ %ñ %˜#ð % cð %Ð.Bñ %r   c                ón   € \        WR R7      pVP                  4        \        VP                  4       4      # )rÉ   rÏ   )rØ   rÞ   rq  r)   )r/   rs   rß   s   && r   Ú_dashboard_listrt  í  s+   € Ü˜$¨BÔ/€DØ×ÑÔÜ˜Ÿ	™	›Ó$Ð$r   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r4   r"   rì   rF   r   r5   r   )r   s   "r   r   r   ó  s&   € ÷ Gñ G˜nð G°cð G¸dñ Gr   c                ód   € V P                  R 4      '       d   \        RV RV R ,           24      hR# )rä   rð   z	 failed: N)r`   rÎ   )r4   rì   s   &&r   Ú_raise_on_error_keyrw  ó  s2   € Ø‡x�x�×ÒÜ˜W V H¨I°d¸7µm°_ÐEÓFÐFñ r   c               ó    € V ^8„  d   QhRRRR/# )r   rs   rF   r   rg  r   )r   s   "r   r   r   ø  s   € ÷ 3ñ 3˜ð 3Ð!5ñ 3r   c                ó   € \        RV 4      # )u7   GET ``/api/projects`` â€” return the caller's projects.ú/api/projects©rt  r|   s   &r   Úlist_projectsr|  ø  s   € ä˜?¨EÓ2Ð2r   c               ó$   € V ^8„  d   QhRRRRRR/# )r   rs   rF   r@   r   úOptional[dict[str, Any]]r   )r   s   "r   r   r   ý  s"   € ÷ ñ  ð ¨3ð Ð3Kñ r   c                óú   € T;'       g    RP                  4       P                  4       p\        V 4       FC  pVP                  R4      ;'       g    RP                  4       P                  4       V8X  g   KA  Vu # 	  R# )z4First project whose name matches (case-insensitive).rÉ   r@   N)r9  r:  r|  r`   )rs   r@   ÚtargetÚprojs   &&  r   Úfind_project_by_namer‚  ý  sc   € à�jˆj�b×ÑÓ!×'Ñ'Ó)€FÜ˜eÖ$ˆØ�H‰H�VÓ×"Ð" ×)Ñ)Ó+×1Ñ1Ó3°vÖ=ØŠKñ %ñ r   ÚlocationzUnited Statesc               ó(   € V ^8„  d   QhRRRRRRRR/# )r   rs   rF   r@   rƒ  r   r"   r   )r   s   "r   r   r     s,   € ÷ ñ ØðØðØ?BðØYgñr   c               ó¬  € RVRVRRRR/p\        RW0RR7      p\        V\        4      '       g   \        R	4      h\	        VR
4       VP                  R4      RJ d(   \        RVP                  R4      ;'       g    T 24      h\        VP                  R4      \        4      '       d
   VR,          MTpVP                  R4      '       g   \        R4      hV# )zyPOST ``/api/projects`` and return the project (Spectrum is always provisioned;
the request carries no ``spectrum`` flag).r@   rƒ  ÚtemplateFÚobservabilityrz  z project creationrÏ   z5Photon create-project returned an unexpected responsezcreate-projectÚsucceedzPhoton create-project failed: rå   r4   Úidz1Photon create-project did not return a project id)rà   ra   rç   rÎ   rw  r`   )rs   r@   rƒ  rÚ   r4   Úprojects   &$$   r   Úcreate_projectr‹    sÇ   € ð # D¨*°hÀ
ÈEÐSbÐdiÐj€DÜ˜?¨DÐ>QÔR€DÜ�dœD×!Ò!ÜÐRÓSÐSÜ˜Ð.Ô/Ø‡x�x�	Ó˜eÓ#ÜÐ;¸D¿H¹HÀYÓ<O×<WÐ<WÐSWÐ;XÐYÓZÐZÜ.8¸¿¹À&Ó9IÌ4×.PÒ.P˜d 6žlÐVZ€GØ�;‰;�t×ÒÜÐNÓOÐOØ€Nr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   rs   rF   r�   r   r   )r   s   "r   r   r     s!   € ÷ ñ  Sð °cð ¸cñ r   c                óš   € \        RV R2/ V 4      p\        VR4       VP                  R4      pV'       g   \        R4      h\	        V4      # )uw   POST ``/api/projects/{id}/regenerate-secret`` â†’ the new secret (the only way to
read one â€” persist it immediately).rJ  z/regenerate-secretzregenerate-secretÚprojectSecretz2Photon regenerate-secret returned no projectSecret)rà   rw  r`   rÎ   rF   )rs   r�   r4   Úsecrets   &&  r   Úregenerate_project_secretr�    sO   € ô ˜^¨J¨<Ð7IÐJÈBÐPUÓV€DÜ˜Ð1Ô2Ø�X‰X�oÓ&€FßÜÐOÓPÐPÜˆv‹;Ðr   c               ó    € V ^8„  d   QhRRRR/# )r   ÚphonerF   r   r   )r   s   "r   r   r   $  s   € ÷ .ñ .˜Cð . Cñ .r   c                óF   € \         P                  ! RRT ;'       g    R4      # )z?Reduce a phone string to ``+`` and digits for dedup comparison.z[^\d+]rÉ   )ÚreÚsub)r’  s   &r   Ú_normalize_phoner–  $  s   € ä�6Š6�)˜R § ¨"Ó-Ð-r   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r�   rF   r‘   r   rg  r   )r   s   "r   r   r   )  s"   € ÷ %ñ %˜3ð %°ð %Ð8Lñ %r   c                óÄ   € \        4        \        4        RV  R2p\        P                  ! V\	        W4      RR7      p\        VR4       \        VP                  4       4      # )uD   GET Spectrum Cloud ``/projects/{id}/users/`` â†’ ``SpectrumUser[]``.ú
/projects/ú/users/rÔ   rÕ   z
list-users)rÐ   r¼   rÍ   r`   rÈ   rò   rq  r)   )r�   r‘   r"  rß   s   &&  r   Ú
list_usersr›  )  sP   € äÔÜÓÐ˜j¨¨°GÐ
<€CÜ�9Š9�S¤&¨Ó"DÈdÔS€DÜ�d˜LÔ)Ü˜Ÿ	™	›Ó$Ð$r   c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   r�   rF   r‘   r£   r   r~  r   )r   s   "r   r   r   2  s)   € ÷ ñ  3ð ¸ð È3ð ÐSkñ r   c                óš   € \        V4      p\        W4       F0  p\        VP                  R4      ;'       g    R4      V8X  g   K.  Vu # 	  R# )z<Existing Spectrum user with the given phone number, or None.ÚphoneNumberrÉ   N)r–  r›  r`   )r�   r‘   r£   r€  rI  s   &&&  r   Úfind_user_by_phonerŸ  2  sC   € ä˜lÓ+€FÜ˜:Ö6ˆÜ˜DŸH™H ]Ó3×9Ð9°rÓ:¸fÖDØŠKñ 7ñ r   Ú
first_nameÚ	last_nameÚemailÚsend_inviteFc               ó8   € V ^8„  d   QhRRRRRRRRRRRRR	R
RR/# )r   r�   rF   r‘   r£   r   rn   r¡  r¢  r£  rƒ   r   r"   r   )r   s   "r   r   r   ;  sL   € ÷ ñ ØðØ%(ðØ;>ðØLYðàðà,9ðàOSðà`nñr   c               ó¤  € \        R4       \        P                  V4      '       g   \        RV: 24      h\	        4        RV  R2pRRRV/pV'       d   \
        P                  R4       TP                  R	V3R
V3RV33 U	U
u/ uF  w  ršV
'       g   K  WšbK  	  up
p	4       \        P                  ! Wx\        W4      RR7      p\        VR4       VP                  4       ;'       g    / p\        VR4       VP                  R4      ;'       g    VP                  R4      ;'       g    Tp\        V\         4      '       g   \#        R4      hV# u up
p	i )zBPOST Spectrum Cloud ``/projects/{id}/users/`` and return the user.z user creationz4phone_number must be E.164 (e.g. +15551234567); got r™  rš  ÚtypeÚsharedrž  z?photon: send_invite is ignored by Spectrum shared-user creationÚ	firstNameÚlastNamer¢  rÔ   rÜ   zcreate-userrI  r4   z2Photon create-user returned an unexpected response)rÐ   ÚE164_REÚmatchr  r¼   r-   Údebugrž   rÍ   rÝ   rÈ   rò   r)   rw  r`   ra   rç   rÎ   )r�   r‘   r£   r   r¡  r¢  r£  r"  rÚ   r    r¡   rß   r4   rI  s   &&$$$$$       r   Úcreate_userr­  ;  s-  € ô Ð#Ô$Ü�=‰=˜×&Ò&ÜÐOÐP\ÑO_Ð`ÓaÐaÜÓÐ˜j¨¨°GÐ
<€CØ" H¨m¸\ÐJ€DßÜ�‰ÐVÔWØ‡K�K K°Ð#<¸zÈ9Ð>UÐX_ÐafÐWgÑ"hÔnÑ"h™$˜!Ôlm”�’Ñ"hÒnÔoÜ�:Š:�c¬f°ZÓ.PÐZ^Ô_€DÜ�d˜MÔ*Ø�9‰9‹;×Ð˜"€DÜ˜˜mÔ,Ø�8‰8�FÓ×7Ð7˜tŸx™x¨Ó/×7Ð7°4€DÜ�dœD×!Ò!ÜÐOÓPÐPØ€Kùó os   ÂE
ÂE
c               ó4   € V ^8„  d   QhRRRRRRRRRRRRR	R
/# )r   r�   rF   r‘   r£   r   rn   r¡  r¢  r   ztuple[dict[str, Any], bool]r   )r   s   "r   r   r   Q  sD   € ÷ 	?ñ 	?Øð	?Ø%(ð	?Ø;>ð	?ØLYð	?àð	?à,9ð	?àE`ñ	?r   c          	     óJ   € \        WV4      pVe   VR3# \        WW#WER7      R3# )uŠ   Idempotently register a Spectrum user â†’ ``(user, created)``; the official CLI does
no dedup, so we add it to keep ``setup`` re-runnable.F)r£   r   r¡  r¢  T)rŸ  r­  )r�   r‘   r£   r   r¡  r¢  Úexistings   &&$$$$ r   Úregister_user_if_absentr±  Q  s<   € ô
 " *¸lÓK€HØÒØ˜ˆÐÜ�zÀØ!*ô9Ø:>ð?ð ?r   c               ó    € V ^8„  d   QhRRRR/# )r   rI  r~  r   rn   r   )r   s   "r   r   r   ]  s   € ÷ %ñ %Ð5ð %¸-ñ %r   c                ób   € V '       d   V P                  R4      MRpV'       d   \        V4      # R# )z™The iMessage number a user texts to reach the agent (``assignedPhoneNumber``, the
dashboard's "TEXTS ON" column). None when unset (freshly created user).ÚassignedPhoneNumberN)r`   rF   )rI  Úvals   & r   Úuser_assigned_liner¶  ]  s(   € ÷ .2ˆ$�(‰(Ð(Ô
)°t€CßŒ3ˆs‹8Ð$ Ð$r   c               ó   € V ^8„  d   QhRR/# r‰   r   )r   s   "r   r   r   d  s   € ÷ hñ hÐ>ñ hr   c                 ól  € \        \        4       R4      p \        V \        4      '       d   T M/ p V P	                  R4      ;'       g    V P	                  R4      pV P	                  R4      ;'       g    V P	                  R4      pV'       d   \        V4      M	\        4       V'       d   \        V4      3# R3# )z>``(operator_phone_number, assigned_phone_number)`` for status.r¨   r£   rž  r¤   r´  N)rd   r2   ra   rç   r`   rF   Ú_configured_operator_phone)rt   r’  Úassigneds      r   Úload_user_numbersr»  d  sŠ   € äœ
› mÓ4€EÜ ¤t×,Ò,‰E°"€EØ�I‰I�nÓ%×AÐA¨¯©°=Ó)A€EØ�y‰yÐ0Ó1×UÐU°U·Y±YÐ?TÓ5U€HßŒC�ŒJÔ%?Ó%A×T\Ä3ÀxÃ=ÐgÐgÐbfÐgÐgr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r�   rF   r‘   r   rŠ   r   )r   s   "r   r   r   m  s"   € ÷ ñ  Sð ¸#ð ÐBeñ r   c                óî  € \        4       w  r#V'       d   \        WV4      pM&\        W4      p\        V4      ^8X  d
   V^ ,          MRpRpV'       dg   VP	                  R4      p\        \        VP	                  R4      ;'       g    R4      4      p\        P                  V4      '       d   Tp\        V4      p\        4       pV'       g   \        4       MRp	V	'       dH   V'       d@    \        W˜RR7      p
V
'       d*   V
P	                  R4      '       d   \        V
R,          4      p\!        Y#V'       d   \        V4      MRVR7       W#3#   \         d!   p\        P                  RT4        Rp?LJRp?ii ; i)	zFRefresh cached user numbers from Photon without provisioning anything.Nr‰  rž  rÉ   F)Úcreate_if_missingz6photon: could not refresh iMessage line for status: %s)r£   r¤   r¥   rš   )r»  rŸ  r›  Úlenr`   r–  rF   rª  r«  r¶  r›   rv   Úget_imessage_liner†   r-   r¬  r©   )r�   r‘   r’  rº  rI  rm  r¥   Údashboard_phoneÚdashboard_idÚdashboard_tokenÚliner1   s   &&          r   Úrefresh_user_numbersrÅ  m  s$  € ä'Ó)�O€EßÜ! *¸eÓD‰ä˜:Ó6ˆÜ˜u›:¨œ?ˆu�QŽx°ˆØ€GßØ—(‘(˜4“.ˆÜ*¬3¨t¯x©x¸Ó/F×/LÐ/LÈ"Ó+MÓNˆÜ�=‰=˜×)Ò)Ø#ˆEÜ% dÓ+ˆÜ,Ó.€Lß19Ô'Ô)¸t€OßŸ<ð	VÜ$ _ÐV[Ô\ˆDß˜Ÿ™ ×/Ò/Ü˜t MÕ2Ó3�ô  Eß/6œs 7œ|¸DÐWcõeàˆ?Ðøô	 ô 	VÜ�L‰LÐQÐST×UÑUûð	Vús   Ã'+E	 ÄE	 Å	E4ÅE/Å/E4c               ó   € V ^8„  d   QhRR/# rm   r   )r   s   "r   r   r   Š  s   € ÷ ;ñ ; Mñ ;r   c                 ó”  € \        \        R4      ;'       g    R4      p V '       d   \        P                  V 4      '       d   V # \        R4      ;'       g    Rp\	        \         \
        P                  ! RV4      4       Uu. uF"  p\        P                  V4      '       g   K   VNK$  	  pp\        V4      ^8X  d
   V^ ,          # R# u upi )zDInfer the operator's E.164 number from existing Photon env settings.ÚPHOTON_HOME_CHANNELrÉ   ÚPHOTON_ALLOWED_USERSz[,\s]+N)r–  Ú_get_config_env_valuerª  r«  Úmapr”  Úsplitr¿  )ÚhomeÚallowedÚnr%  s       r   r¹  r¹  Š  sž   € äÔ1Ð2GÓH×NÐNÈBÓO€Dß”—‘˜d×#Ò#ØˆÜ#Ð$:Ó;×AÐA¸r€GÜ Ô!1´2·8²8¸IÀwÓ3OÔPÓeÑP˜ÔT[×TaÑTaÐbc×Td—!�!ÑP€JÐeÜ 
›O¨qÔ0ˆ:�a�=Ð:°dÐ:ùò fs   Â CÂ"Cc               ó    € V ^8„  d   QhRRRR/# )r   r]   rF   r   rn   r   )r   s   "r   r   r   ”  s   € ÷ ñ ˜sð  }ñ r   c                ón   €  ^ RI Hp T! T 4      #   \         d    \        P                  ! T 4      u # i ; i)é    )Úget_env_value)r­   rÓ  r†   rA   r´   )r]   rÓ  s   & r   rÊ  rÊ  ”  s6   € ðÝ3ñ ˜ÓÐøô ô Ü�yŠy˜‹~Òðús   ‚ �!4³4c               ó$   € V ^8„  d   QhRRRRRR/# )r   rs   rF   r�   r   rg  r   )r   s   "r   r   r   ž  s'   € ÷ Gñ G�cð G sð GÐ/Cñ Gr   c                ó"   € \        RV R2V 4      # )uO   GET ``/api/projects/{id}/lines`` â†’ ``[{id, platform, phoneNumber, status}]``.rJ  ú/linesr{  )rs   r�   s   &&r   Ú
list_linesr×  ž  s   € ä˜^¨J¨<°vÐ>ÀÓFÐFr   ÚplatformÚimessagec               ó(   € V ^8„  d   QhRRRRRRRR/# )r   rs   rF   r�   rØ  r   r"   r   )r   s   "r   r   r   £  s(   € ÷ $ñ $�Cð $ Sð $°sð $ÈNñ $r   c               óv   € \        RV R2RV/V 4      p\        VR4       VP                  R4      ;'       g    T# )z:POST ``/api/projects/{id}/lines`` to provision a new line.rJ  rÖ  rØ  zadd-linerÄ  )rà   rw  r`   )rs   r�   rØ  r4   s   &&$ r   Úadd_linerÜ  £  s@   € ä˜^¨J¨<°vÐ>ÀÈXÐ@VÐX]Ó^€DÜ˜˜jÔ)Ø�8‰8�FÓ×#Ð#˜tÐ#r   r¾  c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   rs   rF   r�   r¾  rƒ   r   r~  r   )r   s   "r   r   r   ª  s,   € ÷ ñ ØðØðØ7;ðØG_ñr   c               óÐ   € \        R \        W4       4       R4      pVf	   V'       g   V#  \        WRR7      #   \         d"   p\        P                  RT4        Rp?R# Rp?ii ; i)z�The project's iMessage line, provisioning one if absent and ``create_if_missing``;
None if there is none and provisioning failed.c              3  ó„   "  € T F6  qP                  R 4      ;'       g    RP                  4       R8X  g   K2  Vx € K8  	  R# 5i)rØ  rÉ   rÙ  N)r`   r:  )rN  Úlns   & r   rP  Ú$get_imessage_line.<locals>.<genexpr>®  s5   é € ÐpÑ;˜ÇÁÀzÓ@R×@XÐ@XÐVX×?_Ñ?_Ó?aÐeoÑ?o—’Ó;ùs   ‚A žA ¶
A NrÙ  )rØ  z2photon: could not auto-provision iMessage line: %s)ro  r×  rÜ  r†   r-   r.   )rs   r�   r¾  rÄ  r1   s   &&$  r   rÀ  rÀ  ª  s^   € ô Ñpœj¨Ô;ÓpÐrvÓw€DØÒ×0ØˆðÜ˜°JÔ?Ð?øÜô Ü�‰ÐKÈQÔOÝûðús   ¬9 ¹A%ÁA Á A%c               ó    € V ^8„  d   QhRRRR/# )r   Úemitr   r   r5   r   )r   s   "r   r   r   º  s   € ÷ ñ  3ð °4ñ r   c                ó0  € \        4       w  r\        4       w  r4RRR\        4       '       d   RMR,           RV'       d   TMR,           RV'       d   RMR,           R	V'       d   TMR
,           RV'       d   TMR,           .pV ! RP                  V4      4       R# )zÕPretty-print the credential status table via *emit*. Every secret-bearing read is
reduced to a display literal here; the callback only ever receives the assembled
banner, so no tainted value escapes to the caller.zPhoton iMessage statusuB   â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€z  device token        : u
   âœ“ storedu'   âœ— missing (run `hermes photon setup`)z  project id          : u   âœ— missingz  project secret      : z  my number           : u3   âœ— missing (run `hermes photon setup --phone ...`)z  assigned number     : Ú
N)r–   r»  rv   rS  )rã  ÚsidÚsecr’  rº  Úrowss   &     r   Úprint_credential_summaryré  º  s�   € ô (Ó)�H€CÜ'Ó)�O€Eà ØLØ"Ü-×/Ò/‰LÐ5^õ	`à"¯S¡c°mÕDØ"·c¡l¸}ÕMØ"¯u¡eÐ:oÕpØ"·(¡hÐ@iÕjðl€Dñ 	ˆ�‰�4‹Ör   )rÉ   )er   Ú
__future__r   r)   ÚloggingrA   r”  rJ   r{   rC   Úbase64r   Údataclassesr   Úpathlibr   Útypingr   r   r   r	   r
   r   rÍ   r®   Úgateway.platforms._sharedr   r“   Úhermes_constantsr   rO   Ú	getLoggerr   r-   rÎ   r   ÚDEFAULT_CLIENT_IDÚDEFAULT_SCOPErµ   r»   ÚDEFAULT_PROJECT_NAMEr  r
  Úcompilerª  r    r2   rZ   rd   rk   rv   r}   r�   r‡   r–   r›   r¢   r©   rŸ   r·   r¼   rÁ   rÈ   rÐ   rØ   rà   rê   rò   r÷   rù   r  r  r'  r   r0  r.  r…   rY  re  rq  rt  rw  r|  r‚  r‹  r�  r–  r›  rŸ  r­  r±  r¶  r»  rÅ  r¹  rÊ  r×  rÜ  rÀ  Úprintré  r   r   r   Ú<module>rø     sú  ðñõ #ã Û Û 	Û 	Û Û Û Ý Ý !Ý ß =× =ðÛõ NÝ ,Û à	×	Ò	˜8Ó	$€ôP˜|ô Pð !Ð Ø&€Ø3Ð Ø7Ð Ø%Ð ØÐ ØÐ Ø
�*Š*Ð)Ó
*€õ
+õ
	õõ6@õõ)õYõ
õõyõlð>à*.ð>àFJ÷>ð
.Ø%)ð
.ØRVð
.à!ð
.àIM÷
.õOõ"VõTõ0õ/÷
Bð
Y¸÷ Yð
ÐPR÷ õ
6õeõð ÷ð ó ðñ �$Ô÷ð ó ððEØ)ðEØBO÷Eð$48Ø*;ð48ØVZð48à"ð48àOS÷48ðnÐX\÷ õ*Yõõõ(Ið0Ø)ðØ@Dðà;?÷õ(õ %õGõ
3õ
ðØ3ðØET÷õ"õ.õ
%õðØ\`ðà#ðà<@ðàV[÷ð,	?Ø\`ð	?à#ð	?à<@÷	?õ%õhõõ:;õõGð
$¸Z÷ $ðØ>B÷ð  */÷ ñ øðA ô ØƒEðús   ÁI É	I$É#I$