From 8ffb67aa731163be23e46a2286b01dec2b936c74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EC=A7=80=ED=98=81?= Date: Tue, 28 Oct 2025 13:49:41 +0900 Subject: [PATCH 1/9] chore: remove pycache --- .../python_api_advanced.cpython-312.pyc | Bin 566 -> 0 bytes .../python_api_basic.cpython-312.pyc | Bin 464 -> 0 bytes .../python_api_get_info.cpython-312.pyc | Bin 1894 -> 0 bytes fastapps/__pycache__/__init__.cpython-311.pyc | Bin 1049 -> 0 bytes fastapps/__pycache__/__init__.cpython-312.pyc | Bin 1720 -> 0 bytes fastapps/__pycache__/__init__.cpython-313.pyc | Bin 1718 -> 0 bytes fastapps/__pycache__/__init__.cpython-39.pyc | Bin 1321 -> 0 bytes fastapps/__pycache__/dev_server.cpython-312.pyc | Bin 12285 -> 0 bytes fastapps/__pycache__/dev_server.cpython-313.pyc | Bin 12302 -> 0 bytes .../auth/__pycache__/__init__.cpython-313.pyc | Bin 697 -> 0 bytes .../auth/__pycache__/decorators.cpython-313.pyc | Bin 4177 -> 0 bytes .../auth/__pycache__/verifier.cpython-313.pyc | Bin 5420 -> 0 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 360 -> 0 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 331 -> 0 bytes .../__pycache__/compiler.cpython-311.pyc | Bin 5277 -> 0 bytes .../__pycache__/compiler.cpython-313.pyc | Bin 4955 -> 0 bytes .../cli/__pycache__/__init__.cpython-313.pyc | Bin 270 -> 0 bytes fastapps/cli/__pycache__/main.cpython-312.pyc | Bin 8371 -> 0 bytes fastapps/cli/__pycache__/main.cpython-313.pyc | Bin 8796 -> 0 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 336 -> 0 bytes .../commands/__pycache__/create.cpython-313.pyc | Bin 9464 -> 0 bytes .../commands/__pycache__/dev.cpython-312.pyc | Bin 7996 -> 0 bytes .../commands/__pycache__/dev.cpython-313.pyc | Bin 8599 -> 0 bytes .../commands/__pycache__/init.cpython-313.pyc | Bin 9567 -> 0 bytes .../commands/__pycache__/use.cpython-313.pyc | Bin 5910 -> 0 bytes .../core/__pycache__/__init__.cpython-311.pyc | Bin 374 -> 0 bytes .../core/__pycache__/__init__.cpython-312.pyc | Bin 340 -> 0 bytes .../core/__pycache__/__init__.cpython-313.pyc | Bin 336 -> 0 bytes .../core/__pycache__/__init__.cpython-39.pyc | Bin 320 -> 0 bytes .../core/__pycache__/server.cpython-311.pyc | Bin 9228 -> 0 bytes .../core/__pycache__/server.cpython-313.pyc | Bin 15059 -> 0 bytes .../core/__pycache__/widget.cpython-311.pyc | Bin 4154 -> 0 bytes .../core/__pycache__/widget.cpython-312.pyc | Bin 10950 -> 0 bytes .../core/__pycache__/widget.cpython-313.pyc | Bin 10907 -> 0 bytes fastapps/core/__pycache__/widget.cpython-39.pyc | Bin 8287 -> 0 bytes .../types/__pycache__/__init__.cpython-311.pyc | Bin 342 -> 0 bytes .../types/__pycache__/__init__.cpython-313.pyc | Bin 313 -> 0 bytes .../types/__pycache__/schema.cpython-311.pyc | Bin 338 -> 0 bytes .../types/__pycache__/schema.cpython-313.pyc | Bin 309 -> 0 bytes tests/__pycache__/__init__.cpython-312.pyc | Bin 156 -> 0 bytes .../conftest.cpython-312-pytest-8.4.2.pyc | Bin 806 -> 0 bytes .../test_import.cpython-312-pytest-8.4.2.pyc | Bin 10674 -> 0 bytes .../test_widget.cpython-312-pytest-8.4.2.pyc | Bin 4742 -> 0 bytes 43 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 examples/__pycache__/python_api_advanced.cpython-312.pyc delete mode 100644 examples/__pycache__/python_api_basic.cpython-312.pyc delete mode 100644 examples/__pycache__/python_api_get_info.cpython-312.pyc delete mode 100644 fastapps/__pycache__/__init__.cpython-311.pyc delete mode 100644 fastapps/__pycache__/__init__.cpython-312.pyc delete mode 100644 fastapps/__pycache__/__init__.cpython-313.pyc delete mode 100644 fastapps/__pycache__/__init__.cpython-39.pyc delete mode 100644 fastapps/__pycache__/dev_server.cpython-312.pyc delete mode 100644 fastapps/__pycache__/dev_server.cpython-313.pyc delete mode 100644 fastapps/auth/__pycache__/__init__.cpython-313.pyc delete mode 100644 fastapps/auth/__pycache__/decorators.cpython-313.pyc delete mode 100644 fastapps/auth/__pycache__/verifier.cpython-313.pyc delete mode 100644 fastapps/builder/__pycache__/__init__.cpython-311.pyc delete mode 100644 fastapps/builder/__pycache__/__init__.cpython-313.pyc delete mode 100644 fastapps/builder/__pycache__/compiler.cpython-311.pyc delete mode 100644 fastapps/builder/__pycache__/compiler.cpython-313.pyc delete mode 100644 fastapps/cli/__pycache__/__init__.cpython-313.pyc delete mode 100644 fastapps/cli/__pycache__/main.cpython-312.pyc delete mode 100644 fastapps/cli/__pycache__/main.cpython-313.pyc delete mode 100644 fastapps/cli/commands/__pycache__/__init__.cpython-313.pyc delete mode 100644 fastapps/cli/commands/__pycache__/create.cpython-313.pyc delete mode 100644 fastapps/cli/commands/__pycache__/dev.cpython-312.pyc delete mode 100644 fastapps/cli/commands/__pycache__/dev.cpython-313.pyc delete mode 100644 fastapps/cli/commands/__pycache__/init.cpython-313.pyc delete mode 100644 fastapps/cli/commands/__pycache__/use.cpython-313.pyc delete mode 100644 fastapps/core/__pycache__/__init__.cpython-311.pyc delete mode 100644 fastapps/core/__pycache__/__init__.cpython-312.pyc delete mode 100644 fastapps/core/__pycache__/__init__.cpython-313.pyc delete mode 100644 fastapps/core/__pycache__/__init__.cpython-39.pyc delete mode 100644 fastapps/core/__pycache__/server.cpython-311.pyc delete mode 100644 fastapps/core/__pycache__/server.cpython-313.pyc delete mode 100644 fastapps/core/__pycache__/widget.cpython-311.pyc delete mode 100644 fastapps/core/__pycache__/widget.cpython-312.pyc delete mode 100644 fastapps/core/__pycache__/widget.cpython-313.pyc delete mode 100644 fastapps/core/__pycache__/widget.cpython-39.pyc delete mode 100644 fastapps/types/__pycache__/__init__.cpython-311.pyc delete mode 100644 fastapps/types/__pycache__/__init__.cpython-313.pyc delete mode 100644 fastapps/types/__pycache__/schema.cpython-311.pyc delete mode 100644 fastapps/types/__pycache__/schema.cpython-313.pyc delete mode 100644 tests/__pycache__/__init__.cpython-312.pyc delete mode 100644 tests/__pycache__/conftest.cpython-312-pytest-8.4.2.pyc delete mode 100644 tests/__pycache__/test_import.cpython-312-pytest-8.4.2.pyc delete mode 100644 tests/__pycache__/test_widget.cpython-312-pytest-8.4.2.pyc diff --git a/examples/__pycache__/python_api_advanced.cpython-312.pyc b/examples/__pycache__/python_api_advanced.cpython-312.pyc deleted file mode 100644 index 8b068fce20c4d0f49c0ca2284751dbae78e842f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 566 zcmY*W&ubJh6i#L`+nre#YpKwR&>TCL(RwLDDJ+U0h!k5ddkJy#c4ly9G9;O;yWriU zH~$8y|A`k74@*uKPu{8p6;Eb%w+CM!y!Y~b@5h&q{eFa;J!3z!KOREg+ThAME9dfw zbKW6=hDc!H6}a?LbtmJvc*zERk3w8zmD_Tosk|fFB?jSS^_wMZ{ciq{ufq)lHdJ+>$T8Jf? z)W(K|Oj!XFC~S{6MluD3zL&W8UAIZ*0#uw|<2|(76Z^xuw<}`yZ^5seu3h diff --git a/examples/__pycache__/python_api_basic.cpython-312.pyc b/examples/__pycache__/python_api_basic.cpython-312.pyc deleted file mode 100644 index 2361ee059d9bb7f271e5869a2609dfa466571764..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 464 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 JPgXR6{{dYA@6`YR diff --git a/fastapps/__pycache__/__init__.cpython-39.pyc b/fastapps/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index dfac9812fa175d5167e2e25f6191627c4f495682..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1321 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 diff --git a/fastapps/__pycache__/dev_server.cpython-313.pyc b/fastapps/__pycache__/dev_server.cpython-313.pyc deleted file mode 100644 index 837d37f0137a8b9b3b29ca0e1699fe2e782ce2f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12302 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 NP59t?=4axW{{T3s#rgmM diff --git a/fastapps/auth/__pycache__/decorators.cpython-313.pyc b/fastapps/auth/__pycache__/decorators.cpython-313.pyc deleted file mode 100644 index 3d0dc08fdeb5fb266cbf08ec9beb4d4d414494c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4177 zcmcgvO>Er86&@~Et2J%Qf0E-kRyv~HMq8|RRl!YL!8WQUHCiW;jjV(kkem%g4z;wn zBr`)=-Wn==6wr$gEs$H?TZEuUj=lBTqXYz~%2R-zdXpiy36M+Qn<3Y`s}0}=X$g?a zpZDI(eDmJ-zCAd8+#qm#eEO@_ofCxo10V8ZB)d6q;pX?GK{#n>jlx^n%>pmn)VY4M z$cvOTjx>rZ`buGC1Re0aQfQ2vC-;i?G+t_yR*LXEiqCv>sBJm3EwAYn!sT!_oxS{Wd8(y$RVO;vo8@H)ocEY{5)394$FQ|x^?YY^6YQ{ei zql%su-}XeDmTyHkPs>k>i=Buqq$Od85fiqCX4#>`FP;Z0aCr}b)t0kQ2;CC70w4n< z<$J|ITSiNHERr8)QKqvu)-ggBNl<9$ZL*}9WT}u_T!QCIq*Ao5fxGdpS}nab4`>cT zfjGu3Ph=1Plnn-;3@*FPV#~2&OQI!l0URhGoI#ldpRLW*j75M>Fn%9!WdX#u0k(mS zuI?ZZpwJKdU z6BypCQgZ`#j?Lk`&AS!D`1u2?9r;Yf{wYqWnFpFyA+CL$FV4pHEtoH4TFmU2Ilq!A zI{hYoe*?sXbiDk(0Is`ysAgfU zF<7Q+{(?c>SLs6pItOBN%G@+k-S!Wc?-{7TgZJQaUBmKSB7RnqP>|X+EiX`~KnNvu z0^hp?&?pSEXy~;<9piw9I+h3)@WNtR3NcYDW6i$&O^>N(wRP#K zW?sNep@?g{z{}t%-35+9AEonk%pp5=YZD)X*eP_?UmXj{IxUh7)C2BV4@xP1AD>`hK>>7SL)@4YhhhpSKC z#rt!ge}DGR?qB@B7IvrJ+M#dbyUD#1r?-p)@F5GZ0&Fg68N&M3s?WA{xf+hUAr~i* zEZ>I=KaFzg z?c)PzPPO3#5AMqkP0TPT2s4mLXKvo@^E0y>Ws;28#;c9?(y zkVr!cfxJ`73KT>Yv&FYKDlQoz&phdDD(W*Srlp{5W(C3+j7T>^zAmbC6%xOLIipxRms1Qhj_1l5^#)~Pu%e*erP`E}9#Al#cd*&3 zK77Mx4=~4Hywu#x<6A7YY)Q32ke{n2oG>02L1eMEm$bW^Cgpw)z<@ZmYSCAg=$d!; zLm%*?8G|50dW%$evpU=iLwD6rrizAJ=1B$C%ckYrw*s3vs62tkp(^lBv@FidyNCQi zP~n{N<01ti33Q)Flq=5psL%3im)Kn%q@-sLS)7>%m(#uPv5kRwZ5oHWTM-yV>KzAe zzKPz!{?{lDRXro1df*Swfa)nNDDLF1z=$0GpK@&6{5W1t!6nl#vdzoxpJMBKUJ66B z#|P+k9WGLPn1{5-eu<2YKfbeb?A5)A$&b!{a^ch2-D9uskLXi|)FJz2a`McUAqjVZ zpT(J+!;r|e?{^hP-z+$VZ@1702^|&3n_5{(4)}R?_QfSfMO&yGNk^MaC$yW*v;=er zgvoJij@zSjkuG8`RgP>Ta`bJYB#YET<*V(`Nqlyle;0hP`G{Y_Wnb4c?P*~|D}FIX zwCP>)+S5`+oA_}0$(4`vKmKI*{N;Z^$C}u`qfKZh9*;jG@bh$Lw$I|<1fJzk{{c66 Bb`t;q diff --git a/fastapps/auth/__pycache__/verifier.cpython-313.pyc b/fastapps/auth/__pycache__/verifier.cpython-313.pyc deleted file mode 100644 index 0e6c9cad991ba4559bd7f7a08569da506eb1e50a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5420 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 KqyJ1DqyG!7a8wci diff --git a/fastapps/builder/__pycache__/__init__.cpython-311.pyc b/fastapps/builder/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index f94115bdb6248c3b6fd22d9f26363d0b876f95ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 360 zcmZ3^%ge<81h*{SXG{gsk3k$5V1hC}D*+kP8B!Rc7*ZHhm~t3%nWC6-nWLB)8B&;2 zSkjrISW;Mn88q3dl*2Pq(o;(ml1ejkQc{Z)((;QG+;TFLvlY^c5_40_^NX_eUNQnr z(`35E3sLI?QF==di5rw!T$)o-#0*qe!~!JzG+A%4$H%ASC&$O%;z-WVEy&DCEkekH zBolLT;^S8`dyU!b1`atwsjEy#rNq0ZHh zkI&4@EQycTE2#X%VUwGmQks)$SHul85#;7#BOvjCnURt427~4W2F(WyW*4xb2i#H@ RWb`j^88oniU=b%!8vx{8XQ2Q9 diff --git a/fastapps/builder/__pycache__/__init__.cpython-313.pyc b/fastapps/builder/__pycache__/__init__.cpython-313.pyc deleted file mode 100644 index de287eee40b1fc45fe14e19ddccfc0babc86d8b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 331 zcmey&%ge<81gnL9XG{Uok3k$5V1hC}D*zc&8G;##7=jstnYixkrGixk{)GLy3v(uxvuQ_J&jY7HOAv`0lv-SxQ&PkXR9M6UB>Xg4Z?VV6r{pKc$KT>e&d)8#%tE8+$k337C?7?AkD%*e=ilR@)2gV_Ua RsS7gt7q|=>*^4-VQUC~+T@e5P diff --git a/fastapps/builder/__pycache__/compiler.cpython-311.pyc b/fastapps/builder/__pycache__/compiler.cpython-311.pyc deleted file mode 100644 index a062e8487e00f5206f4f32cc69a188a33377d1d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5277 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 diff --git a/fastapps/cli/__pycache__/main.cpython-312.pyc b/fastapps/cli/__pycache__/main.cpython-312.pyc deleted file mode 100644 index a60a2c305657bbdc4d28cac85aa9f10362f2f2fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8371 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 diff --git a/fastapps/cli/commands/__pycache__/create.cpython-313.pyc b/fastapps/cli/commands/__pycache__/create.cpython-313.pyc deleted file mode 100644 index e8ced6d52b6067c929b964a14a48d83496cde707..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9464 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 I@=gT)KN^K%k^lez diff --git a/fastapps/cli/commands/__pycache__/dev.cpython-312.pyc b/fastapps/cli/commands/__pycache__/dev.cpython-312.pyc deleted file mode 100644 index fca501ce0f2cdf604008130ab32e63c5e752a0aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7996 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 diff --git a/fastapps/cli/commands/__pycache__/dev.cpython-313.pyc b/fastapps/cli/commands/__pycache__/dev.cpython-313.pyc deleted file mode 100644 index 67414b681aa6532090f5b0e810c6b300ac31fcf6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8599 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 TPxi2ysGGGLCPpB!z^p+Dk(xoL0%&A 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 diff --git a/fastapps/cli/commands/__pycache__/use.cpython-313.pyc b/fastapps/cli/commands/__pycache__/use.cpython-313.pyc deleted file mode 100644 index c641d11bfc61eb1c0594e5275a630682adfecda5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5910 zcma)A-ER}w6(2hjd$652fqW7IHv!i1Zeo`NmXdA)CLe47lWdH)AQM@GeUr?_o^kJt z0gu{lR;skDcvx2Ls;H_e`bPT~^iOEtY%x_*i>~OFR_Yrn+o!(t+?l!KaiGK##WVNb z-#Pc3Gw0sM#yCt0Vru>JwPk3@*Lx>*GPp4Ika!Ro%%6T+_ z68SumWDRc+QIix|MVf*ZMO90bN-8Q5u@nddMA|}mypD2Wp^#;Y z#sUbgB34y#15YDWBjaXI9+wJArHExME9K38G=r`wGPZ_`N*)(j_vl7Rn>ekE+C$>K ztg7LXI!%2CsmU{e=(IhJWqnpGN!cx2fk8!$ZD0+{cg7;gh3m_!ONp!5#L80k%EHY^ z)D*Iudb7y#%EGn8(jI{((_xC{wnE-G=lsIT)#aPXg=^QdOY`>3=Je7!k`)a>%*HY- zXv~;#)NI^%^IU7T+cd&Nlpr-e77Jy(K!`%dMw61NO7aE*-zB2RG}P#h2ru_`0YmKe zMq?Z`J3HP$G}%6CMXf2`5CoPeV|M6g_WI%uHsD>!zapPwFEhfK~B6YN}7|V zF;bST40B}}Ol@YHl`eEZ#pDhq*~W`fXEKcjB;Z;Dn=42VsTs?bMz@)eGv=?WlcnNJ zkv*AlX);|a6B#iNVj8}u1#X-`Wr>E<{D;;@P$uW_C30EE{D$btujM0OpX}Q$6;R zFWv5BDdc6^$6DfV_l;yPvk~5tqebl9SFX<`*O+gAx&DUSKCvBW89CT8@^H(@p_Y+H zT1F1Hj6B*h@>t7A)G~6UW#nkf$m1;|Pqd6Y*)r0#8|{;I@3gEl(lTU!$!kFA-p`3aP|0 zaVnr+$W(Blpxj+s0qP1|06zUNfuuTo*d(wOrA*kIh$xB%;w^fb1FaS*t)^&Htk|$cJz%*N2i|i%>aF`r zA_4xO)&+b|QnjqIr3bTFDwNH}N|idNhaF~A%$1-^?`DquP$V)iwrKAoD#oyjiU?xIWkyFC4 zY9yk^8Zynrf-TWZ;3N8(2Hu&YshF_knz(pGA9H+}ilHiBdeT{+uZz}mE)Lhe;s@02mltIn&ghpe{sB2`*MfYbD+6fNVg2TH)_l|I+ zCLDRRY6u8`y+^7?}HsL_@$;6Xm)$e~)?E{e%>+ZYo zXCd)i=-m-eO+d!Tq#;bPv4Pl=pFC0iaHHC{Xb6|U(ypFr&pS`Uk?jw6{o&6){p`~n z|3J+@u-kuZr$1Wjk3Q{x?|x`^@c5GtYJ+F*cS8T;AJ_V)?uY&v9Q@6#XQ4x1Ccj8l z58pCEYu|*2pLO;Bvg50cN3zj1@lAM$H48@9doLLDq7j;NShudS)7{wmD^y|`Ff7KDFxl4-o{3C z_}0?hH>r?jb(B3)5~V7N3}0}HS&X)sdlTsZ%Sk8vL_K5}ZYs?fL=MAmJxsSUm+Ryh z^b!jC7=E_7mtCAN@M!v}|Kzjq(1V{=h2a9+Mr2@xr`8MD79gq{UiF2E@W zXL?j`0~TUh`a8~4X89s~0Owle0-hx&0ZE-yPr=X2Hjl^i7jF6=+`wNs^tV9wgXp&Z grSE7jU*)?0ebKYv@pQdh@rFIUk8}Uw;AX1)AB)3(zW@LL diff --git a/fastapps/core/__pycache__/__init__.cpython-311.pyc b/fastapps/core/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index b899714b4e38cf14489829daecd5a2c000e35d96..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 374 zcmZ3^%ge<81bZyrXG{aqk3k$5V1hC}YXKS48B!Rc7*ZHhm~t3%nWC5&L2Tw6=3JI2 z79g7?g*BZiiZz8Tm_d`hO4d2QC{@8NCo?%)A+0DeH?=&!C|e;nKczG$wOH>ZBhWle z##>xYiN&emnJMY1B|siOgyriT5S&_6mReNA3{+Lb0wnx2*>17N$EV~c$H(7dD+g-; z%idxuhA0K80x3z%$%&6&$?zG-W%y;M9}1K%)~_th%cxAv&(?QIEzT~lIY~;;?}@*sh2NXdKAR#dbjA12ZEd;|&Jo3k=E+800RX bB8WIp3XUFdi(in`y1=E~zz%{%px_1o&jV=3 diff --git a/fastapps/core/__pycache__/__init__.cpython-312.pyc b/fastapps/core/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index f46b814d3bb20a83ed382ebcca15fc674b12a777..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 340 zcmXw#K}*9h7>1L!Yo$7NlpU19E>@xW10v{PH%0Iu^b$goZe@)rN#~q5e}woO{4HK~ zwIB%M!JE*VC%>%v9o{@IdGqm-mwrD)Fl5lq9^w43=6{?&Xb%9M5JxHEm^&qwPU>LG zZs|%d^?*Izn>uN4<;TJO-dJf7P0AvhN4aKFER~)|Qt?G8O!9?cTa26Vj2UrV@TsW5 z4LUj>UtS4)E3|eYr9F^~*lz+#xymSQ{N?7s=C<)o7quxHVP#3_Git%2pE2RAW}Re8 zCbiIVaYyn3I*^%?f=rmHPpit19DrHKpf{nkxG8E%lWNs$9e1ry^bYK_#mx}p+r=2a YqR}VXe?z19?cMe8a2*~!1loS)FA-5*8UO$Q diff --git a/fastapps/core/__pycache__/__init__.cpython-313.pyc b/fastapps/core/__pycache__/__init__.cpython-313.pyc deleted file mode 100644 index ce54912cd277d6af6afcf2f2cf333a6b93822df2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 336 zcmey&%ge<81gnL9XG{gsk3k$5V1hC}YXBKj8G;##7=jstnY8T|^9zTTT>l_fAT2z)=RKyHaRm1`${508avB$@!?IWT2b%>ry|&gMZ*J_3Fu+ zXHQPHAP&sSd&!&0lk@pB!n%9^-oD}a8N~mhD6X*mIf9Brq==%1G*nO$cx>V(5ef1{ zO*T|a6kA1k`g&Gar?aK0$~s#++316Hb=Fw5H`*`02yV;CFujppKUZp_J5;A5yDuJB z+P!G!!sjkVc*xn10w`Mo4D4_mIKB$zM{95ei!=t{BXVgN4e(R&C-ivU?|0i?+nV2M tUw5|UYw0`Lww^DMTnF-ZQ(|}yQ0=M?VA1x&$;BA`G6eSwA)zxeqrYsGRY?E< diff --git a/fastapps/core/__pycache__/server.cpython-311.pyc b/fastapps/core/__pycache__/server.cpython-311.pyc deleted file mode 100644 index 82b1164bfc74e61c89d137cf8036f02a59e2e948..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9228 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 ll*lUu^HU=Jg83>*qm(Vfe*-cTR7C&) diff --git a/fastapps/core/__pycache__/server.cpython-313.pyc b/fastapps/core/__pycache__/server.cpython-313.pyc deleted file mode 100644 index 690bf2999b259f74df42592edc6df0bb162437be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15059 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 diff --git a/fastapps/core/__pycache__/widget.cpython-313.pyc b/fastapps/core/__pycache__/widget.cpython-313.pyc deleted file mode 100644 index 94aecc7f4ca615758f8324538d310e09d62fd0e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10907 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%0At6QTzlxt_R!vZKG2p62-$#;>CFWq z$AL(`qxa8!&+J^sIAB}+LNSmH3>-NJsPV4~ka3~t89{vg-wF7N2#dxU!*fX$K04{b zot-!&rNQ(=3Q$g1tE=)C4kRJ_>IuS zE0xP#+xT}ds@4I;F85WSg{?&{FqsWZz9`Veb3^6km~E(g&g!2LprU1|xqw;+Gl&+^ z3N6kWW~g4Uh_Gh<}P zn8AiEn@}g+XOeXo`Ub#BmNbbJk!8B~TiWG#y2ET{kb*_Bh%`(67{Hp7RDiDWRLq3d zhDTD>BxZn5Ofk5ZsaX2eiw`HVG)JGXn=e; z=90;~NLt6@H7F`Un&Cs0L_t~P+*x58s#^XYJK{Jrb{-zfoqH*J?xlQq^3$!STlw*c zPgkF==10zhsmOx07@zp6u`k1a;(y{V*sMMNXFgY>dyjwK$$4A$r;fTpInQ|3GoFWW zhF{GNzq)tzsA(We&4{-#=jn%EerPl|bSXP@Y47TOf7W{z&c~kTv>m{+!0Z4$4;MHU z(J~NguNuv&ZH78%mW66oU9?^54;~WE2Al$ux6YX=@v>7;hXeZ4NEqf?=k(R|ufqZT zY2*z3ou@^q>0gHf`d6N!SC#@Y-{kb~h; zzLaS@bXrb#R~>*mGz+F(p+Qr+v#Jd~(3b+aVk(ghbd|n`skohBP(*%!#rLsz8;kn- z+=N<-5eiDfbh@WTr>k3GDD8bH&;mE@@p(_+!`DB29aQ;~A3gaI>g8b8I|S66=N$a< z6Vpf=&w9g@HjwiSAtoAdPM$HKX>SF>YOzlXEl5!zBuyYuz6 z8*MDr?tg%Xk2w^cUG?HY_`fAf1xFpN8$g>}N1I+`>z5F})?6?`J*&ypd~^Igcq6&(R{Hn-GcuU6v06Cymo(Q zjbJtcLRK4?NW-}W+3Dq-ZQ0dA(Tv77%y%!DdSbLL zL%(7TFo6!Sjm8XX9J(4jzL<@Rx^l|vx1rV*B`T?(A=GHzDGvNsF%6-`F8411fIH`Y z5jdA`I`dC$-MO~NFWMsc*3Mk(xoqpXd{-F0ndI7rvu(qR(E z@4uP%_}^d1H@ENad=xq6&*nS2c5m*t9P?fI*1_HDd$D7FFyGR*du>lV=KBE0_75KM zZTtE$Kad{{9r5ifw|dNnz)0*a>^l!y4`P|AL)(*A4%BSdc(yTo%unYBMvnNdgQ;VF zBtJN^dwc)V!Q4levDV(bdt(nq>jvoS@)c~Hy?y^thR=vkT-lB>%*Hz4rDytg7x(92 z0?&Dy{bIq*Ia+}~bOBuHGgpV*_f>0e!47v|ZSFo8&o+$~8nEi*0v-GBWc`B$>$)jpjW%4mmknMY5; z&tX2(1!tSR^+3YeP%ZR%?Cwm8frD>2sIY7@ zZbmR**rc@>K{H3U+;G4t1!u!HoGJ^|9rR-Gh1~?|ROJlUR%|;=4>h3*Koy0m4Bc#u z4cMD#xbA$Fec*qM{1h5s|2YkL3pR_z@@sDJ*IekB3;l}g`xV#qjpLGK=otsaHxDcW YmhNX96wendx2+cUe#>t-ywj2Y3+7VzT>t<8 diff --git a/fastapps/core/__pycache__/widget.cpython-39.pyc b/fastapps/core/__pycache__/widget.cpython-39.pyc deleted file mode 100644 index 8714f07c3a191b841f18767586716ac9fef44f8e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8287 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 diff --git a/fastapps/types/__pycache__/__init__.cpython-313.pyc b/fastapps/types/__pycache__/__init__.cpython-313.pyc deleted file mode 100644 index 26d38fc5e4b5551643f2bca67187e608071440e8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 313 zcmYL@y-LJD6oqFd$*yMEX2E_ewg|$GilvC+vc)Pk(Rv8UWHaPv!kw%VyL|&2A3}T? zTc;8%#7;-;iJEsD(!qB0wPy$pu~#AWH`v zib5xJ83A|Y1%vzPbz2LXh*Vaxk*d-(RX`VojB}boREj$V`Ro(hw160IAw`iG|6En6 zT+L-|elaC^&yYoqa~;l-+0nnzidHYlX_$T-spa`a*?KQQW@<9s zVs*<*%}Ke%<(!|FmYMF7nOsuD43sHi0TOQ1!&rC>Z}#ApbMdkMk;!A zczi;aTsLP*X_{K#Jv8=9O?8}!iqBMQhNqQGHl>)u$ksV_^q#dZ*KwRzbnt=p-$s+| NXoDx<(5|Hr)B1(7VBr^=cekrB^H-B78Dfgm!uY#6zj*wXXa&=#K-FuRNmsS$<0qG a%}KQ@Vg;JT2*kx8#z$sGM#ds$APWGjfF<_; diff --git a/tests/__pycache__/conftest.cpython-312-pytest-8.4.2.pyc b/tests/__pycache__/conftest.cpython-312-pytest-8.4.2.pyc deleted file mode 100644 index 95945f9d32c94de7d5fe89cafbdcfd59b0cce48f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 806 zcmZ`%J8u**5VpOWy;lwrL?qHAi-dwsif$#EL;@v86VX7E0_mES+4WtPc+V@_OLA9% zh{R8z$qxY0Q1TyWlGY6c72Tohs2F>5c_3lQ;~CHE`^Ns>ZZ{FEjpf;+C&|5pV3hm)1zdT8e!zm)x8})LXG%`cmkYCB|4)o}Ev7NX9sifj zc#&L{LC6QggFUlN01r+{>ZSgvw~smtla1#hISdt(Z@N3O4%lI7+Fel}JS1)lBB)aWu#{ zQ_+FQS+qx$ep(bNvTamk+gq($j7=*CTycdnH!-4 z^DpT7lU$@FXFGBg5W;6hY6ryB!x;ZS=mfoQ!@NoT!`i#G--O^Eo_UBYO`SSzUh`}_ KhmF~G%>Dx5!pm9! diff --git a/tests/__pycache__/test_import.cpython-312-pytest-8.4.2.pyc b/tests/__pycache__/test_import.cpython-312-pytest-8.4.2.pyc deleted file mode 100644 index 694cb91b69fca596f9c619e03c3d727918e34b8e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10674 zcmeHNO>ErO5hi!JztqZ+Y{|B4$6ML5%tl>VvSjVHm8`UAVl;u%qDF%l0SJ1zvUaoD z-9A#*Pr0yBY@-LQ00VMPao`+;q6Z%f6g{X`1nY! zva+03g<(V#HJmq`c|XHPzIos5UlNHp2S>H-$>fs2abIGA9RY`VG6c*br*fA$l~)5r z{&K*+2B(B|_JymTq2>B%dKmd_?DO71wLV%BNXzC?ir;s42f=o)kY8|SK=!4+87eA}5j1#KS5+_s?H_7L4eJS} zQ)p$JcYWZn*Ep?1Z)5U8JL9~o!LhoDU-N*c!8lK|#6$aDt<7`;tkxa$f=o}1?AnN2 zZ|yiDR})4Q*`cR`+tcXX(0mx4Wk^5BeB-k{#AM#p2r*lB{SC~(S-y(@L3pN3=b=3( zy1ZH#x6Pc0?ac}5p{BFkebTSWoPdvBeEx*}(bFdt-TmgNd6LWOWipq{&E_@u+!6eQ zU3?ZurL5?LqUlPdqCJD}q?RLkvgdSVZbZ`Jzjm19s4W^|B48ZRAQJ)Oh|Wk_s-ozV z@oI{;6|bJgdWI!UidU^bS+hjvQ!GQbJ_}jF%G?D|7Od>3mVomozI}EN7LXX2n6O z!#X1?#5yEvljZ56DwAA=$XAQyD@sweYm3R6GLb{E)%vFG=X|;J6GFGVkd~&#?eLNfMR~22MBwL3C)cPmOQ@Q@PU?#i?Goc@cL#a#`$W&#{>a+*Ro}IgllJ7thutIX$Td;QB z;Jy?RU$h@s?7h>w*mtL|MwVxmW|prnUH^R6Jf41d@{e(|{hb@{JQ5SPMixfu!l|1h zx5gI6fLR!;yqm*?iR8u=bOJKxU zFse6J4k}Rp6L2Tj5RBl$Z3iO{Vc~wYf{|?&DhNsnIBm8Z)sPyhh9Q6n8N3mG;DlP$ zh#k^I*iu-H8qwRlrm5jw3<3VTpk}8K;AI9i|DO@LL5(kfX%^180gM`b$zfV_hn|Mn zyxKv}oqs#%MOl4n>~7o+`(iH~_QjYjyZXaE2>k^355a#J{v+@nY##pM0Ruz&VCL zA-5|q)qsm~A?H+?Va)*Jh??MIfN(M73xOmDXc_=Otk1^J1^^@j2n(^gQ6$HZybi>VGNmaN=73QH4W+XU zrD0m4c+|j%h9jkb6jGAJiVxbz5}gV*=B5D@1~?@dguCbqZ4hPyg_icU-I`gLxn(RE zwI40Nz4Z3-drR+qe!)CCyrMt6Z0@^ojn}%DdzX6a!g#HBxo@ct zn3ZEdmiq3^t{gMF&I1o1RM&Y^90zty9N!eYb3+(M#0LeY$F2hlh!0BFfCT``vj85d zsdZBs7%>)%YQ0nnMzy}L8{M0AjTj3?E5}?T=%vRuc9hCN-Nmyq%mW$bfeiCNW~H+( zWNZ&~nq6t&(F18y%mBM4W;O-y+z>M80Th@XyACXP0Htfdf(JYc;Gr5EwqwLtFhUPF zM(Ba9MmTK8h_PU_(&-vOFFn4oqf`#+t_RZ018L@gH1j}urK2vSZ4Y#qT>wU=(E}r< z2+l~aiO`65ZU|}g018ZxT?ZCCfYLQ!!2_NJ@K6m7+c9D+7@-FoBlN&lBOJD4#8@y| z>2Qspmmc5PQ7Q*@*8|_qIRI0Zbi=o!0Y-Imx<3P8+=EeRV%g5K; z0{bX5gYEpFp{I!p#+GACZuj}RZq8PJzMczb0I<7TBXYe3dA#Sh?rPe(-RJB2;7upD z8`g0hwGUrAO|r27Y&&^=c6C6$AOd`lBgRG7K8%1GV<7l2SB)5a;W)eJT@4qQtVY!U z{xKW)X+*plGh)}H*SKojh-*u}o+Z5+7^jLxe1cb7jF=If2t07+xX-59Ja8dPHL+{Y z%!N>Qd}gjDJhOz_dN*JLuA;h6ea#T_yj^N};V09=o(Q`-PbLQ5i>h&az-k-PYD{g1 zEPTumJ~|C0^lj1SrzG>JcUAY>zCD08-`uypx|=-%@4e}8JdjX3)q_UD@j&Yfd!Ut# zoL!j*U~a~%f_ez%lj8|GerIOwGQxjZc ztN^)UMVtmC-U{SP5aTCq{RMy&AQ|i#E=>*fzy=H}KW9a*<%k9uY`?wJAJ|bzZFEo( zbV@OQh5QeD6-y{rawSVBmKD`%lOY*jwDaze+^#4jC+(wKne*7+Ata6a8G;{Di=QBA zy!w*sXZ(dhe?EQ4Uuej*5Bu+oXgDfwmt%7ziOqD-Bs!ZLGJQJar*IF~3GH7jhyHh= z{fmx+i|^lgfAPaRAKvT!wD*(VPy0XV|HEms`v(s%)qng8v*Xf@i*Ar7u`pMHJbKC$ zrMe)kiqfXwog0FLAuI|^k6i~Af=QIF0Sh6lX8}A^Q%g}97%>)%>W!6y3JCuhOWZ8g z)b-ejd%t?rc4+bFouf77cgJsrp9Z=3$;YrWgNXa}o0(d;F224g;JHD|v|wU^vMvRK zTG+kKSobzCVD(6ByA9(#TKnayc(N`G-j4&Rizn~LS49{zo*RyUmQ1@u`8Cw1te=9) zBk=$_AY7YUJ%p~fnW@8-jT{|{0@pp*cgK;gm`W%G4?PvZ5v#Ei&oq36{(QLo-6O%j z9tpkM6OrOpM3&WZ7JhLOWxq19x9rwonf}BiMPFQayHX@KL-Kmgin$^y;>hUc9)Ff$ zZ=mhT^%-Xe!*mixe@OS*W8~+cgpr7b8~n#Xp6CC@^*;@BJpVEG6&L1DebpZ4zyG8w J!GG7b{}0WJBUAtY diff --git a/tests/__pycache__/test_widget.cpython-312-pytest-8.4.2.pyc b/tests/__pycache__/test_widget.cpython-312-pytest-8.4.2.pyc deleted file mode 100644 index 33e136398dda953b119779644d92bebac311b695..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4742 zcmeHK&2QX96t}(JkF4{ZrZg>u?H5^B)o$7(C8e~21Bg?5K|;#Ka(A3&Q}2gnylr>! zN(lAPLsfADq#k-ql>Pzy4_r1$qZEq+5?p#iL;|TN-t&0IPO~aO0)Yf0@0&Mo=FPnK z=GpJJ-wqAs6=?q0?s7>}lpk>*8jQoXTY~OOg(!;(QHkcNi(1&HYI;Rem0Ef+qbekI zPg%?o9cZqtndwgPV~2UHu+*T14{heX-)yqNohrHMc!i}_z2a3Hb=$3a?b35R{6ILnf5!Y3H^$V35Xp;x)d0WW{F7DSO_O`;Nya6F!2_qmz zy!YgAJvy5vH3sFhWcF*+!5)%2utk%vw#ZkcCCQb2EQwcw1u3E**n*^o{bCYF{-810 zrvVn}2|xQ;uL?g&`jOs&TkLAyhzN5+cT~vY-q(b#=s#CUrmK={H|3{%<+jq*eVu)s z*kwG{797d>`jScpP-973*SS2Q)6eRZIPqU_Imxm`KTuL?ic+yXGSC6H8MuV zeJ%1mdtl$Q!e`M7-@$(!JXXj=; zQ^(9vtdD_#R`3wa@&Q+$PtMFF2N#md=8}Vp$-&zUV_b;Zi>qb4J5IoalIPG`>u!0e z>N>2v+^9Kafegy%J_~7d?WJZrI2r~ekp`AbGzy;`1>?#dLNWSG`N^2<9WFjOy=9bp zdU?|*@96Qft(PCX!v$M0D_3mlWzd!Ll20ypa$3#{_9>|CJqMP+;m?diJ^k$cOT8m! zd&b!v9iQz;z(-p+;;f{g0t#|o@^S$%p!du;{#A9uDE9QrtJi__jNMH*8Lm?|b&A&*A{&aSK_`Li zfg}qYk}Q}5^e8TP6UpmHjvzrWOOGKrj^qs_=YYU+gbMT|l2b@dBRK;ki3|21hB^9{ zoaTVy62pJl0uL1EG|o@%tLhjxdLHI5YV3Mg(0}-QtLQ{eH}5a>UNd`!xufH=9SQho z3rC!l6jVS#&P!e{00#8FH->v7C$@}}J$?GI4WwtBd~8F7-O=&cjs$!p>4rZ2V3v=z zVpdLXg4W0hIWPIA;ci?GYaZKDLo%?0nh;oXAjn!4X;dsL$O#M@WL0YbJl`g Date: Tue, 28 Oct 2025 09:35:38 +0900 Subject: [PATCH 2/9] Added .gitignore & removed redundants --- .gitignore | 2 + CLI_COMMANDS_UPDATED.md | 456 ---------------- CLI_EXAMPLES.md | 385 ------------- CLI_UPDATE_SUMMARY.md | 400 -------------- DEVELOPMENT.md | 525 ------------------ PYPI_RELEASE_v1.0.8.md | 343 ------------ PYTHON_API_SUMMARY.md | 233 -------- RELEASE_SUMMARY.md | 309 ----------- fastapps.egg-info/PKG-INFO | 720 ------------------------- fastapps.egg-info/SOURCES.txt | 145 ----- fastapps.egg-info/dependency_links.txt | 1 - fastapps.egg-info/entry_points.txt | 2 - fastapps.egg-info/requires.txt | 16 - fastapps.egg-info/top_level.txt | 1 - setup.py | 2 +- test_cli_auth.py | 280 ---------- test_cli_simple.py | 193 ------- test_dev_command.py | 203 ------- 18 files changed, 3 insertions(+), 4213 deletions(-) create mode 100644 .gitignore delete mode 100644 CLI_COMMANDS_UPDATED.md delete mode 100644 CLI_EXAMPLES.md delete mode 100644 CLI_UPDATE_SUMMARY.md delete mode 100644 DEVELOPMENT.md delete mode 100644 PYPI_RELEASE_v1.0.8.md delete mode 100644 PYTHON_API_SUMMARY.md delete mode 100644 RELEASE_SUMMARY.md delete mode 100644 fastapps.egg-info/PKG-INFO delete mode 100644 fastapps.egg-info/SOURCES.txt delete mode 100644 fastapps.egg-info/dependency_links.txt delete mode 100644 fastapps.egg-info/entry_points.txt delete mode 100644 fastapps.egg-info/requires.txt delete mode 100644 fastapps.egg-info/top_level.txt delete mode 100644 test_cli_auth.py delete mode 100644 test_cli_simple.py delete mode 100644 test_dev_command.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0c81efd --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.vscode/ +*.egg-info/ \ No newline at end of file diff --git a/CLI_COMMANDS_UPDATED.md b/CLI_COMMANDS_UPDATED.md deleted file mode 100644 index a5b24a2..0000000 --- a/CLI_COMMANDS_UPDATED.md +++ /dev/null @@ -1,456 +0,0 @@ -# FastApps CLI Commands - Updated for v1.0.7 - -## Overview - -The FastApps CLI has been updated to support OAuth 2.1 authentication features with new flags and commands. - -## Commands - -### `fastapps --version` - -Show version information. - -```bash -$ fastapps --version -fastapps, version 1.0.7 -``` - ---- - -### `fastapps init ` - -Initialize a new FastApps project. - -**Usage:** -```bash -fastapps init my-project -``` - -**Creates:** -- `server/main.py` - MCP server with auto-discovery -- `server/tools/` - Widget backend directory -- `server/api/` - Shared APIs directory -- `widgets/` - React components directory -- `requirements.txt` - Python dependencies -- `package.json` - JavaScript dependencies -- `.gitignore` - Git ignore rules - -**Server main.py includes:** -- Commented OAuth configuration example -- Auto-discovery code -- Build and server setup - ---- - -### `fastapps create ` ⭐ UPDATED - -Create a new widget with optional authentication configuration. - -#### Basic Usage (No Auth) - -```bash -fastapps create mywidget -``` - -**Generates:** -- `server/tools/mywidget_tool.py` - With commented auth examples -- `widgets/mywidget/index.jsx` - React component - -#### With Authentication Required - -```bash -fastapps create mywidget --auth --scopes user,read:data -``` - -**Generates widget with:** -```python -from fastapps import BaseWidget, ConfigDict, auth_required, UserContext - -@auth_required(scopes=["user", "read:data"]) -class MywidgetTool(BaseWidget): - identifier = "mywidget" - title = "Mywidget" - description = "Requires authentication (user, read:data)" - - async def execute(self, input_data, context, user): - if user and user.is_authenticated: - return { - "message": f"Hello, {user.claims.get('name', 'User')}!", - "user_id": user.subject, - "scopes": user.scopes, - } - return {"message": "Welcome to FastApps"} -``` - -#### Public Widget (No Auth Required) - -```bash -fastapps create public-widget --public -``` - -**Generates widget with:** -```python -from fastapps import BaseWidget, ConfigDict, no_auth - -@no_auth -class PublicWidgetTool(BaseWidget): - identifier = "public-widget" - description = "Public widget - no authentication required" - - async def execute(self, input_data, context, user): - return {"message": "Welcome to FastApps"} -``` - -#### Optional Authentication - -```bash -fastapps create flexible-widget --optional-auth --scopes user -``` - -**Generates widget with:** -```python -from fastapps import BaseWidget, ConfigDict, optional_auth, UserContext - -@optional_auth(scopes=["user"]) -class FlexibleWidgetTool(BaseWidget): - identifier = "flexible-widget" - description = "Supports both authenticated and anonymous access" - - async def execute(self, input_data, context, user): - if user and user.is_authenticated: - return { - "message": f"Hello, {user.claims.get('name', 'User')}!", - "user_id": user.subject, - "scopes": user.scopes, - } - return {"message": "Welcome to FastApps"} -``` - -#### Options - -| Flag | Description | Example | -|------|-------------|---------| -| `--auth` | Require OAuth authentication | `--auth` | -| `--public` | Mark as public (no auth) | `--public` | -| `--optional-auth` | Support both authenticated and anonymous | `--optional-auth` | -| `--scopes` | OAuth scopes (comma-separated) | `--scopes user,read:data,write:data` | - -**Notes:** -- Only one auth flag can be used at a time -- `--scopes` can be used with `--auth` or `--optional-auth` -- Without any flag, widget includes commented auth examples - ---- - -### `fastapps auth-info` ⭐ NEW - -Show authentication setup information and examples. - -**Usage:** -```bash -fastapps auth-info -``` - -**Output:** -``` -FastApps Authentication Guide - -Server-Wide Auth: - Configure in server/main.py: - server = WidgetMCPServer( - name='my-widgets', - widgets=tools, - auth_issuer_url='https://tenant.auth0.com', - auth_resource_server_url='https://example.com/mcp', - auth_required_scopes=['user'], - ) - -Per-Widget Auth: - Create authenticated widget: - $ fastapps create mywidget --auth --scopes user,read:data - - Create public widget: - $ fastapps create mywidget --public - - Create optional auth widget: - $ fastapps create mywidget --optional-auth --scopes user - -Decorators: - @auth_required(scopes=['user']) - Require authentication - @no_auth - Public widget (opt-out) - @optional_auth(scopes=['user']) - Works both ways - -UserContext: - Access authenticated user in execute(): - async def execute(self, input_data, context, user): - if user.is_authenticated: - return {'user_id': user.subject} - -Documentation: - Server auth: docs/08-AUTH.md - Per-widget auth: docs/09-PER-WIDGET-AUTH.md -``` - ---- - -### `fastapps dev` - -Start development server with hot reload (planned feature). - -**Usage:** -```bash -fastapps dev -``` - -**Current status:** Not yet implemented. Use `python server/main.py` instead. - ---- - -### `fastapps build` - -Build widgets for production (planned feature). - -**Usage:** -```bash -fastapps build -``` - -**Current status:** Not yet implemented. Use `npm run build` instead. - ---- - -## Complete Examples - -### Example 1: Create Project with Mixed Auth - -```bash -# Initialize project -fastapps init my-auth-app -cd my-auth-app - -# Install dependencies -pip install -r requirements.txt -npm install - -# Create protected admin widget -fastapps create admin-dashboard --auth --scopes admin - -# Create public search widget -fastapps create search --public - -# Create flexible content widget (freemium) -fastapps create content --optional-auth --scopes user - -# Build all widgets -npm run build - -# Configure OAuth in server/main.py -# (Edit auth_issuer_url, auth_resource_server_url, etc.) - -# Start server -python server/main.py -``` - -### Example 2: Quick Widget Creation - -```bash -# Simple widget (no auth, includes examples) -fastapps create hello - -# Auth-required widget with multiple scopes -fastapps create protected --auth --scopes user,read:data,write:data - -# Public API widget -fastapps create api-search --public - -# Premium features widget -fastapps create premium --optional-auth --scopes premium -``` - -### Example 3: Get Help - -```bash -# Show all commands -fastapps --help - -# Show create command help -fastapps create --help - -# Show auth information -fastapps auth-info - -# Show version -fastapps --version -``` - ---- - -## Output Examples - -### Creating Widget with --auth - -```bash -$ fastapps create admin-panel --auth --scopes admin,write:data - -[OK] Widget created successfully! - -Created files: - - server/tools/admin_panel_tool.py - - widgets/admin-panel/index.jsx - -🔒 Authentication: Required with scopes: admin, write:data - -Next steps: - 1. npm run build - 2. python server/main.py - -Your widget will be automatically discovered by FastApps! -``` - -### Creating Widget with --public - -```bash -$ fastapps create search --public - -[OK] Widget created successfully! - -Created files: - - server/tools/search_tool.py - - widgets/search/index.jsx - -🌐 Authentication: Public (no auth) - -Next steps: - 1. npm run build - 2. python server/main.py - -Your widget will be automatically discovered by FastApps! -``` - -### Creating Widget with --optional-auth - -```bash -$ fastapps create content --optional-auth --scopes user - -[OK] Widget created successfully! - -Created files: - - server/tools/content_tool.py - - widgets/content/index.jsx - -🔓 Authentication: Optional (scopes: user) - -Next steps: - 1. npm run build - 2. python server/main.py - -Your widget will be automatically discovered by FastApps! -``` - -### Creating Widget without Auth Flags - -```bash -$ fastapps create mywidget - -[OK] Widget created successfully! - -Created files: - - server/tools/mywidget_tool.py - - widgets/mywidget/index.jsx - -ℹ️ Authentication: Not configured (will inherit from server) - -Next steps: - 1. npm run build - 2. python server/main.py - -Your widget will be automatically discovered by FastApps! - -Tip: Use --auth, --public, or --optional-auth flags for authentication -Example: fastapps create mywidget --auth --scopes user,read:data -``` - ---- - -## Migration from Previous Version - -If you have existing widgets created with an earlier version: - -### Before (v1.0.6) -```bash -fastapps create mywidget -# Then manually add decorators in the generated file -``` - -### After (v1.0.7) -```bash -# Directly create with auth configuration -fastapps create mywidget --auth --scopes user -# Or -fastapps create mywidget --public -# Or -fastapps create mywidget --optional-auth --scopes user -``` - -**Note:** Existing widgets continue to work without modification. The new flags are optional. - ---- - -## Quick Reference - -### Command Cheat Sheet - -```bash -# Initialize new project -fastapps init my-project - -# Create widgets -fastapps create name # No auth (commented examples) -fastapps create name --auth --scopes s1,s2 # Auth required -fastapps create name --public # Public (no auth) -fastapps create name --optional-auth --scopes s1 # Optional auth - -# Get help -fastapps --help # All commands -fastapps create --help # Create command help -fastapps auth-info # Auth guide - -# Version -fastapps --version # Show version -``` - -### Common Patterns - -```bash -# Admin-only widget -fastapps create admin --auth --scopes admin - -# User data widget -fastapps create profile --auth --scopes user,read:profile - -# Public search -fastapps create search --public - -# Freemium content -fastapps create article --optional-auth --scopes user - -# Premium features -fastapps create premium --optional-auth --scopes premium,user -``` - ---- - -## Documentation References - -- **CLI Usage**: This document -- **Server-Wide Auth**: `docs/08-AUTH.md` -- **Per-Widget Auth**: `docs/09-PER-WIDGET-AUTH.md` -- **OAuth Testing**: `OAUTH_TESTING_GUIDE.md` -- **Testing Summary**: `TESTING_SUMMARY.md` - ---- - -**Version**: 1.0.7 -**Last Updated**: January 2025 -**Status**: ✅ All commands implemented and tested - diff --git a/CLI_EXAMPLES.md b/CLI_EXAMPLES.md deleted file mode 100644 index af9ecf2..0000000 --- a/CLI_EXAMPLES.md +++ /dev/null @@ -1,385 +0,0 @@ -# FastApps CLI - Complete Examples - -## ✨ What's New in v1.0.7 - -### Enhanced `create` Command -- `--auth` flag to require authentication -- `--public` flag to mark as public -- `--optional-auth` flag for flexible auth -- `--scopes` flag to specify OAuth scopes -- Auto-generates appropriate decorators and code - -### New `auth-info` Command -- Quick reference for authentication setup -- Shows examples and documentation links - -### Updated Version -- Now reports v1.0.7 - ---- - -## Command Reference - -### Help Output - -```bash -$ fastapps --help - -Usage: fastapps [OPTIONS] COMMAND [ARGS]... - - FastApps - ChatGPT Widget Framework - - Build interactive ChatGPT widgets with zero boilerplate. - Supports OAuth 2.1 authentication for secure widgets. - -Options: - --version Show the version and exit. - --help Show this message and exit. - -Commands: - init Initialize a new FastApps project. - create Create a new widget with tool and component files. - dev Start development server with hot reload. - build Build widgets for production. - auth-info Show authentication setup information. -``` - -### Create Command Help - -```bash -$ fastapps create --help - -Usage: fastapps create [OPTIONS] WIDGET_NAME - - Create a new widget with tool and component files. - - Examples: - fastapps create mywidget - fastapps create mywidget --auth --scopes user,read:data - fastapps create mywidget --public - fastapps create mywidget --optional-auth --scopes user - - Authentication options: - --auth: Require OAuth authentication - --public: Mark as public (no auth) - --optional-auth: Support both authenticated and anonymous - --scopes: OAuth scopes to require - -Options: - --auth Add auth_required decorator to widget - --public Add no_auth decorator (public widget) - --optional-auth Add optional_auth decorator - --scopes TEXT OAuth scopes (comma-separated, e.g., 'user,read:data') - --help Show this message and exit. -``` - ---- - -## Usage Examples - -### Example 1: E-commerce App - -```bash -# Initialize project -fastapps init shop-widgets -cd shop-widgets -pip install -r requirements.txt -npm install - -# Public product catalog -fastapps create product-catalog --public - -# User cart (requires auth) -fastapps create shopping-cart --auth --scopes user,read:cart - -# Checkout (requires auth + write) -fastapps create checkout --auth --scopes user,write:orders - -# Product reviews (optional auth - better when logged in) -fastapps create reviews --optional-auth --scopes user - -# Admin panel -fastapps create admin-panel --auth --scopes admin - -# Build and run -npm run build -python server/main.py -``` - -**Result:** 5 widgets with appropriate auth: -- 🌐 product-catalog - Public -- 🔒 shopping-cart - Auth required -- 🔒 checkout - Auth required -- 🔓 reviews - Optional auth -- 🔒 admin-panel - Admin only - -### Example 2: Content Management System - -```bash -fastapps init cms-widgets -cd cms-widgets -pip install -r requirements.txt && npm install - -# Public article viewer -fastapps create article-viewer --public - -# Article editor (requires auth) -fastapps create article-editor --auth --scopes user,write:articles - -# Comment system (better when authenticated) -fastapps create comments --optional-auth --scopes user - -# Media library (requires auth) -fastapps create media-library --auth --scopes user,read:media - -# Analytics dashboard (admin only) -fastapps create analytics --auth --scopes admin - -npm run build -# Configure OAuth in server/main.py -python server/main.py -``` - -### Example 3: SaaS Dashboard - -```bash -fastapps init saas-app -cd saas-app -pip install -r requirements.txt && npm install - -# Public landing page -fastapps create landing --public - -# User dashboard (requires auth) -fastapps create dashboard --auth --scopes user - -# Settings panel (requires auth) -fastapps create settings --auth --scopes user,write:profile - -# API key manager (requires auth) -fastapps create api-keys --auth --scopes user,write:keys - -# Billing (requires auth + billing scope) -fastapps create billing --auth --scopes user,read:billing - -# Team management (admin) -fastapps create team --auth --scopes admin,write:team - -npm run build -python server/main.py -``` - ---- - -## Step-by-Step Walkthrough - -### Creating Your First Authenticated Widget - -```bash -# 1. Create project -fastapps init my-app -cd my-app - -# 2. Install dependencies -pip install -r requirements.txt -npm install - -# 3. Create authenticated widget -fastapps create user-profile --auth --scopes user,read:profile -``` - -**Output:** -``` -[OK] Widget created successfully! - -Created files: - - server/tools/user_profile_tool.py - - widgets/user-profile/index.jsx - -🔒 Authentication: Required with scopes: user, read:profile - -Next steps: - 1. npm run build - 2. python server/main.py - -Your widget will be automatically discovered by FastApps! -``` - -**Generated code** (`server/tools/user_profile_tool.py`): -```python -from fastapps import BaseWidget, ConfigDict, auth_required, UserContext -from pydantic import BaseModel -from typing import Dict, Any - - -class UserProfileInput(BaseModel): - model_config = ConfigDict(populate_by_name=True) - - -@auth_required(scopes=["user", "read:profile"]) -class UserProfileTool(BaseWidget): - identifier = "user-profile" - title = "User Profile" - description = "Requires authentication (user, read:profile)" - input_schema = UserProfileInput - invoking = "Loading widget..." - invoked = "Widget ready!" - - widget_csp = { - "connect_domains": [], - "resource_domains": [] - } - - async def execute(self, input_data: UserProfileInput, context=None, user=None) -> Dict[str, Any]: - # Access authenticated user - if user and user.is_authenticated: - return { - "message": f"Hello, {user.claims.get('name', 'User')}!", - "user_id": user.subject, - "scopes": user.scopes, - } - - return { - "message": "Welcome to FastApps" - } -``` - -### 4. Build and Run - -```bash -# Build widgets -npm run build - -# Configure OAuth in server/main.py -# Edit: auth_issuer_url, auth_resource_server_url, auth_audience - -# Start server -python server/main.py - -# In another terminal: expose with ngrok -ngrok http 8001 - -# Add to ChatGPT -# Settings → Connectors → Add Connector -# URL: https://YOUR-URL.ngrok-free.app/mcp -``` - ---- - -## Comparison: Before vs After - -### Before v1.0.7 - -```bash -# Create widget -fastapps create mywidget - -# Manually edit server/tools/mywidget_tool.py to add: -# - Import auth_required -# - Add @auth_required decorator -# - Update execute signature -# - Add user context handling -``` - -### After v1.0.7 - -```bash -# Create widget with auth in one command -fastapps create mywidget --auth --scopes user,read:data - -# Everything is generated automatically! -``` - -**Time saved:** ~5 minutes per widget - ---- - -## Tips & Tricks - -### 1. Use auth-info for Quick Reference - -```bash -fastapps auth-info -``` - -Shows all auth patterns and examples without needing to check docs. - -### 2. Specify Scopes Clearly - -```bash -# Good: Clear, specific scopes -fastapps create editor --auth --scopes user,write:documents - -# Also valid: Single scope -fastapps create viewer --auth --scopes user -``` - -### 3. Public by Default for Discovery - -```bash -# For APIs or search, mark as public -fastapps create api-search --public -``` - -### 4. Optional Auth for Freemium - -```bash -# Unlock features when user authenticates -fastapps create content --optional-auth --scopes user -``` - -### 5. Check Generated Code - -After creating, always review the generated files: -```bash -cat server/tools/mywidget_tool.py -# Verify decorator and scopes are correct -``` - ---- - -## Error Handling - -### Error: Multiple auth flags - -```bash -$ fastapps create test --auth --public - -Error: Only one auth option allowed (--auth, --public, or --optional-auth) -``` - -**Solution:** Use only one auth flag. - -### Error: Scopes without auth flag - -```bash -$ fastapps create test --scopes user - -[WARNING] --scopes has no effect without --auth or --optional-auth -``` - -**Solution:** Add `--auth` or `--optional-auth`. - ---- - -## Documentation - -- **This guide**: Complete CLI examples -- **Server auth**: `docs/08-AUTH.md` -- **Per-widget auth**: `docs/09-PER-WIDGET-AUTH.md` -- **Quick start**: `README.md` -- **OAuth testing**: `OAUTH_TESTING_GUIDE.md` - ---- - -**Ready to build?** - -```bash -fastapps init my-app -cd my-app -pip install -r requirements.txt && npm install -fastapps create mywidget --auth --scopes user -npm run build -python server/main.py -``` - diff --git a/CLI_UPDATE_SUMMARY.md b/CLI_UPDATE_SUMMARY.md deleted file mode 100644 index 73a5584..0000000 --- a/CLI_UPDATE_SUMMARY.md +++ /dev/null @@ -1,400 +0,0 @@ -# CLI Commands Update Summary - v1.0.7 - -## ✅ Changes Implemented - -### 1. Updated Version -- **Before**: v1.0.4 -- **After**: v1.0.7 -- Updated in `cli/main.py` - -### 2. Enhanced `fastapps create` Command - -#### New Flags Added: -```bash ---auth # Add @auth_required decorator ---public # Add @no_auth decorator ---optional-auth # Add @optional_auth decorator ---scopes TEXT # Comma-separated OAuth scopes -``` - -#### Features: -- ✅ Auto-generates appropriate decorators -- ✅ Imports correct modules (UserContext, decorators) -- ✅ Generates auth-aware execute() method -- ✅ Adds descriptive comments -- ✅ Shows auth status in output -- ✅ Validates flag combinations - -#### Examples: -```bash -# Auth required -fastapps create admin --auth --scopes admin,write:data - -# Public widget -fastapps create search --public - -# Optional auth -fastapps create content --optional-auth --scopes user - -# No auth specified (commented examples included) -fastapps create basic -``` - -### 3. New `fastapps auth-info` Command - -Quick reference command that displays: -- Server-wide auth configuration example -- Per-widget auth command examples -- Decorator syntax -- UserContext usage -- Documentation links - -**Usage:** -```bash -$ fastapps auth-info - -FastApps Authentication Guide - -Server-Wide Auth: - Configure in server/main.py: - server = WidgetMCPServer( - name='my-widgets', - widgets=tools, - auth_issuer_url='https://tenant.auth0.com', - auth_resource_server_url='https://example.com/mcp', - auth_required_scopes=['user'], - ) - -Per-Widget Auth: - Create authenticated widget: - $ fastapps create mywidget --auth --scopes user,read:data - ... -``` - -### 4. Updated `generate_tool_code()` Function - -New function in `cli/commands/create.py` that: -- Takes `auth_type` and `scopes` parameters -- Generates appropriate imports based on auth type -- Adds correct decorator -- Creates auth-aware execute() body -- Sets descriptive `description` field - ---- - -## Files Modified - -1. **`/fastapps/cli/main.py`** - - Updated version to 1.0.7 - - Added `--auth`, `--public`, `--optional-auth`, `--scopes` options to create command - - Added `auth-info` command - - Added validation logic for auth flags - -2. **`/fastapps/cli/commands/create.py`** - - Added `generate_tool_code()` function - - Updated `create_widget()` signature - - Enhanced output with auth status indicators - - Added helpful tips - ---- - -## Generated Code Examples - -### With `--auth --scopes user,read:data` - -```python -from fastapps import BaseWidget, ConfigDict, auth_required, UserContext -from pydantic import BaseModel -from typing import Dict, Any - - -class MywidgetInput(BaseModel): - model_config = ConfigDict(populate_by_name=True) - - -@auth_required(scopes=["user", "read:data"]) -class MywidgetTool(BaseWidget): - identifier = "mywidget" - title = "Mywidget" - description = "Requires authentication (user, read:data)" - input_schema = MywidgetInput - invoking = "Loading widget..." - invoked = "Widget ready!" - - widget_csp = { - "connect_domains": [], - "resource_domains": [] - } - - async def execute(self, input_data: MywidgetInput, context=None, user=None) -> Dict[str, Any]: - # Access authenticated user - if user and user.is_authenticated: - return { - "message": f"Hello, {user.claims.get('name', 'User')}!", - "user_id": user.subject, - "scopes": user.scopes, - } - - return { - "message": "Welcome to FastApps" - } -``` - -### With `--public` - -```python -from fastapps import BaseWidget, ConfigDict, no_auth -from pydantic import BaseModel -from typing import Dict, Any - - -class MywidgetInput(BaseModel): - model_config = ConfigDict(populate_by_name=True) - - -@no_auth -class MywidgetTool(BaseWidget): - identifier = "mywidget" - title = "Mywidget" - description = "Public widget - no authentication required" - input_schema = MywidgetInput - invoking = "Loading widget..." - invoked = "Widget ready!" - - widget_csp = { - "connect_domains": [], - "resource_domains": [] - } - - async def execute(self, input_data: MywidgetInput, context=None, user=None) -> Dict[str, Any]: - return { - "message": "Welcome to FastApps" - } -``` - -### With `--optional-auth --scopes user` - -```python -from fastapps import BaseWidget, ConfigDict, optional_auth, UserContext -from pydantic import BaseModel -from typing import Dict, Any - - -class MywidgetInput(BaseModel): - model_config = ConfigDict(populate_by_name=True) - - -@optional_auth(scopes=["user"]) -class MywidgetTool(BaseWidget): - identifier = "mywidget" - title = "Mywidget" - description = "Supports both authenticated and anonymous access" - input_schema = MywidgetInput - invoking = "Loading widget..." - invoked = "Widget ready!" - - widget_csp = { - "connect_domains": [], - "resource_domains": [] - } - - async def execute(self, input_data: MywidgetInput, context=None, user=None) -> Dict[str, Any]: - # Access authenticated user - if user and user.is_authenticated: - return { - "message": f"Hello, {user.claims.get('name', 'User')}!", - "user_id": user.subject, - "scopes": user.scopes, - } - - return { - "message": "Welcome to FastApps" - } -``` - -### Without Auth Flags (Default) - -```python -from fastapps import BaseWidget, ConfigDict -# from fastapps import auth_required, no_auth, optional_auth, UserContext -from pydantic import BaseModel -from typing import Dict, Any - - -class MywidgetInput(BaseModel): - model_config = ConfigDict(populate_by_name=True) - - -# Optional: Add authentication -# @auth_required(scopes=["user"]) -# @no_auth -# @optional_auth(scopes=["user"]) -class MywidgetTool(BaseWidget): - identifier = "mywidget" - title = "Mywidget" - input_schema = MywidgetInput - invoking = "Loading widget..." - invoked = "Widget ready!" - - widget_csp = { - "connect_domains": [], - "resource_domains": [] - } - - async def execute(self, input_data: MywidgetInput, context=None, user=None) -> Dict[str, Any]: - # Access authenticated user (if present) - # if user and user.is_authenticated: - # return { - # "message": f"Hello {user.subject}!", - # "scopes": user.scopes, - # "user_data": user.claims - # } - - return { - "message": "Welcome to FastApps" - } -``` - ---- - -## Terminal Output Examples - -### Creating Protected Widget - -```bash -$ fastapps create admin-dashboard --auth --scopes admin,write:data - -[OK] Widget created successfully! - -Created files: - - server/tools/admin_dashboard_tool.py - - widgets/admin-dashboard/index.jsx - -🔒 Authentication: Required with scopes: admin, write:data - -Next steps: - 1. npm run build - 2. python server/main.py - -Your widget will be automatically discovered by FastApps! -``` - -### Creating Public Widget - -```bash -$ fastapps create search --public - -[OK] Widget created successfully! - -Created files: - - server/tools/search_tool.py - - widgets/search/index.jsx - -🌐 Authentication: Public (no auth) - -Next steps: - 1. npm run build - 2. python server/main.py - -Your widget will be automatically discovered by FastApps! -``` - -### Creating Optional Auth Widget - -```bash -$ fastapps create premium-content --optional-auth --scopes premium - -[OK] Widget created successfully! - -Created files: - - server/tools/premium_content_tool.py - - widgets/premium-content/index.jsx - -🔓 Authentication: Optional (scopes: premium) - -Next steps: - 1. npm run build - 2. python server/main.py - -Your widget will be automatically discovered by FastApps! -``` - -### Creating Widget Without Auth - -```bash -$ fastapps create basic-widget - -[OK] Widget created successfully! - -Created files: - - server/tools/basic_widget_tool.py - - widgets/basic-widget/index.jsx - -ℹ️ Authentication: Not configured (will inherit from server) - -Next steps: - 1. npm run build - 2. python server/main.py - -Your widget will be automatically discovered by FastApps! - -Tip: Use --auth, --public, or --optional-auth flags for authentication -Example: fastapps create basic-widget --auth --scopes user,read:data -``` - ---- - -## Workflow Improvements - -### Before v1.0.7 - -1. Run: `fastapps create mywidget` -2. Open `server/tools/mywidget_tool.py` -3. Manually add: `from fastapps import auth_required, UserContext` -4. Manually add: `@auth_required(scopes=["user"])` -5. Manually update: execute signature -6. Manually add: user handling code - -**Time: ~5 minutes per widget** - -### After v1.0.7 - -1. Run: `fastapps create mywidget --auth --scopes user` - -**Time: 10 seconds** ✨ - ---- - -## Cheat Sheet - -```bash -# Quick commands -fastapps init my-app # New project -fastapps create name # Widget with examples -fastapps create name --auth --scopes s1,s2 # Protected -fastapps create name --public # Public -fastapps create name --optional-auth --scopes s1 # Flexible -fastapps auth-info # Show auth guide - -# Common patterns -fastapps create admin --auth --scopes admin # Admin only -fastapps create profile --auth --scopes user # User data -fastapps create search --public # Public API -fastapps create premium --optional-auth --scopes premium # Freemium -``` - ---- - -## Documentation - -- **Full CLI reference**: `CLI_COMMANDS_UPDATED.md` -- **Complete examples**: `CLI_EXAMPLES.md` (this file) -- **Auth setup**: `docs/08-AUTH.md` -- **Per-widget auth**: `docs/09-PER-WIDGET-AUTH.md` - ---- - -**Version**: 1.0.7 -**Status**: ✅ Complete and tested -**Impact**: 10x faster widget creation with authentication - diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md deleted file mode 100644 index 089b39f..0000000 --- a/DEVELOPMENT.md +++ /dev/null @@ -1,525 +0,0 @@ -# Development Guide - -Quick reference for FastApps developers. - -## Setup - -```bash -# Clone repository -git clone https://github.com/fastapps-framework/fastapps.git -cd fastapps - -# Create virtual environment -python -m venv venv -source venv/bin/activate # Windows: venv\Scripts\activate - -# Install in development mode -pip install -e ".[dev]" - -# Install pre-commit hooks (optional but recommended) -pip install pre-commit -pre-commit install -``` - -## Daily Workflow - -### Before You Start - -```bash -# Update your fork -git checkout main -git pull upstream main - -# Create feature branch -git checkout -b feature/my-feature -``` - -### While Developing - -```bash -# Format code (do this often!) -black . - -# Lint and auto-fix -ruff check --fix . - -# Run tests -pytest - -# Run specific test -pytest tests/test_widget.py -v - -# Check coverage -pytest --cov=fastapps --cov-report=html -``` - -### Before Committing - -```bash -# Run all checks -black . -ruff check . -pytest --cov=fastapps - -# If all pass, commit -git add . -git commit -m "feat: add new feature" -``` - -## Code Formatting - -### Black - Automatic Formatter - -```bash -# Format all files -black . - -# Check what would change (don't modify) -black --check . - -# Format specific file/directory -black fastapps/core/ -black fastapps/cli/commands/create.py -``` - -**Black is non-negotiable** - all code must be Black-formatted before merge. - -### Ruff - Linter - -```bash -# Lint all files -ruff check . - -# Auto-fix issues -ruff check --fix . - -# Lint specific file -ruff check fastapps/core/widget.py - -# Show all violations (even fixed ones) -ruff check --show-fixes . -``` - -## Testing - -### Running Tests - -```bash -# All tests -pytest - -# With verbose output -pytest -v - -# Specific test file -pytest tests/test_widget.py - -# Specific test function -pytest tests/test_widget.py::test_widget_creation - -# Stop on first failure -pytest -x - -# Show local variables on failure -pytest -l - -# Run tests matching pattern -pytest -k "widget" -``` - -### Coverage - -```bash -# Generate coverage report -pytest --cov=fastapps - -# HTML coverage report -pytest --cov=fastapps --cov-report=html -open htmlcov/index.html # macOS -xdg-open htmlcov/index.html # Linux - -# Coverage with missing lines -pytest --cov=fastapps --cov-report=term-missing -``` - -### Writing Tests - -```python -# tests/test_my_feature.py -import pytest -from fastapps import Widget - - -def test_simple_case(): - """Test description.""" - result = my_function() - assert result == expected - - -@pytest.fixture -def sample_widget(): - """Reusable test fixture.""" - class TestWidget(Widget): - def render(self): - return {"message": "test"} - return TestWidget() - - -def test_with_fixture(sample_widget): - """Use the fixture.""" - assert sample_widget.render()["message"] == "test" - - -@pytest.mark.slow -def test_slow_operation(): - """Mark slow tests.""" - # This test takes a while - pass - - -# Run without slow tests: pytest -m "not slow" -``` - -## Building and Packaging - -```bash -# Install build tools -pip install build twine - -# Build distribution packages -python -m build - -# Check package validity -twine check dist/* - -# Test upload to Test PyPI (optional) -twine upload --repository testpypi dist/* - -# Install from built package (testing) -pip install dist/fastapps-*.whl -``` - -## Project Structure - -``` -fastapps/ -├── fastapps/ # Main package -│ ├── __init__.py # Package exports -│ ├── auth/ # Authentication system -│ ├── builder/ # Widget build system -│ ├── cli/ # CLI commands -│ │ ├── main.py # CLI entry point -│ │ └── commands/ # Command implementations -│ ├── core/ # Core functionality -│ │ ├── widget.py # Base widget class -│ │ └── server.py # MCP server -│ ├── types/ # Type definitions -│ └── dev_server.py # Development server -│ -├── tests/ # Test suite -│ ├── __init__.py -│ ├── conftest.py # Shared fixtures -│ ├── test_widget.py -│ └── test_import.py -│ -├── examples/ # Example scripts -├── .github/ # GitHub Actions workflows -│ └── workflows/ -│ -├── pyproject.toml # Project configuration -├── setup.py # Legacy setup file -├── pytest.ini # Pytest configuration -├── CONTRIBUTING.md # Contribution guidelines -├── CODE_STYLE.md # Code style guide -└── README.md # Project readme -``` - -## Common Tasks - -### Adding a New Widget Feature - -1. **Write the code** in appropriate module -2. **Add tests** in `tests/` -3. **Update exports** in `fastapps/__init__.py` if needed -4. **Format and lint**: - ```bash - black . - ruff check --fix . - ``` -5. **Run tests**: `pytest` -6. **Update docs** if API changed - -### Adding a New CLI Command - -1. **Create command file** in `fastapps/cli/commands/` -2. **Import in** `fastapps/cli/main.py` -3. **Add tests** in `tests/` -4. **Update CLI examples** in README.md - -### Fixing a Bug - -1. **Write a failing test** that demonstrates the bug -2. **Fix the bug** -3. **Verify test passes** -4. **Add regression test** if appropriate -5. **Format and commit** - -## Debugging - -### Interactive Debugging - -```python -# Add breakpoint in code -import pdb; pdb.set_trace() - -# Or use built-in breakpoint() (Python 3.7+) -breakpoint() - -# Run pytest with pdb -pytest --pdb # Drop into debugger on failure -pytest --pdb -x # Stop on first failure -``` - -### Print Debugging - -```python -# Use rich for better output -from rich import print as rprint -from rich.console import Console - -console = Console() - -# Pretty print -rprint({"key": "value", "list": [1, 2, 3]}) - -# With colors -console.print("[bold red]Error![/bold red]") -console.print("[green]Success![/green]") - -# Print with inspect -from rich import inspect -inspect(my_object, methods=True) -``` - -### Logging - -```python -import logging - -# Set up logging -logging.basicConfig(level=logging.DEBUG) -logger = logging.getLogger(__name__) - -# Use in code -logger.debug("Debug message") -logger.info("Info message") -logger.warning("Warning message") -logger.error("Error message") -``` - -## Git Workflow - -### Feature Development - -```bash -# 1. Start from main -git checkout main -git pull upstream main - -# 2. Create feature branch -git checkout -b feature/my-feature - -# 3. Make changes, commit often -git add . -git commit -m "feat: add initial implementation" - -# 4. Keep up to date with main -git fetch upstream -git rebase upstream/main - -# 5. Push to your fork -git push origin feature/my-feature - -# 6. Create pull request on GitHub -``` - -### Commit Messages - -Follow [Conventional Commits](https://www.conventionalcommits.org/): - -```bash -# Format: (): - -# Types: -feat: New feature -fix: Bug fix -docs: Documentation -style: Code style (formatting) -refactor: Code refactoring -test: Tests -chore: Maintenance - -# Examples: -git commit -m "feat(cli): add create command" -git commit -m "fix(widget): resolve render issue" -git commit -m "docs(readme): update installation steps" -git commit -m "test(core): add widget tests" -``` - -## CI/CD - -### GitHub Actions Workflows - -Located in `.github/workflows/`: - -- **`ci.yml`**: Main CI pipeline (tests, lint, build) -- **`publish.yml`**: PyPI publishing on release -- **`codeql.yml`**: Security scanning -- **`dependency-review.yml`**: Dependency security - -### Local CI Simulation - -Run the same checks that CI runs: - -```bash -# Format check -black --check . - -# Linting -ruff check . - -# Tests with coverage -pytest --cov=fastapps --cov-report=xml - -# Type checking (optional) -mypy fastapps --ignore-missing-imports - -# Build check -python -m build -twine check dist/* -``` - -## Troubleshooting - -### Import Errors After Install - -```bash -# Reinstall in editable mode -pip install -e ".[dev]" -``` - -### Tests Failing Locally - -```bash -# Clean cache and rerun -pytest --cache-clear -rm -rf .pytest_cache __pycache__ - -# Reinstall dependencies -pip install -e ".[dev]" --force-reinstall -``` - -### Black/Ruff Conflicts - -```bash -# Black takes precedence -black . - -# Then run ruff -ruff check --fix . -``` - -### Pre-commit Hook Fails - -```bash -# Update pre-commit -pre-commit autoupdate - -# Run manually -pre-commit run --all-files - -# Skip if needed (not recommended) -git commit --no-verify -``` - -## Performance Testing - -```bash -# Time a specific test -pytest tests/test_widget.py --durations=10 - -# Profile with pytest-profiling -pip install pytest-profiling -pytest --profile - -# Memory profiling -pip install memory_profiler -python -m memory_profiler my_script.py -``` - -## Documentation - -### Docstring Format - -Use Google-style docstrings: - -```python -def function(arg1: str, arg2: int = 0) -> bool: - """Short one-line description. - - Longer description if needed, explaining the function's purpose - and behavior in more detail. - - Args: - arg1: Description of arg1 - arg2: Description of arg2 (default: 0) - - Returns: - Description of return value - - Raises: - ValueError: When arg1 is empty - TypeError: When arg2 is negative - - Example: - >>> function("test", 5) - True - """ - pass -``` - -## Resources - -- **Black**: https://black.readthedocs.io/ -- **Ruff**: https://docs.astral.sh/ruff/ -- **pytest**: https://docs.pytest.org/ -- **FastApps Docs**: https://www.fastapps.org/ - -## Quick Command Reference - -```bash -# Format all code -black . - -# Lint and fix -ruff check --fix . - -# Run tests -pytest - -# Run tests with coverage -pytest --cov=fastapps - -# Build package -python -m build - -# Install locally -pip install -e ".[dev]" - -# Run pre-commit on all files -pre-commit run --all-files -``` - ---- - -Happy coding! 🚀 diff --git a/PYPI_RELEASE_v1.0.8.md b/PYPI_RELEASE_v1.0.8.md deleted file mode 100644 index 789d397..0000000 --- a/PYPI_RELEASE_v1.0.8.md +++ /dev/null @@ -1,343 +0,0 @@ -# FastApps v1.0.8 - PyPI Release Notes - -## 🎉 Published Successfully! - -**Package**: fastapps -**Version**: 1.0.8 -**PyPI URL**: https://pypi.org/project/fastapps/1.0.8/ -**Release Date**: January 17, 2025 - ---- - -## 📦 What's New in v1.0.8 - -### Major Feature: Complete OAuth 2.1 Authentication System - -FastApps now includes a production-ready OAuth 2.1 authentication system that makes securing your ChatGPT widgets incredibly simple. - -#### 1. Server-Wide Authentication (3 Parameters) - -Enable OAuth for all widgets with just 3 parameters: - -```python -from fastapps import WidgetMCPServer - -server = WidgetMCPServer( - name="my-widgets", - widgets=tools, - auth_issuer_url="https://tenant.auth0.com", - auth_resource_server_url="https://example.com/mcp", - auth_required_scopes=["user"], -) -``` - -Features: -- ✅ Built-in JWT verification with JWKS auto-discovery -- ✅ Supports Auth0, Okta, Azure AD, AWS Cognito -- ✅ Custom `TokenVerifier` for advanced cases -- ✅ Automatic token validation on every request - -#### 2. Per-Widget Authentication Decorators - -Control auth requirements per widget: - -```python -from fastapps import BaseWidget, auth_required, no_auth, optional_auth, UserContext - -# Require authentication -@auth_required(scopes=["user", "write:data"]) -class ProtectedWidget(BaseWidget): - async def execute(self, input_data, context, user: UserContext): - return {"user_id": user.subject, "scopes": user.scopes} - -# Explicitly public -@no_auth -class PublicWidget(BaseWidget): - async def execute(self, input_data, context, user): - return {"message": "Public content"} - -# Optional authentication (freemium) -@optional_auth(scopes=["user"]) -class FlexibleWidget(BaseWidget): - async def execute(self, input_data, context, user: UserContext): - if user.is_authenticated: - return {"tier": "premium", "user": user.subject} - return {"tier": "free"} -``` - -#### 3. Enhanced CLI Commands - -Create authenticated widgets instantly: - -```bash -# Protected widget -fastapps create admin --auth --scopes admin,write:data - -# Public widget -fastapps create search --public - -# Optional auth widget -fastapps create content --optional-auth --scopes user - -# Get auth help -fastapps auth-info -``` - -Features: -- ✅ Auto-generates decorators and imports -- ✅ Creates auth-aware execute() methods -- ✅ Shows auth status in terminal output -- ✅ Quick reference command (`auth-info`) - -#### 4. UserContext for Authenticated Users - -Access user information in your widgets: - -```python -async def execute(self, input_data, context, user: UserContext): - # Properties - user.is_authenticated # bool - user.subject # User ID - user.client_id # OAuth client - user.scopes # List of scopes - user.claims # Full JWT claims - - # Methods - user.has_scope("admin") # Check scope -``` - ---- - -## 🔐 Security Features - -- **RS256 JWT Validation**: Secure signature verification -- **JWKS Auto-Discovery**: Automatic public key fetching -- **Scope Enforcement**: Fine-grained permission control -- **Server-Side Enforcement**: Can't be bypassed by client -- **MCP Specification Compliant**: Follows official patterns - ---- - -## 📚 New Documentation - -- **docs/08-AUTH.md** (11.8KB) - Server-wide OAuth setup -- **docs/09-PER-WIDGET-AUTH.md** (15KB) - Per-widget decorators -- **OAUTH_TESTING_GUIDE.md** - Complete Auth0 testing guide -- **CLI_COMMANDS_UPDATED.md** - Updated CLI reference -- **CLI_EXAMPLES.md** - Real-world CLI examples - ---- - -## 🆕 New Exports - -```python -from fastapps import ( - # New in v1.0.8 - JWTVerifier, # Built-in JWT verifier - TokenVerifier, # Base class for custom verifiers - AccessToken, # Token data class - UserContext, # Authenticated user context - auth_required, # Auth required decorator - no_auth, # Public widget decorator - optional_auth, # Optional auth decorator - - # Existing - BaseWidget, - ClientContext, - WidgetMCPServer, - Field, - ConfigDict, -) -``` - ---- - -## 📦 New Dependencies - -- `PyJWT>=2.8.0` - JWT token validation -- `cryptography>=41.0.0` - RS256 signature verification -- `httpx>=0.28.0` - HTTP client for JWKS - ---- - -## 🔄 Upgrade Guide - -### From v1.0.7 or earlier - -```bash -pip install --upgrade fastapps -``` - -**Backward Compatibility:** All existing code continues to work. Auth is optional. - -**To use auth features:** - -1. **Server-wide auth** - Add parameters to `WidgetMCPServer` -2. **Per-widget auth** - Use decorators on widgets -3. **CLI shortcuts** - Use `--auth`, `--public`, `--optional-auth` flags - ---- - -## 🚀 Quick Start with Auth - -```bash -# Install -pip install fastapps==1.0.8 - -# Create project -fastapps init my-app -cd my-app -pip install -r requirements.txt && npm install - -# Create authenticated widget -fastapps create admin --auth --scopes admin - -# Configure OAuth in server/main.py -# Build and run -npm run build -python server/main.py -``` - ---- - -## 📊 Package Statistics - -- **Version**: 1.0.8 -- **Python**: 3.11+ -- **Wheel Size**: 41.5 KB -- **Source Size**: 106.1 KB -- **Files**: 21 Python modules -- **Documentation**: 50+ KB - ---- - -## 🎯 What's Included - -### Core Modules -- `fastapps/core/widget.py` - BaseWidget, UserContext, ClientContext -- `fastapps/core/server.py` - WidgetMCPServer with OAuth -- `fastapps/builder/compiler.py` - Widget builder - -### Auth Modules (NEW) -- `fastapps/auth/verifier.py` - JWTVerifier -- `fastapps/auth/decorators.py` - Auth decorators -- `fastapps/auth/__init__.py` - Auth exports - -### CLI Modules -- `fastapps/cli/main.py` - CLI with auth commands -- `fastapps/cli/commands/create.py` - Smart widget generation -- `fastapps/cli/commands/init.py` - Project scaffolding - ---- - -## 🔍 Installation Verification - -After installing, verify auth features: - -```python -from fastapps import ( - BaseWidget, - UserContext, - auth_required, - no_auth, - optional_auth, - JWTVerifier, -) - -print("✓ All auth features available!") -``` - -Or check CLI: - -```bash -fastapps --version # Should show 1.0.8 -fastapps auth-info # Should display auth guide -``` - ---- - -## 📖 Documentation Links - -- **PyPI Page**: https://pypi.org/project/fastapps/1.0.8/ -- **GitHub**: https://github.com/fastapps-framework/fastapps -- **Documentation**: https://fastapps.dev/docs -- **Server Auth Guide**: docs/08-AUTH.md -- **Widget Auth Guide**: docs/09-PER-WIDGET-AUTH.md - ---- - -## 🐛 Bug Fixes - -None - this is a feature release. - ---- - -## ⚠️ Breaking Changes - -None - fully backward compatible with v1.0.7 and earlier. - ---- - -## 🎓 Next Steps for Users - -1. **Upgrade**: `pip install --upgrade fastapps` -2. **Read docs**: Check `docs/08-AUTH.md` for setup -3. **Set up OAuth**: Create Auth0 or Okta tenant -4. **Test**: Use `OAUTH_TESTING_GUIDE.md` for testing -5. **Deploy**: Secure your widgets! - ---- - -## 👥 Contributors - -FastApps Team - ---- - -## 📝 Changelog - -### v1.0.8 (2025-01-17) - -**Added:** -- Server-wide OAuth 2.1 authentication -- Built-in JWTVerifier with JWKS auto-discovery -- Per-widget auth decorators (@auth_required, @no_auth, @optional_auth) -- UserContext for accessing authenticated user info -- CLI flags for widget creation with auth (--auth, --public, --optional-auth, --scopes) -- New CLI command: `fastapps auth-info` -- Comprehensive authentication documentation (26KB+) -- OAuth testing guide and automation scripts -- PyJWT, cryptography dependencies - -**Changed:** -- WidgetMCPServer accepts OAuth parameters -- BaseWidget.execute() signature includes `user` parameter -- CLI create command generates auth-aware code -- Version bumped to 1.0.8 - -**Fixed:** -- None - ---- - -## 🎉 Summary - -FastApps v1.0.8 delivers **complete OAuth 2.1 authentication** with: -- ✅ 3-parameter server configuration -- ✅ Per-widget control with decorators -- ✅ Built-in JWT verification -- ✅ CLI shortcuts for fast development -- ✅ MCP specification compliant -- ✅ Production ready -- ✅ Fully documented - -Install now: -```bash -pip install fastapps==1.0.8 -``` - ---- - -**License**: MIT -**Support**: https://github.com/fastapps-framework/fastapps/issues - diff --git a/PYTHON_API_SUMMARY.md b/PYTHON_API_SUMMARY.md deleted file mode 100644 index 9c6d49a..0000000 --- a/PYTHON_API_SUMMARY.md +++ /dev/null @@ -1,233 +0,0 @@ -# Python API Implementation Summary - -Complete Python API for FastApps development server with ngrok integration. - -## What Was Created - -### 1. Core API Module (`fastapps/dev_server.py`) - -**Functions:** -- `start_dev_server()` - Start dev server with ngrok tunnel -- `start_dev_server_with_config()` - Start with config object -- `get_server_info()` - Get URLs without starting server -- `run_dev_server()` - Alias for start_dev_server - -**Classes:** -- `DevServerConfig` - Configuration dataclass -- `ServerInfo` - Server information dataclass -- `DevServerError` - Base exception -- `ProjectNotFoundError` - Project validation error -- `NgrokError` - ngrok tunnel error - -**Features:** -- ✅ Full programmatic control -- ✅ Configuration via kwargs or object -- ✅ Proper error handling -- ✅ Type hints throughout -- ✅ Comprehensive docstrings - -### 2. API Exports (`fastapps/__init__.py`) - -Exported all Python API components: -```python -from fastapps import ( - start_dev_server, - DevServerConfig, - ServerInfo, - get_server_info, - # ... and more -) -``` - -### 3. Example Scripts (`examples/`) - -Created 5 complete working examples: - -1. **python_api_basic.py** - Simplest usage -2. **python_api_advanced.py** - Custom configuration -3. **python_api_with_token.py** - Environment variable token -4. **python_api_get_info.py** - Get URLs without starting -5. **python_api_error_handling.py** - Proper error handling - -Plus **examples/README.md** with quick reference. - -### 4. Documentation (`docs/PYTHON_API.md`) - -Complete 400+ line documentation covering: -- Quick start -- Basic and advanced usage -- API reference -- Error handling -- Use cases -- Examples -- Best practices -- Troubleshooting - -### 5. Updated Main Documentation - -Updated files: -- **README.md** - Added Python API section and examples -- All docs now mention Python API alongside CLI - -## Usage Examples - -### Simple Start -```python -from fastapps import start_dev_server - -start_dev_server() -``` - -### With Configuration -```python -from fastapps import start_dev_server - -start_dev_server( - port=8080, - ngrok_token="your_token", - auto_reload=True -) -``` - -### Get Server Info -```python -from fastapps import get_server_info - -info = get_server_info(port=8001) -print(f"Public URL: {info.public_url}") -``` - -### Error Handling -```python -from fastapps import start_dev_server, DevServerError - -try: - start_dev_server() -except DevServerError as e: - print(f"Error: {e}") -``` - -## File Structure - -``` -FastApps/ -├── fastapps/ -│ ├── __init__.py # Updated with API exports -│ └── dev_server.py # NEW: Core Python API -├── examples/ -│ ├── README.md # NEW: Examples guide -│ ├── python_api_basic.py # NEW: Basic example -│ ├── python_api_advanced.py # NEW: Advanced example -│ ├── python_api_with_token.py # NEW: Token example -│ ├── python_api_get_info.py # NEW: Info example -│ └── python_api_error_handling.py # NEW: Error handling -├── docs/ -│ └── PYTHON_API.md # NEW: Complete Python API docs -├── README.md # Updated with Python API section -└── PYTHON_API_SUMMARY.md # This file -``` - -## API Surface - -### Functions -| Function | Purpose | -|----------|---------| -| `start_dev_server()` | Start server (blocks) | -| `start_dev_server_with_config()` | Start with config object | -| `get_server_info()` | Get URLs without starting | -| `run_dev_server()` | Alias for start_dev_server | - -### Classes -| Class | Purpose | -|-------|---------| -| `DevServerConfig` | Server configuration | -| `ServerInfo` | Server URL information | -| `DevServerError` | Base exception | -| `ProjectNotFoundError` | Project validation | -| `NgrokError` | ngrok tunnel errors | - -## Key Features - -1. **Simple API** - One function call to start everything -2. **Flexible Configuration** - kwargs or config object -3. **Proper Errors** - Exception hierarchy for handling -4. **Type Safety** - Full type hints and dataclasses -5. **Documentation** - Comprehensive docs and examples -6. **Testable** - Can get info without starting server - -## Use Cases - -1. **Automation** - Start servers in scripts -2. **Testing** - Get URLs for integration tests -3. **CI/CD** - Run dev servers in pipelines -4. **Notebooks** - Use in Jupyter notebooks -5. **Custom Tools** - Build your own dev workflows - -## Testing - -All syntax validated: -- ✅ `dev_server.py` - Syntax valid -- ✅ All example files - Syntax valid -- ✅ Imports work correctly - -## Next Steps for Users - -1. **Install FastApps**: - ```bash - pip install fastapps pyngrok - ``` - -2. **Try Basic Example**: - ```bash - python examples/python_api_basic.py - ``` - -3. **Read Documentation**: - - [Python API Docs](docs/PYTHON_API.md) - - [Examples README](examples/README.md) - -4. **Use in Your Code**: - ```python - from fastapps import start_dev_server - start_dev_server(port=8080) - ``` - -## Benefits Over CLI Only - -| Feature | CLI Only | With Python API | -|---------|----------|-----------------| -| Programmatic control | ❌ | ✅ | -| Use in scripts | ⚠️ subprocess | ✅ Native | -| Error handling | ❌ Exit codes | ✅ Exceptions | -| Get URLs without starting | ❌ | ✅ | -| Configuration | ⚠️ Flags | ✅ Full control | -| Testing integration | ❌ | ✅ Easy | -| Jupyter notebooks | ❌ | ✅ Yes | - -## Implementation Quality - -- ✅ Clean, readable code -- ✅ Comprehensive docstrings -- ✅ Type hints throughout -- ✅ Proper error handling -- ✅ Following Python best practices -- ✅ Consistent with FastApps style -- ✅ Well-documented -- ✅ Multiple examples -- ✅ Tested syntax - -## Version - -Implemented for FastApps v1.0.8+ - -## Summary - -The Python API provides full programmatic control over the FastApps development server with ngrok integration. It's designed to be simple for basic use cases while providing flexibility for advanced scenarios. Complete documentation, multiple examples, and proper error handling make it production-ready. - -Users can now: -- Start servers programmatically -- Configure all options via Python -- Get server URLs without starting -- Handle errors properly -- Use in automation, testing, and notebooks -- Build custom development workflows diff --git a/RELEASE_SUMMARY.md b/RELEASE_SUMMARY.md deleted file mode 100644 index 62bd03c..0000000 --- a/RELEASE_SUMMARY.md +++ /dev/null @@ -1,309 +0,0 @@ -# FastApps v1.0.8 - Complete Release Summary - -## 🎉 Successfully Published to PyPI! - -**Package**: https://pypi.org/project/fastapps/1.0.8/ -**Install**: `pip install fastapps==1.0.8` -**Released**: January 17, 2025 - ---- - -## 🚀 Major Features Added - -### 1. Server-Wide OAuth 2.1 Authentication - -Enable OAuth for all widgets with 3 parameters: - -```python -server = WidgetMCPServer( - name="my-widgets", - widgets=tools, - auth_issuer_url="https://tenant.auth0.com", - auth_resource_server_url="https://example.com/mcp", - auth_required_scopes=["user"], -) -``` - -### 2. Per-Widget Authentication Decorators - -```python -@auth_required(scopes=["admin"]) -class AdminWidget(BaseWidget): - async def execute(self, input_data, context, user): - return {"user_id": user.subject} - -@no_auth -class PublicWidget(BaseWidget): - async def execute(self, input_data, context, user): - return {"message": "Public"} - -@optional_auth(scopes=["user"]) -class FlexibleWidget(BaseWidget): - async def execute(self, input_data, context, user): - if user.is_authenticated: - return {"tier": "premium"} - return {"tier": "free"} -``` - -### 3. Enhanced CLI - -```bash -fastapps create admin --auth --scopes admin -fastapps create search --public -fastapps create content --optional-auth --scopes user -fastapps auth-info -``` - ---- - -## 📦 What's in the Package - -### New Modules -- `fastapps.auth.verifier` - JWTVerifier class -- `fastapps.auth.decorators` - Auth decorators -- `fastapps.auth` - Auth module - -### New Exports -- `JWTVerifier` - Built-in JWT verifier -- `TokenVerifier` - Base class -- `AccessToken` - Token data class -- `UserContext` - User info class -- `auth_required` - Decorator -- `no_auth` - Decorator -- `optional_auth` - Decorator - -### New Dependencies -- `PyJWT>=2.8.0` -- `cryptography>=41.0.0` -- `httpx>=0.28.0` - -### Updated Modules -- `fastapps.core.server` - OAuth integration -- `fastapps.core.widget` - UserContext, updated execute -- `fastapps.cli.main` - New flags and commands -- `fastapps.cli.commands.create` - Smart code generation - ---- - -## 📊 Implementation Statistics - -| Metric | Count | -|--------|-------| -| **Files Created** | 10+ | -| **Files Modified** | 11 | -| **Lines of Code** | ~1,500 | -| **Documentation** | 50+ KB | -| **New Features** | 15+ | -| **CLI Commands Added** | 4 flags + 1 command | - ---- - -## ✨ Key Benefits - -### For Developers - -- **10x Faster Development**: Widget creation with auth in 10 seconds vs 5 minutes -- **Zero Boilerplate**: 3 parameters vs 150+ lines of MCP code -- **Production Ready**: Secure JWT validation built-in -- **MCP Compliant**: Follows official specification -- **Well Documented**: 50KB+ of guides and examples - -### For Users - -- **Secure**: Industry-standard OAuth 2.1 with PKCE -- **Flexible**: Server-wide, per-widget, or mixed auth -- **Simple**: One-line decorators or 3-parameter config -- **Compatible**: Works with Auth0, Okta, Azure AD, Cognito - ---- - -## 🎯 Use Cases Enabled - -### E-commerce -```bash -fastapps create catalog --public -fastapps create cart --auth --scopes user -fastapps create checkout --auth --scopes user,write:orders -fastapps create admin --auth --scopes admin -``` - -### Content Platform -```bash -fastapps create articles --optional-auth --scopes user -fastapps create editor --auth --scopes user,write:content -fastapps create analytics --auth --scopes admin -``` - -### SaaS Dashboard -```bash -fastapps create dashboard --auth --scopes user -fastapps create settings --auth --scopes user,write:profile -fastapps create billing --auth --scopes user,read:billing -``` - ---- - -## 📖 Documentation Included - -### Guides (in package) -- `docs/08-AUTH.md` - Server-wide OAuth setup -- `docs/09-PER-WIDGET-AUTH.md` - Per-widget decorators - -### Extra Docs (in repo) -- `OAUTH_TESTING_GUIDE.md` - Complete testing guide -- `TESTING_SUMMARY.md` - Quick testing reference -- `CLI_COMMANDS_UPDATED.md` - Command reference -- `CLI_EXAMPLES.md` - Usage examples -- `PYPI_RELEASE_v1.0.8.md` - Release notes - ---- - -## 🧪 Testing - -### Structure Tests -✅ All file structures validated -✅ All imports verified -✅ All code patterns confirmed - -### CLI Tests -✅ generate_tool_code function works -✅ All auth flags tested -✅ Code generation verified - -### Ready for Integration Tests -- Auth0 setup guide provided -- Test automation script included -- ChatGPT testing procedures documented - ---- - -## 🔄 Migration Path - -### Existing Users (v1.0.6 → v1.0.8) - -```bash -# 1. Upgrade -pip install --upgrade fastapps - -# 2. No code changes required! -# Your existing widgets continue to work - -# 3. Optional: Add auth -# Edit server/main.py to add OAuth config -# Or add decorators to widgets -``` - -### New Users - -```bash -# 1. Install -pip install fastapps - -# 2. Create project -fastapps init my-app -cd my-app - -# 3. Create widgets with auth -fastapps create admin --auth --scopes admin -fastapps create search --public - -# 4. Configure OAuth -# Edit server/main.py - -# 5. Run -npm run build && python server/main.py -``` - ---- - -## 💡 Examples - -### Protected Admin Widget - -```python -from fastapps import BaseWidget, auth_required, UserContext - -@auth_required(scopes=["admin", "write:data"]) -class AdminWidget(BaseWidget): - identifier = "admin" - - async def execute(self, input_data, context, user: UserContext): - return { - "user_id": user.subject, - "name": user.claims.get('name'), - "scopes": user.scopes, - "is_admin": user.has_scope("admin") - } -``` - -### Freemium Content Widget - -```python -from fastapps import BaseWidget, optional_auth, UserContext - -@optional_auth(scopes=["premium"]) -class ContentWidget(BaseWidget): - identifier = "content" - - async def execute(self, input_data, context, user: UserContext): - content = get_content(input_data.id) - - if user.is_authenticated and user.has_scope("premium"): - return { - "content": content, - "quality": "hd", - "downloads": True - } - - return { - "content": content[:200] + "...", - "quality": "standard", - "message": "Upgrade for full access" - } -``` - ---- - -## 🌟 Highlights - -1. **Production Ready**: Secure, tested, MCP-compliant -2. **Developer Friendly**: Simple API, clear errors -3. **Well Documented**: 50KB+ of guides -4. **Battle Tested**: Structure and logic tests pass -5. **Backward Compatible**: No breaking changes -6. **Future Proof**: Extensible architecture - ---- - -## 📞 Support - -- **Issues**: https://github.com/fastapps-framework/fastapps/issues -- **Documentation**: https://fastapps.dev/docs -- **PyPI**: https://pypi.org/project/fastapps/ - ---- - -## 🎊 Thank You! - -Thank you for using FastApps! We hope the new authentication features make building secure ChatGPT widgets easier than ever. - ---- - -**Install now:** -```bash -pip install fastapps==1.0.8 -``` - -**Get started:** -```bash -fastapps init my-app -fastapps create mywidget --auth --scopes user -``` - -**Learn more:** -```bash -fastapps auth-info -``` - -Happy building! 🚀 - diff --git a/fastapps.egg-info/PKG-INFO b/fastapps.egg-info/PKG-INFO deleted file mode 100644 index 8de57f7..0000000 --- a/fastapps.egg-info/PKG-INFO +++ /dev/null @@ -1,720 +0,0 @@ -Metadata-Version: 2.4 -Name: fastapps -Version: 1.1.1 -Summary: A zero-boilerplate framework for building interactive ChatGPT widgets -Home-page: https://github.com/DooiLabs/FastApps -Author: FastApps Team -Author-email: FastApps Team -License: MIT -Project-URL: Homepage, https://www.fastapps.org -Project-URL: Documentation, https://www.fastapps.org/quickstart -Project-URL: Repository, https://github.com/DooiLabs/FastApps -Project-URL: Bug Reports, https://github.com/DooiLabs/FastApps/issues -Keywords: chatgpt,widgets,mcp,framework,react -Classifier: Development Status :: 4 - Beta -Classifier: Intended Audience :: Developers -Classifier: Topic :: Software Development :: Libraries :: Application Frameworks -Classifier: License :: OSI Approved :: MIT License -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Requires-Python: >=3.11 -Description-Content-Type: text/markdown -License-File: LICENSE -Requires-Dist: fastmcp>=0.1.0 -Requires-Dist: pydantic>=2.0.0 -Requires-Dist: uvicorn>=0.20.0 -Requires-Dist: click>=8.0.0 -Requires-Dist: rich>=13.0.0 -Requires-Dist: httpx>=0.28.0 -Requires-Dist: PyJWT>=2.8.0 -Requires-Dist: cryptography>=41.0.0 -Requires-Dist: pyngrok>=7.4.0 -Provides-Extra: dev -Requires-Dist: pytest>=7.0.0; extra == "dev" -Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev" -Requires-Dist: pytest-cov>=4.0.0; extra == "dev" -Requires-Dist: black>=23.0.0; extra == "dev" -Requires-Dist: ruff>=0.1.0; extra == "dev" -Dynamic: author -Dynamic: home-page -Dynamic: license-file -Dynamic: requires-python - -# FastApps -image 39 - -

- The python framework for ChatGPT apps -

- -

- PyPI - Python - PyPI Downloads - License -
- CI Status - Code style: black - Ruff - GitHub Stars -

- ---- - -📚 **Documentation**: [https://www.fastapps.org/](https://www.fastapps.org/) - -👥 **Community**: [Join Our Discord](https://discord.gg/5cEy3Jqek3) - ---- - -## Quick Start - -### 1. Create Virtual Environment (Recommended) - -```bash -python -m venv venv -source venv/bin/activate # macOS/Linux -venv\Scripts\activate # Windows -``` - -### 2. Install FastApps & Create Project - -```bash -pip install fastapps -fastapps init my-app -``` - -This generates the complete project structure: - -``` -my-app/ -├── server/ -│ ├── __init__.py -│ ├── main.py # Auto-discovery server -│ ├── tools/ # Widget backends -│ │ └── __init__.py -│ └── api/ # (optional) Shared APIs -│ └── __init__.py -├── widgets/ # Widget frontends (empty initially) -├── requirements.txt # Python dependencies -├── package.json # JavaScript dependencies -├── .gitignore -└── README.md -``` - -### 3. Install Dependencies - -```bash -cd my-app -pip install -r requirements.txt -npm install -``` - -### 4. Create Your First Widget - -```bash -fastapps create my-widget -``` - -This adds to your project: - -``` -my-app/ -├── server/ -│ └── tools/ -│ └── my_widget_tool.py # ← Generated: Widget backend -└── widgets/ - └── my-widget/ - └── index.jsx # ← Generated: Widget frontend -``` - -### 5. Edit Your Widget Code - -**You only need to edit these 2 files:** - -#### `server/tools/my_widget_tool.py` - Backend Logic - -```python -from fastapps import BaseWidget, Field, ConfigDict -from pydantic import BaseModel -from typing import Dict, Any - -class MyWidgetInput(BaseModel): - model_config = ConfigDict(populate_by_name=True) - name: str = Field(default="World") - -class MyWidgetTool(BaseWidget): - identifier = "my-widget" - title = "My Widget" - input_schema = MyWidgetInput - invoking = "Processing..." - invoked = "Done!" - - widget_csp = { - "connect_domains": [], # APIs you'll call - "resource_domains": [] # Images/fonts you'll use - } - - async def execute(self, input_data: MyWidgetInput) -> Dict[str, Any]: - # Your logic here - return { - "name": input_data.name, - "message": f"Hello, {input_data.name}!" - } -``` - -#### `widgets/my-widget/index.jsx` - Frontend UI - -```jsx -import React from 'react'; -import { useWidgetProps } from 'fastapps'; - -export default function MyWidget() { - const props = useWidgetProps(); - - return ( -
-

{props.message}

-

Welcome, {props.name}!

-
- ); -} -``` - -**That's it! These are the only files you need to write.** - -### 6. Build Widgets - -```bash -npm run build -``` - -### 7. Start Development Server with Public Access - -**Option A: Using `fastapps dev` (Recommended)** - -The easiest way to run and expose your server: - -```bash -fastapps dev -``` - -On first run, you'll be prompted for your ngrok auth token: -- Get it free at: https://dashboard.ngrok.com/get-started/your-authtoken -- Token is saved and won't be asked again - -You'll see: -``` -🚀 FastApps Development Server -┌─────────┬─────────────────────────┐ -│ Local │ http://0.0.0.0:8001 │ -│ Public │ https://xyz.ngrok.io │ -└─────────┴─────────────────────────┘ - -📡 MCP Server Endpoint: https://xyz.ngrok.io -``` - -**Option B: Manual Setup** - -```bash -# Start server -python server/main.py - -# In a separate terminal, create tunnel -ngrok http 8001 -``` - -### 8. Test - -**Option A: Test on ChatGPT** - -Add your public URL with a “/mcp” to ChatGPT in Settings > Connectors. -For example: https://.ngrok-free.app/mcp -​ - -**Option B: Test on MCPJam Inspector** - -```bash -# Start server -npx @mcpjam/inspector@latest -``` - - ---- - -## What You Need to Know - -### Widget Structure - -Every widget has **exactly 2 files you write**: - -1. **Python Tool** (`server/tools/*_tool.py`) - - Define inputs with Pydantic - - Write your logic in `execute()` - - Return data as a dictionary - -2. **React Component** (`widgets/*/index.jsx`) - - Get data with `useWidgetProps()` - - Render your UI - - Use inline styles - -**Everything else is automatic:** -- Widget discovery -- Registration -- Build process -- Server setup -- Mounting logic - -### Input Schema - -```python -from fastapps import Field, ConfigDict -from pydantic import BaseModel - -class MyInput(BaseModel): - model_config = ConfigDict(populate_by_name=True) - - name: str = Field(default="", description="User's name") - age: int = Field(default=0, ge=0, le=150) - email: str = Field(default="", pattern=r'^[\w\.-]+@[\w\.-]+\.\w+$') -``` - -### CSP (Content Security Policy) - -Allow external resources: - -```python -widget_csp = { - "connect_domains": ["https://api.example.com"], # For API calls - "resource_domains": ["https://cdn.example.com"] # For images/fonts -} -``` - -### React Hooks - -```jsx -import { useWidgetProps, useWidgetState, useOpenAiGlobal } from 'fastapps'; - -function MyWidget() { - const props = useWidgetProps(); // Data from Python - const [state, setState] = useWidgetState({}); // Persistent state - const theme = useOpenAiGlobal('theme'); // ChatGPT theme - - return
{props.message}
; -} -``` - ---- - -## Documentation - -- **[Quick Start Guide](https://github.com/DooiLabs/FastApps/blob/main/docs/QUICKSTART.md)** - Detailed setup instructions -- **[Tutorial](https://github.com/DooiLabs/FastApps/blob/main/docs/TUTORIAL.md)** - Step-by-step widget examples -- **[Python API](https://github.com/DooiLabs/FastApps/blob/main/docs/PYTHON_API.md)** - Programmatic dev server control -- **[API Reference](https://github.com/DooiLabs/FastApps/blob/main/docs/API.md)** - Complete API documentation -- **[Examples](https://github.com/DooiLabs/FastApps/tree/main/examples)** - Real-world code examples - ---- - -## CLI Commands - -```bash -# Initialize new project -fastapps init my-app - -# Create new widget (auto-generates both files) -fastapps create mywidget - -# Start development server with ngrok tunnel -fastapps dev - -# Start on custom port -fastapps dev --port 8080 - -# Reset ngrok auth token -fastapps reset-token - -# View authentication guide -fastapps auth-info -``` - -**Tip**: If `fastapps` command is not found, use: -```bash -python -m fastapps.cli.main -``` - ---- - -## Project Structure After `fastapps create` - -When you run `python -m fastapps.cli.main create my-widget`, you get: - -``` -my-app/ -├── server/ -│ ├── __init__.py -│ ├── main.py # Already setup (no edits needed) -│ ├── tools/ -│ │ ├── __init__.py -│ │ └── my_widget_tool.py # ← Edit this: Your widget logic -│ └── api/ # (optional: for shared APIs) -│ -├── widgets/ -│ └── my-widget/ -│ └── index.jsx # ← Edit this: Your UI -│ -├── assets/ # Auto-generated during build -│ ├── my-widget-HASH.html -│ └── my-widget-HASH.js -│ -├── requirements.txt # Python dependencies -├── package.json # JavaScript dependencies -└── build-all.mts # Auto-copied from fastapps -``` - -**You only edit the 2 files marked with ←** - ---- - -## Key Features - -- **Zero Boilerplate** - Just write your widget code -- **Auto-Discovery** - Widgets automatically registered -- **Type-Safe** - Pydantic for Python, TypeScript for React -- **CLI Tools** - Scaffold widgets instantly -- **Python API** - Programmatic server control -- **ngrok Integration** - Public URLs with one command -- **React Hooks** - Modern React patterns via `fastapps` -- **MCP Protocol** - Native ChatGPT integration - ---- - -## Python API - -Start dev servers programmatically: - -```python -from fastapps import start_dev_server - -# Simple usage -start_dev_server() - -# With configuration -start_dev_server( - port=8080, - auto_reload=True, - ngrok_token="your_token" -) - -# Get server info without starting -from fastapps import get_server_info -info = get_server_info(port=8001) -print(f"Public URL: {info.public_url}") -``` - -See [Python API Documentation](./docs/PYTHON_API.md) for more details. - ---- - -## Examples - -### Simple Widget - -```python -# server/tools/hello_tool.py -class HelloTool(BaseWidget): - identifier = "hello" - title = "Hello" - input_schema = HelloInput - - async def execute(self, input_data): - return {"message": "Hello World!"} -``` - -```jsx -// widgets/hello/index.jsx -export default function Hello() { - const props = useWidgetProps(); - return

{props.message}

; -} -``` - -### With API Call - -```python -async def execute(self, input_data): - async with httpx.AsyncClient() as client: - response = await client.get("https://api.example.com/data") - data = response.json() - return {"data": data} -``` - -### With State - -```jsx -function Counter() { - const [state, setState] = useWidgetState({ count: 0 }); - return ( - - ); -} -``` - -### Development Server with ngrok (pyngrok) - -**Real-World Use Cases:** - -| Use Case | Scenario | Benefits | -|----------|----------|----------| -| 🤖 **ChatGPT Development** | Develop ChatGPT custom actions locally | Test widgets in ChatGPT without deployment | -| 🪝 **Webhook Testing** | Test webhooks from Stripe, GitHub, Slack | Get real webhook events on localhost | -| 👥 **Remote Collaboration** | Share your local dev server with team | Instant demos without pushing code | -| 📱 **Mobile Testing** | Test mobile apps against local backend | Access localhost from phone/tablet | -| 🔌 **API Integration** | Third-party APIs need public callback URLs | Receive OAuth callbacks locally | -| 🏢 **Client Demos** | Show work-in-progress to clients | Professional public URL instantly | -| 🎓 **Workshops/Teaching** | Students access instructor's local server | Share examples in real-time | -| 🌐 **Cross-Browser Testing** | Test on BrowserStack, Sauce Labs | Cloud browsers access your localhost | -| 🔄 **CI/CD Preview** | Preview branches before deployment | Test PRs with temporary URLs | -| 🛠️ **IoT Development** | IoT devices callback to local server | Hardware talks to dev environment | - -#### Programmatic Start -```python -from fastapps import start_dev_server - -# Start with automatic ngrok tunnel -start_dev_server(port=8001) - -# With custom configuration -start_dev_server( - port=8080, - ngrok_token="your_token", - auto_reload=True -) -``` - -#### Get Server URLs for Testing -```python -from fastapps import get_server_info - -# Create tunnel and get URLs -info = get_server_info(port=8001) -print(f"Local: {info.local_url}") -print(f"Public: {info.public_url}") - -# Use in integration tests -import requests -response = requests.get(f"{info.public_url}/health") -``` - -#### Environment Variable Token -```python -import os -from fastapps import start_dev_server - -# Get token from environment (great for CI/CD) -token = os.getenv("NGROK_TOKEN") -start_dev_server(ngrok_token=token) -``` - -#### Error Handling -```python -from fastapps import start_dev_server, DevServerError, NgrokError - -try: - start_dev_server(port=8001) -except NgrokError as e: - print(f"ngrok tunnel failed: {e}") -except DevServerError as e: - print(f"Server error: {e}") -``` - -#### Automation Script -```python -#!/usr/bin/env python3 -from fastapps import start_dev_server -import sys - -if __name__ == "__main__": - try: - print("🚀 Starting FastApps with ngrok...") - start_dev_server( - port=8001, - auto_reload=True, - log_level="info" - ) - except KeyboardInterrupt: - print("\n✅ Server stopped") - sys.exit(0) -``` - -#### Real-World Example Scenarios - -**Scenario 1: ChatGPT Custom Action Development** -```python -# Start server for ChatGPT testing -from fastapps import start_dev_server - -# Auto-reload when you edit widgets -start_dev_server( - port=8001, - auto_reload=True # Restart on code changes -) -# Copy ngrok URL → ChatGPT Settings → Connectors → Add your URL -# Test widgets live in ChatGPT while developing! -``` - -**Scenario 2: Webhook Testing (Stripe, GitHub, etc.)** -```python -from fastapps import get_server_info - -# Get public URL for webhook configuration -info = get_server_info(port=8001) -print(f"Configure webhook URL: {info.public_url}/webhooks") - -# Add this URL to Stripe Dashboard → Webhooks -# Receive real webhook events on your localhost! -``` - -**Scenario 3: Team Demo / Client Preview** -```python -from fastapps import start_dev_server - -# Share work-in-progress with team -start_dev_server(port=8001) - -# Send the ngrok URL to your team/client -# They can access your local server instantly! -# No deployment needed -``` - -**Scenario 4: Mobile App Testing** -```python -from fastapps import get_server_info - -info = get_server_info(port=8001) - -# Use public URL in your mobile app config -print(f"API Base URL for mobile app: {info.public_url}") - -# Test your iOS/Android app against local backend -# No need for deployed staging server -``` - -**Scenario 5: OAuth Callback (Third-Party APIs)** -```python -from fastapps import start_dev_server - -# OAuth providers need public callback URL -start_dev_server(port=8001) - -# Register callback: https://your-ngrok-url.ngrok.io/auth/callback -# Test OAuth flow locally: -# 1. User clicks "Login with Google" -# 2. Google redirects to your ngrok URL -# 3. Your local server receives the callback -``` - -**Scenario 6: CI/CD Integration Testing** -```python -# .github/workflows/test.yml -import os -from fastapps import start_dev_server -import threading -import requests - -# Start server in background -def run_server(): - start_dev_server( - port=8001, - ngrok_token=os.getenv("NGROK_TOKEN") - ) - -# Run server in separate thread -server_thread = threading.Thread(target=run_server, daemon=True) -server_thread.start() - -# Run integration tests against public URL -# Perfect for testing webhooks, OAuth, etc. in CI -``` - -**Scenario 7: IoT/Embedded Device Testing** -```python -from fastapps import get_server_info - -# IoT devices need to callback to your server -info = get_server_info(port=8001) - -# Configure IoT device with this URL -print(f"Configure device callback: {info.public_url}/iot/callback") - -# Your Raspberry Pi, Arduino, etc. can now reach your localhost! -``` - ---- - -## Troubleshooting - -**Widget not loading?** -- Check `identifier` matches folder name -- Rebuild: `npm run build` -- Restart: `python server/main.py` - -**Import errors?** -```bash -pip install --upgrade fastapps -npm install fastapps@latest -``` - -**Need help?** Check our [docs](https://github.com/DooiLabs/FastApps/tree/main/docs) or [open an issue](https://github.com/DooiLabs/FastApps/issues) - ---- - -## Contributing - -We welcome contributions! Please see our contributing guidelines: - -- **[Contributing Guide](https://github.com/DooiLabs/FastApps/blob/main/CONTRIBUTING.md)** - How to contribute to FastApps -- **[Code Style Guide](https://github.com/DooiLabs/FastApps/blob/main/CODE_STYLE.md)** - Code formatting and style standards -- **[GitHub Workflows](https://github.com/DooiLabs/FastApps/blob/main/.github/WORKFLOWS.md)** - CI/CD documentation - -### Quick Start for Contributors - -```bash -# Fork and clone the repository -git clone https://github.com/YOUR_USERNAME/FastApps.git -cd FastApps - -# Install development dependencies -pip install -e ".[dev]" - -# Install pre-commit hooks -pip install pre-commit -pre-commit install - -# Make changes and ensure they pass checks -black . -ruff check --fix . -pytest - -# Submit a pull request -``` - -## License - -MIT © FastApps Team - -## Links - -- **PyPI**: https://pypi.org/project/fastapps/ -- **ChatJS Hooks**: https://www.npmjs.com/package/fastapps -- **GitHub**: https://github.com/DooiLabs/FastApps -- **MCP Spec**: https://modelcontextprotocol.io/ diff --git a/fastapps.egg-info/SOURCES.txt b/fastapps.egg-info/SOURCES.txt deleted file mode 100644 index 1f1d7b7..0000000 --- a/fastapps.egg-info/SOURCES.txt +++ /dev/null @@ -1,145 +0,0 @@ -.pre-commit-config.yaml -CLI_COMMANDS_UPDATED.md -CLI_EXAMPLES.md -CLI_UPDATE_SUMMARY.md -CODE_STYLE.md -CONTRIBUTING.md -DEVELOPMENT.md -LICENSE -MANIFEST.in -NGROK_INTEGRATION.md -OAUTH_TESTING_GUIDE.md -PYPI_RELEASE_v1.0.8.md -PYTHON_API_SUMMARY.md -README.md -RELEASE_SUMMARY.md -TESTING_SUMMARY.md -create_auth_test.sh -pyproject.toml -pytest.ini -setup.py -test_cli_auth.py -test_cli_simple.py -test_dev_command.py -./fastapps/__init__.py -./fastapps/dev_server.py -./fastapps/auth/__init__.py -./fastapps/auth/decorators.py -./fastapps/auth/verifier.py -./fastapps/builder/__init__.py -./fastapps/builder/compiler.py -./fastapps/cli/__init__.py -./fastapps/cli/main.py -./fastapps/cli/commands/__init__.py -./fastapps/cli/commands/create.py -./fastapps/cli/commands/dev.py -./fastapps/cli/commands/init.py -./fastapps/cli/commands/use.py -./fastapps/core/__init__.py -./fastapps/core/server.py -./fastapps/core/widget.py -./fastapps/types/__init__.py -./fastapps/types/schema.py -.github/WORKFLOWS.md -.github/dependabot.yml -.github/release-drafter.yml -.github/workflows/ci.yml -.github/workflows/codeql.yml -.github/workflows/dependency-review.yml -.github/workflows/publish.yml -.github/workflows/release-drafter.yml -.github/workflows/test-publish.yml -dist/fastapps-1.0.8-py3-none-any.whl -dist/fastapps-1.0.8.tar.gz -docs/01-INTRO.md -docs/02-WIDGETS.md -docs/03-TOOLS.md -docs/04-STATE.md -docs/05-STYLING.md -docs/06-API.md -docs/08-AUTH.md -docs/09-PER-WIDGET-AUTH.md -docs/API.md -docs/PROJECT_SETUP.md -docs/PYTHON_API.md -docs/QUICKSTART.md -docs/TUTORIAL.md -examples/README.md -examples/python_api_advanced.py -examples/python_api_basic.py -examples/python_api_error_handling.py -examples/python_api_get_info.py -examples/python_api_with_token.py -examples/__pycache__/python_api_advanced.cpython-312.pyc -examples/__pycache__/python_api_basic.cpython-312.pyc -examples/__pycache__/python_api_get_info.cpython-312.pyc -fastapps/__init__.py -fastapps/dev_server.py -fastapps.egg-info/PKG-INFO -fastapps.egg-info/SOURCES.txt -fastapps.egg-info/dependency_links.txt -fastapps.egg-info/entry_points.txt -fastapps.egg-info/requires.txt -fastapps.egg-info/top_level.txt -fastapps/__pycache__/__init__.cpython-311.pyc -fastapps/__pycache__/__init__.cpython-312.pyc -fastapps/__pycache__/__init__.cpython-313.pyc -fastapps/__pycache__/__init__.cpython-39.pyc -fastapps/__pycache__/dev_server.cpython-312.pyc -fastapps/__pycache__/dev_server.cpython-313.pyc -fastapps/auth/__init__.py -fastapps/auth/decorators.py -fastapps/auth/verifier.py -fastapps/auth/__pycache__/__init__.cpython-313.pyc -fastapps/auth/__pycache__/decorators.cpython-313.pyc -fastapps/auth/__pycache__/verifier.cpython-313.pyc -fastapps/builder/__init__.py -fastapps/builder/build-all.mts -fastapps/builder/compiler.py -fastapps/builder/__pycache__/__init__.cpython-311.pyc -fastapps/builder/__pycache__/__init__.cpython-313.pyc -fastapps/builder/__pycache__/compiler.cpython-311.pyc -fastapps/builder/__pycache__/compiler.cpython-313.pyc -fastapps/cli/__init__.py -fastapps/cli/main.py -fastapps/cli/__pycache__/__init__.cpython-313.pyc -fastapps/cli/__pycache__/main.cpython-312.pyc -fastapps/cli/__pycache__/main.cpython-313.pyc -fastapps/cli/commands/__init__.py -fastapps/cli/commands/create.py -fastapps/cli/commands/dev.py -fastapps/cli/commands/init.py -fastapps/cli/commands/use.py -fastapps/cli/commands/__pycache__/__init__.cpython-313.pyc -fastapps/cli/commands/__pycache__/create.cpython-313.pyc -fastapps/cli/commands/__pycache__/dev.cpython-312.pyc -fastapps/cli/commands/__pycache__/dev.cpython-313.pyc -fastapps/cli/commands/__pycache__/init.cpython-313.pyc -fastapps/cli/commands/__pycache__/use.cpython-313.pyc -fastapps/core/__init__.py -fastapps/core/server.py -fastapps/core/widget.py -fastapps/core/__pycache__/__init__.cpython-311.pyc -fastapps/core/__pycache__/__init__.cpython-312.pyc -fastapps/core/__pycache__/__init__.cpython-313.pyc -fastapps/core/__pycache__/__init__.cpython-39.pyc -fastapps/core/__pycache__/server.cpython-311.pyc -fastapps/core/__pycache__/server.cpython-313.pyc -fastapps/core/__pycache__/widget.cpython-311.pyc -fastapps/core/__pycache__/widget.cpython-312.pyc -fastapps/core/__pycache__/widget.cpython-313.pyc -fastapps/core/__pycache__/widget.cpython-39.pyc -fastapps/types/__init__.py -fastapps/types/schema.py -fastapps/types/__pycache__/__init__.cpython-311.pyc -fastapps/types/__pycache__/__init__.cpython-313.pyc -fastapps/types/__pycache__/schema.cpython-311.pyc -fastapps/types/__pycache__/schema.cpython-313.pyc -tests/__init__.py -tests/conftest.py -tests/test_import.py -tests/test_widget.py -tests/__pycache__/__init__.cpython-312.pyc -tests/__pycache__/conftest.cpython-312-pytest-8.4.2.pyc -tests/__pycache__/test_import.cpython-312-pytest-8.4.2.pyc -tests/__pycache__/test_widget.cpython-312-pytest-8.4.2.pyc \ No newline at end of file diff --git a/fastapps.egg-info/dependency_links.txt b/fastapps.egg-info/dependency_links.txt deleted file mode 100644 index 8b13789..0000000 --- a/fastapps.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/fastapps.egg-info/entry_points.txt b/fastapps.egg-info/entry_points.txt deleted file mode 100644 index cbe69b3..0000000 --- a/fastapps.egg-info/entry_points.txt +++ /dev/null @@ -1,2 +0,0 @@ -[console_scripts] -fastapps = fastapps.cli.main:cli diff --git a/fastapps.egg-info/requires.txt b/fastapps.egg-info/requires.txt deleted file mode 100644 index 8866715..0000000 --- a/fastapps.egg-info/requires.txt +++ /dev/null @@ -1,16 +0,0 @@ -fastmcp>=0.1.0 -pydantic>=2.0.0 -uvicorn>=0.20.0 -click>=8.0.0 -rich>=13.0.0 -httpx>=0.28.0 -PyJWT>=2.8.0 -cryptography>=41.0.0 -pyngrok>=7.4.0 - -[dev] -pytest>=7.0.0 -pytest-asyncio>=0.21.0 -pytest-cov>=4.0.0 -black>=23.0.0 -ruff>=0.1.0 diff --git a/fastapps.egg-info/top_level.txt b/fastapps.egg-info/top_level.txt deleted file mode 100644 index 4853af7..0000000 --- a/fastapps.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -fastapps diff --git a/setup.py b/setup.py index 350c9bf..fa2d1cb 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -from setuptools import setup, find_packages +from setuptools import find_packages, setup with open("README.md", "r", encoding="utf-8") as fh: long_description = fh.read() diff --git a/test_cli_auth.py b/test_cli_auth.py deleted file mode 100644 index 72ea3e0..0000000 --- a/test_cli_auth.py +++ /dev/null @@ -1,280 +0,0 @@ -""" -Test script for CLI auth features. -Tests the generate_tool_code function and CLI logic. -""" - -import sys -from pathlib import Path - -# Add FastApps to path -sys.path.insert(0, str(Path(__file__).parent)) - -print("=" * 70) -print("FastApps CLI Auth Features - Test Suite") -print("=" * 70) - -# Test 1: Import CLI functions -print("\n[TEST 1] Testing CLI imports...") -try: - from fastapps.cli.commands.create import generate_tool_code - - print("✓ generate_tool_code imported") -except ImportError as e: - print(f"✗ Import failed: {e}") - sys.exit(1) - -# Test 2: Generate code with --auth -print("\n[TEST 2] Testing --auth flag...") -try: - code = generate_tool_code( - class_name="TestWidget", - identifier="test-widget", - title="Test Widget", - auth_type="required", - scopes=["user", "read:data"], - ) - - # Verify code includes correct elements - assert ( - "from fastapps import BaseWidget, ConfigDict, auth_required, UserContext" - in code - ), "Should import auth_required and UserContext" - assert ( - '@auth_required(scopes=["user", "read:data"])' in code - ), "Should have decorator with scopes" - assert ( - 'description = "Requires authentication (user, read:data)"' in code - ), "Should have description" - assert "if user and user.is_authenticated:" in code, "Should check authentication" - assert "user.claims" in code, "Should access user claims" - assert "user.subject" in code, "Should access user subject" - assert "user.scopes" in code, "Should access user scopes" - - print("✓ Generated code with @auth_required") - print(" - Correct imports") - print(" - Decorator with scopes") - print(" - User context handling") - -except Exception as e: - print(f"✗ --auth test failed: {e}") - import traceback - - traceback.print_exc() - sys.exit(1) - -# Test 3: Generate code with --public -print("\n[TEST 3] Testing --public flag...") -try: - code = generate_tool_code( - class_name="PublicWidget", - identifier="public-widget", - title="Public Widget", - auth_type="none", - scopes=None, - ) - - assert ( - "from fastapps import BaseWidget, ConfigDict, no_auth" in code - ), "Should import no_auth" - assert "UserContext" not in code, "Should not import UserContext for public widgets" - assert "@no_auth" in code, "Should have @no_auth decorator" - assert ( - 'description = "Public widget - no authentication required"' in code - ), "Should have public description" - assert ( - "if user and user.is_authenticated:" not in code - ), "Should not check auth for simple public widgets" - - print("✓ Generated code with @no_auth") - print(" - Correct imports (no UserContext)") - print(" - @no_auth decorator") - print(" - Simple execute body") - -except Exception as e: - print(f"✗ --public test failed: {e}") - import traceback - - traceback.print_exc() - sys.exit(1) - -# Test 4: Generate code with --optional-auth -print("\n[TEST 4] Testing --optional-auth flag...") -try: - code = generate_tool_code( - class_name="FlexibleWidget", - identifier="flexible-widget", - title="Flexible Widget", - auth_type="optional", - scopes=["user"], - ) - - assert ( - "from fastapps import BaseWidget, ConfigDict, optional_auth, UserContext" - in code - ), "Should import optional_auth and UserContext" - assert ( - '@optional_auth(scopes=["user"])' in code - ), "Should have decorator with scopes" - assert ( - 'description = "Supports both authenticated and anonymous access"' in code - ), "Should have optional description" - assert "if user and user.is_authenticated:" in code, "Should check authentication" - - print("✓ Generated code with @optional_auth") - print(" - Correct imports") - print(" - Decorator with scopes") - print(" - Conditional auth handling") - -except Exception as e: - print(f"✗ --optional-auth test failed: {e}") - import traceback - - traceback.print_exc() - sys.exit(1) - -# Test 5: Generate code without auth (default) -print("\n[TEST 5] Testing default (no auth flags)...") -try: - code = generate_tool_code( - class_name="BasicWidget", - identifier="basic-widget", - title="Basic Widget", - auth_type=None, - scopes=None, - ) - - assert ( - "# from fastapps import auth_required, no_auth, optional_auth, UserContext" - in code - ), "Should have commented auth imports" - assert ( - "# @auth_required(scopes=[" in code - ), "Should have commented decorator examples" - assert "# @no_auth" in code, "Should have @no_auth example" - assert "# @optional_auth(scopes=[" in code, "Should have @optional_auth example" - assert ( - "# if user and user.is_authenticated:" in code - ), "Should have commented user handling" - - print("✓ Generated code without auth flags") - print(" - Commented import examples") - print(" - Commented decorator examples") - print(" - Commented user handling examples") - -except Exception as e: - print(f"✗ Default test failed: {e}") - import traceback - - traceback.print_exc() - sys.exit(1) - -# Test 6: Scopes formatting -print("\n[TEST 6] Testing scopes formatting...") -try: - # Test with multiple scopes - code1 = generate_tool_code( - "Test", "test", "Test", "required", ["user", "read:data", "write:data"] - ) - assert ( - '@auth_required(scopes=["user", "read:data", "write:data"])' in code1 - ), "Should format multiple scopes" - print("✓ Multiple scopes formatted correctly") - - # Test with single scope - code2 = generate_tool_code("Test", "test", "Test", "required", ["admin"]) - assert '@auth_required(scopes=["admin"])' in code2, "Should format single scope" - print("✓ Single scope formatted correctly") - - # Test with no scopes - code3 = generate_tool_code("Test", "test", "Test", "required", None) - assert "@auth_required(scopes=[])" in code3, "Should have empty scopes array" - print("✓ No scopes formatted correctly") - -except Exception as e: - print(f"✗ Scopes formatting test failed: {e}") - import traceback - - traceback.print_exc() - sys.exit(1) - -# Test 7: Verify code structure for all types -print("\n[TEST 7] Verifying code structure...") -try: - for auth_type, decorator_name in [ - ("required", "@auth_required"), - ("none", "@no_auth"), - ("optional", "@optional_auth"), - ]: - code = generate_tool_code("Test", "test", "Test", auth_type, ["user"]) - - # All should have these basic elements - assert ( - "class TestInput(BaseModel):" in code - ), f"{auth_type}: Should have Input class" - assert ( - "class TestTool(BaseWidget):" in code - ), f"{auth_type}: Should have Tool class" - assert 'identifier = "test"' in code, f"{auth_type}: Should have identifier" - assert 'title = "Test"' in code, f"{auth_type}: Should have title" - assert "async def execute" in code, f"{auth_type}: Should have execute method" - assert "widget_csp" in code, f"{auth_type}: Should have CSP config" - - print(f"✓ {auth_type}: All required elements present") - -except Exception as e: - print(f"✗ Structure test failed: {e}") - import traceback - - traceback.print_exc() - sys.exit(1) - -# Test 8: Check execute signature -print("\n[TEST 8] Testing execute signature...") -try: - code = generate_tool_code("Test", "test", "Test", "required", ["user"]) - - # Should have all three parameters - assert ( - "async def execute(self, input_data: TestInput, context=None, user=None)" - in code - ), "Should have correct execute signature with user parameter" - - print("✓ Execute signature includes user parameter") - -except Exception as e: - print(f"✗ Execute signature test failed: {e}") - import traceback - - traceback.print_exc() - sys.exit(1) - -# Final summary -print("\n" + "=" * 70) -print("Test Summary") -print("=" * 70) -print( - """ -✓ generate_tool_code function works -✓ --auth flag generates correct code -✓ --public flag generates correct code -✓ --optional-auth flag generates correct code -✓ Default (no flags) includes commented examples -✓ Scopes formatted correctly -✓ All code structures valid -✓ Execute signature includes user parameter - -All CLI tests PASSED! ✓ - -CLI Commands Ready: - fastapps create name --auth --scopes s1,s2 - fastapps create name --public - fastapps create name --optional-auth --scopes s1 - fastapps auth-info - -Documentation: - - CLI_COMMANDS_UPDATED.md - Complete reference - - CLI_EXAMPLES.md - Usage examples - - CLI_UPDATE_SUMMARY.md - Update summary -""" -) -print("=" * 70) diff --git a/test_cli_simple.py b/test_cli_simple.py deleted file mode 100644 index 8a1c231..0000000 --- a/test_cli_simple.py +++ /dev/null @@ -1,193 +0,0 @@ -""" -Simple test for CLI auth features without requiring dependencies. -""" - -from pathlib import Path -import re - -fastapps_dir = Path(__file__).parent - -print("=" * 70) -print("FastApps CLI Auth Features - Structure Test") -print("=" * 70) - -# Test 1: Check generate_tool_code function exists -print("\n[TEST 1] Checking generate_tool_code function...") -create_file = fastapps_dir / "fastapps/cli/commands/create.py" -create_code = create_file.read_text() - -if "def generate_tool_code(" in create_code: - print("✓ generate_tool_code function defined") - - # Check function signature - sig_match = re.search(r"def generate_tool_code\((.*?)\):", create_code, re.DOTALL) - if sig_match: - sig = sig_match.group(1) - params = ["class_name", "identifier", "title", "auth_type", "scopes"] - missing = [p for p in params if p not in sig] - if not missing: - print(f"✓ Function signature includes all parameters: {', '.join(params)}") - else: - print(f"✗ Missing parameters: {', '.join(missing)}") -else: - print("✗ generate_tool_code function not found") - sys.exit(1) - -# Test 2: Check CLI main.py updates -print("\n[TEST 2] Checking CLI main.py...") -main_file = fastapps_dir / "fastapps/cli/main.py" -main_code = main_file.read_text() - -main_checks = [ - ('version="1.0.7"', "Version updated to 1.0.7"), - ("--auth", "--auth flag added"), - ("--public", "--public flag added"), - ("--optional-auth", "--optional-auth flag added"), - ("--scopes", "--scopes flag added"), - ("def auth_info()", "auth_info command added"), - ("auth_type=", "auth_type parameter passed"), - ("scopes=", "scopes parameter passed"), -] - -for check_str, description in main_checks: - if check_str in main_code: - print(f"✓ {description}") - else: - print(f"✗ {description} - NOT FOUND") - -# Test 3: Verify decorator logic -print("\n[TEST 3] Checking decorator generation logic...") - -decorator_patterns = { - "required": "@auth_required(scopes=", - "none": "@no_auth", - "optional": "@optional_auth(scopes=", -} - -for auth_type, pattern in decorator_patterns.items(): - if pattern in create_code: - print(f"✓ {auth_type}: '{pattern}' pattern found") - else: - print(f"✗ {auth_type}: '{pattern}' pattern NOT FOUND") - -# Test 4: Check import generation -print("\n[TEST 4] Checking import generation...") - -import_patterns = [ - ('auth_type == "required"', "auth_required, UserContext"), - ('auth_type == "none"', "no_auth"), - ('auth_type == "optional"', "optional_auth, UserContext"), -] - -for condition, imports in import_patterns: - if condition in create_code and imports in create_code: - print(f"✓ Conditional import for {imports}") - else: - print(f"⚠ Check import logic for {imports}") - -# Test 5: Check execute body generation -print("\n[TEST 5] Checking execute body generation...") - -if 'auth_type in ["required", "optional"]' in create_code: - print("✓ Auth-aware execute body for required/optional") -else: - print("⚠ Check execute body logic") - -if "user and user.is_authenticated" in create_code: - print("✓ User authentication check included") -else: - print("✗ User authentication check missing") - -# Test 6: Check output enhancements -print("\n[TEST 6] Checking CLI output enhancements...") - -output_checks = [ - ('if auth_type == "required":', "Auth required output"), - ("🔒 Authentication: Required", "Auth required icon"), - ("🌐 Authentication: Public", "Public icon"), - ("🔓 Authentication: Optional", "Optional icon"), - ("ℹ️ Authentication: Not configured", "Not configured message"), -] - -for check_str, description in output_checks: - if check_str in create_code: - print(f"✓ {description}") - else: - print(f"⚠ {description}") - -# Test 7: Simulate code generation -print("\n[TEST 7] Simulating code generation...") - -test_cases = [ - ("required", ["admin"], "auth_required", "admin"), - ("none", None, "no_auth", None), - ("optional", ["user"], "optional_auth", "user"), -] - -# We can't actually run generate_tool_code without dependencies, -# but we can verify the logic exists -if "def generate_tool_code" in create_code: - print("✓ Code generation function structure verified") - print(" Test cases that would be generated:") - for auth_type, scopes, decorator, scope_name in test_cases: - scope_str = f" with scopes: {', '.join(scopes)}" if scopes else "" - print(f" - auth_type='{auth_type}'{scope_str} → @{decorator}") - -# Test 8: Check create_widget signature -print("\n[TEST 8] Checking create_widget signature...") - -if ( - "def create_widget(name: str, auth_type: str = None, scopes: list = None):" - in create_code -): - print("✓ create_widget accepts auth_type and scopes parameters") -else: - print("✗ create_widget signature not updated") - -# Test 9: Verify tool_content generation -print("\n[TEST 9] Checking tool_content generation...") - -if "tool_content = generate_tool_code(" in create_code: - print("✓ tool_content uses generate_tool_code function") - - # Check parameters are passed - params = ["class_name=", "identifier=", "title=", "auth_type=", "scopes="] - for param in params: - if param in create_code: - print(f" ✓ {param} parameter passed") - else: - print(f" ✗ {param} parameter NOT passed") -else: - print("✗ tool_content not using generate_tool_code") - -# Final summary -print("\n" + "=" * 70) -print("Test Summary") -print("=" * 70) -print( - """ -✓ generate_tool_code function defined with correct signature -✓ CLI main.py updated with auth flags -✓ auth_info command added -✓ Decorator generation logic implemented -✓ Import generation conditional on auth_type -✓ Execute body varies based on auth_type -✓ CLI output includes auth status indicators -✓ create_widget signature updated -✓ Code generation integrated - -All structure tests PASSED! ✓ - -Updated CLI Commands: - fastapps create name --auth --scopes user,read:data - fastapps create name --public - fastapps create name --optional-auth --scopes user - fastapps auth-info - -To test with real project: - 1. Install dependencies: pip install click rich pydantic - 2. Run: fastapps create test --auth --scopes user - 3. Check generated files in server/tools/ and widgets/ -""" -) -print("=" * 70) diff --git a/test_dev_command.py b/test_dev_command.py deleted file mode 100644 index caf8708..0000000 --- a/test_dev_command.py +++ /dev/null @@ -1,203 +0,0 @@ -"""Test script for the new fastapps dev command with ngrok integration.""" - -import sys -from pathlib import Path - - -def test_imports(): - """Test that all required modules can be imported.""" - print("Testing imports...") - - try: - from fastapps.cli.commands.dev import ( - get_config_dir, - get_config_file, - load_config, - save_config, - get_ngrok_token, - set_ngrok_auth, - start_dev_server, - reset_ngrok_token, - ) - - print("✓ All dev.py functions imported successfully") - except ImportError as e: - print(f"✗ Import error in dev.py: {e}") - return False - - try: - from fastapps.cli.main import cli - - print("✓ CLI imported successfully") - except ImportError as e: - print(f"✗ Import error in main.py: {e}") - return False - - return True - - -def test_config_management(): - """Test configuration file management.""" - print("\nTesting config management...") - - from fastapps.cli.commands.dev import ( - get_config_dir, - get_config_file, - save_config, - load_config, - ) - - # Test config directory creation - config_dir = get_config_dir() - if config_dir.exists(): - print(f"✓ Config directory: {config_dir}") - else: - print(f"✗ Config directory not created: {config_dir}") - return False - - # Test config file path - config_file = get_config_file() - print(f"✓ Config file path: {config_file}") - - # Test save and load - test_config = {"test_key": "test_value", "ngrok_token": "test_token_123"} - if save_config(test_config): - print("✓ Config saved successfully") - else: - print("✗ Failed to save config") - return False - - loaded_config = load_config() - if loaded_config.get("test_key") == "test_value": - print("✓ Config loaded successfully") - else: - print("✗ Failed to load config correctly") - return False - - # Clean up test config - config_file.unlink(missing_ok=True) - print("✓ Test config cleaned up") - - return True - - -def test_cli_structure(): - """Test CLI command structure.""" - print("\nTesting CLI structure...") - - try: - from fastapps.cli.main import cli - from click.testing import CliRunner - - runner = CliRunner() - - # Test --help - result = runner.invoke(cli, ["--help"]) - if result.exit_code == 0: - print("✓ CLI --help works") - - # Check if dev command is listed - if "dev" in result.output: - print("✓ 'dev' command is registered") - else: - print("✗ 'dev' command not found in help") - return False - - # Check if reset-token command is listed - if "reset-token" in result.output or "reset_token" in result.output: - print("✓ 'reset-token' command is registered") - else: - print("✗ 'reset-token' command not found in help") - return False - else: - print(f"✗ CLI --help failed with exit code: {result.exit_code}") - return False - - # Test dev --help - result = runner.invoke(cli, ["dev", "--help"]) - if result.exit_code == 0: - print("✓ 'fastapps dev --help' works") - - # Check for key features in help text - if "ngrok" in result.output.lower(): - print("✓ ngrok mentioned in dev command help") - if "--port" in result.output: - print("✓ --port option available") - if "--host" in result.output: - print("✓ --host option available") - else: - print(f"✗ 'fastapps dev --help' failed") - return False - - return True - - except Exception as e: - print(f"✗ CLI structure test failed: {e}") - return False - - -def test_syntax(): - """Test Python syntax of new files.""" - print("\nTesting Python syntax...") - - import py_compile - - files_to_check = ["fastapps/cli/commands/dev.py", "fastapps/cli/main.py"] - - all_valid = True - for file_path in files_to_check: - try: - py_compile.compile(file_path, doraise=True) - print(f"✓ {file_path}: syntax valid") - except py_compile.PyCompileError as e: - print(f"✗ {file_path}: syntax error - {e}") - all_valid = False - - return all_valid - - -def main(): - """Run all tests.""" - print("=" * 60) - print("FastApps Dev Command Test Suite") - print("=" * 60) - - tests = [ - ("Syntax Check", test_syntax), - ("Import Check", test_imports), - ("Config Management", test_config_management), - ("CLI Structure", test_cli_structure), - ] - - results = {} - for test_name, test_func in tests: - try: - results[test_name] = test_func() - except Exception as e: - print(f"\n✗ {test_name} crashed: {e}") - results[test_name] = False - - # Summary - print("\n" + "=" * 60) - print("Test Summary") - print("=" * 60) - - passed = sum(1 for result in results.values() if result) - total = len(results) - - for test_name, result in results.items(): - status = "✓ PASS" if result else "✗ FAIL" - print(f"{status}: {test_name}") - - print(f"\nTotal: {passed}/{total} tests passed") - - if passed == total: - print("\n🎉 All tests passed! The dev command is ready to use.") - return 0 - else: - print(f"\n⚠️ {total - passed} test(s) failed. Please review the errors above.") - return 1 - - -if __name__ == "__main__": - sys.exit(main()) From 794619292d1090e43eed32b8af2298aeccf4bbbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EC=A7=80=ED=98=81?= Date: Tue, 28 Oct 2025 17:13:27 +0900 Subject: [PATCH 3/9] chore : added example application --- examples/fastapps-demo/.gitignore | 42 + examples/fastapps-demo/README.md | 47 + examples/fastapps-demo/package-lock.json | 2094 +++++++++++++++++ examples/fastapps-demo/package.json | 21 + examples/fastapps-demo/requirements.txt | 2 + examples/fastapps-demo/server/__init__.py | 0 examples/fastapps-demo/server/api/__init__.py | 0 examples/fastapps-demo/server/main.py | 63 + .../fastapps-demo/server/tools/__init__.py | 0 .../server/tools/my_widget_tool.py | 30 + .../fastapps-demo/widgets/my_widget/index.jsx | 19 + 11 files changed, 2318 insertions(+) create mode 100644 examples/fastapps-demo/.gitignore create mode 100644 examples/fastapps-demo/README.md create mode 100644 examples/fastapps-demo/package-lock.json create mode 100644 examples/fastapps-demo/package.json create mode 100644 examples/fastapps-demo/requirements.txt create mode 100644 examples/fastapps-demo/server/__init__.py create mode 100644 examples/fastapps-demo/server/api/__init__.py create mode 100644 examples/fastapps-demo/server/main.py create mode 100644 examples/fastapps-demo/server/tools/__init__.py create mode 100644 examples/fastapps-demo/server/tools/my_widget_tool.py create mode 100644 examples/fastapps-demo/widgets/my_widget/index.jsx diff --git a/examples/fastapps-demo/.gitignore b/examples/fastapps-demo/.gitignore new file mode 100644 index 0000000..ee56dcf --- /dev/null +++ b/examples/fastapps-demo/.gitignore @@ -0,0 +1,42 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +venv/ +ENV/ +env/ +.venv + +# JavaScript +node_modules/ +npm-debug.log* +*.log +dist/ +.cache/ + +# Build outputs +assets/ +build-all.mts + +# IDEs +.vscode/ +.idea/ +*.swp +.DS_Store diff --git a/examples/fastapps-demo/README.md b/examples/fastapps-demo/README.md new file mode 100644 index 0000000..f4c13e5 --- /dev/null +++ b/examples/fastapps-demo/README.md @@ -0,0 +1,47 @@ +# fastapps-demo + +ChatGPT widgets built with [FastApps](https://pypi.org/project/fastapps/). + +## Quick Start + +Your project includes an example widget (`my_widget`) to get you started! + +```bash +fastapps dev +``` + +This will build your widgets and start the development server. + +## Creating More Widgets + +```bash +fastapps create another_widget +fastapps dev +``` + +## Project Structure + +``` +fastapps-demo/ +├── server/ +│ ├── main.py # Server (auto-discovery) +│ └── tools/ # Widget backends +│ └── my_widget_tool.py # Example widget +│ +├── widgets/ # Widget frontends +│ └── my_widget/ +│ └── index.jsx # Example React component +│ +├── assets/ # Built widgets (auto-generated) +└── package.json +``` + +## Learn More + +- **FastApps Framework**: https://pypi.org/project/fastapps/ +- **FastApps (React)**: https://www.npmjs.com/package/fastapps +- **Documentation**: https://github.com/fastapps-framework/fastapps + +## License + +MIT diff --git a/examples/fastapps-demo/package-lock.json b/examples/fastapps-demo/package-lock.json new file mode 100644 index 0000000..955d513 --- /dev/null +++ b/examples/fastapps-demo/package-lock.json @@ -0,0 +1,2094 @@ +{ + "name": "fastapps-demo", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "fastapps-demo", + "version": "1.0.0", + "dependencies": { + "fastapps": "^1.0.0", + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "devDependencies": { + "@vitejs/plugin-react": "^4.3.4", + "fast-glob": "^3.3.2", + "tsx": "^4.19.2", + "typescript": "^5.7.2", + "vite": "^6.0.5" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz", + "integrity": "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.11.tgz", + "integrity": "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.11.tgz", + "integrity": "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.11.tgz", + "integrity": "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.11.tgz", + "integrity": "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.11.tgz", + "integrity": "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.11.tgz", + "integrity": "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.11.tgz", + "integrity": "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.11.tgz", + "integrity": "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.11.tgz", + "integrity": "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.11.tgz", + "integrity": "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.11.tgz", + "integrity": "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.11.tgz", + "integrity": "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.11.tgz", + "integrity": "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.11.tgz", + "integrity": "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.11.tgz", + "integrity": "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.11.tgz", + "integrity": "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.11.tgz", + "integrity": "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.11.tgz", + "integrity": "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.11.tgz", + "integrity": "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.11.tgz", + "integrity": "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.11.tgz", + "integrity": "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.11.tgz", + "integrity": "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.11.tgz", + "integrity": "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.11.tgz", + "integrity": "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.11.tgz", + "integrity": "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.5.tgz", + "integrity": "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.5.tgz", + "integrity": "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.5.tgz", + "integrity": "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.5.tgz", + "integrity": "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.5.tgz", + "integrity": "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.5.tgz", + "integrity": "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.5.tgz", + "integrity": "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.5.tgz", + "integrity": "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.5.tgz", + "integrity": "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.5.tgz", + "integrity": "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.5.tgz", + "integrity": "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.5.tgz", + "integrity": "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.5.tgz", + "integrity": "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.5.tgz", + "integrity": "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.5.tgz", + "integrity": "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.5.tgz", + "integrity": "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.5.tgz", + "integrity": "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.5.tgz", + "integrity": "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.5.tgz", + "integrity": "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.5.tgz", + "integrity": "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.5.tgz", + "integrity": "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.5.tgz", + "integrity": "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.20", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.20.tgz", + "integrity": "sha512-JMWsdF+O8Orq3EMukbUN1QfbLK9mX2CkUmQBcW2T0s8OmdAUL5LLM/6wFwSrqXzlXB13yhyK9gTKS1rIizOduQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz", + "integrity": "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.8.19", + "caniuse-lite": "^1.0.30001751", + "electron-to-chromium": "^1.5.238", + "node-releases": "^2.0.26", + "update-browserslist-db": "^1.1.4" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001751", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001751.tgz", + "integrity": "sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.241", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.241.tgz", + "integrity": "sha512-ILMvKX/ZV5WIJzzdtuHg8xquk2y0BOGlFOxBVwTpbiXqWIH0hamG45ddU4R3PQ0gYu+xgo0vdHXHli9sHIGb4w==", + "dev": true, + "license": "ISC" + }, + "node_modules/esbuild": { + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.11.tgz", + "integrity": "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.11", + "@esbuild/android-arm": "0.25.11", + "@esbuild/android-arm64": "0.25.11", + "@esbuild/android-x64": "0.25.11", + "@esbuild/darwin-arm64": "0.25.11", + "@esbuild/darwin-x64": "0.25.11", + "@esbuild/freebsd-arm64": "0.25.11", + "@esbuild/freebsd-x64": "0.25.11", + "@esbuild/linux-arm": "0.25.11", + "@esbuild/linux-arm64": "0.25.11", + "@esbuild/linux-ia32": "0.25.11", + "@esbuild/linux-loong64": "0.25.11", + "@esbuild/linux-mips64el": "0.25.11", + "@esbuild/linux-ppc64": "0.25.11", + "@esbuild/linux-riscv64": "0.25.11", + "@esbuild/linux-s390x": "0.25.11", + "@esbuild/linux-x64": "0.25.11", + "@esbuild/netbsd-arm64": "0.25.11", + "@esbuild/netbsd-x64": "0.25.11", + "@esbuild/openbsd-arm64": "0.25.11", + "@esbuild/openbsd-x64": "0.25.11", + "@esbuild/openharmony-arm64": "0.25.11", + "@esbuild/sunos-x64": "0.25.11", + "@esbuild/win32-arm64": "0.25.11", + "@esbuild/win32-ia32": "0.25.11", + "@esbuild/win32-x64": "0.25.11" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastapps": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fastapps/-/fastapps-1.1.0.tgz", + "integrity": "sha512-Cxzp7xN9nM+Me51T/WX7TMeRg3KSn0a3Mw0381FrrzycMAvR40cQwPSc4j257oug6frvLQ6y99i+8nlVZTsumA==", + "license": "MIT", + "bin": { + "fastapps-build": "build-all.mts" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", + "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.26", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.26.tgz", + "integrity": "sha512-S2M9YimhSjBSvYnlr5/+umAnPHE++ODwt5e2Ij6FoX45HA/s4vHdkDx1eax2pAPeAOqu4s9b7ppahsyEFdVqQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.5.tgz", + "integrity": "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.52.5", + "@rollup/rollup-android-arm64": "4.52.5", + "@rollup/rollup-darwin-arm64": "4.52.5", + "@rollup/rollup-darwin-x64": "4.52.5", + "@rollup/rollup-freebsd-arm64": "4.52.5", + "@rollup/rollup-freebsd-x64": "4.52.5", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", + "@rollup/rollup-linux-arm-musleabihf": "4.52.5", + "@rollup/rollup-linux-arm64-gnu": "4.52.5", + "@rollup/rollup-linux-arm64-musl": "4.52.5", + "@rollup/rollup-linux-loong64-gnu": "4.52.5", + "@rollup/rollup-linux-ppc64-gnu": "4.52.5", + "@rollup/rollup-linux-riscv64-gnu": "4.52.5", + "@rollup/rollup-linux-riscv64-musl": "4.52.5", + "@rollup/rollup-linux-s390x-gnu": "4.52.5", + "@rollup/rollup-linux-x64-gnu": "4.52.5", + "@rollup/rollup-linux-x64-musl": "4.52.5", + "@rollup/rollup-openharmony-arm64": "4.52.5", + "@rollup/rollup-win32-arm64-msvc": "4.52.5", + "@rollup/rollup-win32-ia32-msvc": "4.52.5", + "@rollup/rollup-win32-x64-gnu": "4.52.5", + "@rollup/rollup-win32-x64-msvc": "4.52.5", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tsx": { + "version": "4.20.6", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.6.tgz", + "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", + "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/vite": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", + "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + } + } +} diff --git a/examples/fastapps-demo/package.json b/examples/fastapps-demo/package.json new file mode 100644 index 0000000..fc66afe --- /dev/null +++ b/examples/fastapps-demo/package.json @@ -0,0 +1,21 @@ +{ + "name": "fastapps-demo", + "version": "1.0.0", + "type": "module", + "description": "Floydr ChatGPT widgets project", + "scripts": { + "build": "npx tsx node_modules/fastapps/build-all.mts" + }, + "dependencies": { + "fastapps": "^1.0.0", + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "devDependencies": { + "@vitejs/plugin-react": "^4.3.4", + "fast-glob": "^3.3.2", + "tsx": "^4.19.2", + "typescript": "^5.7.2", + "vite": "^6.0.5" + } +} \ No newline at end of file diff --git a/examples/fastapps-demo/requirements.txt b/examples/fastapps-demo/requirements.txt new file mode 100644 index 0000000..4d523fc --- /dev/null +++ b/examples/fastapps-demo/requirements.txt @@ -0,0 +1,2 @@ +# All dependencies included in fastapps package +# pip install fastapps (or: uv pip install fastapps) is all you need! diff --git a/examples/fastapps-demo/server/__init__.py b/examples/fastapps-demo/server/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/fastapps-demo/server/api/__init__.py b/examples/fastapps-demo/server/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/fastapps-demo/server/main.py b/examples/fastapps-demo/server/main.py new file mode 100644 index 0000000..def6739 --- /dev/null +++ b/examples/fastapps-demo/server/main.py @@ -0,0 +1,63 @@ +from pathlib import Path +import sys +import importlib +import inspect + +# Add parent directory to path +sys.path.insert(0, str(Path(__file__).parent.parent)) + +# Import FastApps framework +from fastapps import WidgetBuilder, WidgetMCPServer, BaseWidget +import uvicorn + +PROJECT_ROOT = Path(__file__).parent.parent +TOOLS_DIR = Path(__file__).parent / "tools" + +def auto_load_tools(build_results): + """Automatically discover and load all widget tools.""" + tools = [] + for tool_file in TOOLS_DIR.glob("*_tool.py"): + module_name = tool_file.stem + try: + module = importlib.import_module(f"server.tools.{module_name}") + for name, obj in inspect.getmembers(module, inspect.isclass): + if issubclass(obj, BaseWidget) and obj is not BaseWidget: + tool_identifier = obj.identifier + if tool_identifier in build_results: + tool_instance = obj(build_results[tool_identifier]) + tools.append(tool_instance) + print(f"[OK] Loaded tool: {name} (identifier: {tool_identifier})") + else: + print(f"[WARNING] Warning: No build result found for tool '{tool_identifier}'") + except Exception as e: + print(f"[ERROR] Error loading {tool_file.name}: {e}") + return tools + +# Build all widgets +builder = WidgetBuilder(PROJECT_ROOT) +build_results = builder.build_all() + +# Auto-load and register tools +tools = auto_load_tools(build_results) + +# Create MCP server +server = WidgetMCPServer(name="my-widgets", widgets=tools) + +# Optional: Enable OAuth 2.1 authentication +# Uncomment and configure to protect your widgets with OAuth: +# +# server = WidgetMCPServer( +# name="my-widgets", +# widgets=tools, +# auth_issuer_url="https://your-tenant.us.auth0.com", +# auth_resource_server_url="https://yourdomain.com/mcp", +# auth_required_scopes=["user"], +# ) +# +# See docs: https://fastapps.dev/docs/auth + +app = server.get_app() + +if __name__ == "__main__": + print(f"\n[START] Starting server with {len(tools)} tools") + uvicorn.run(app, host="0.0.0.0", port=8001) diff --git a/examples/fastapps-demo/server/tools/__init__.py b/examples/fastapps-demo/server/tools/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/fastapps-demo/server/tools/my_widget_tool.py b/examples/fastapps-demo/server/tools/my_widget_tool.py new file mode 100644 index 0000000..0f730ea --- /dev/null +++ b/examples/fastapps-demo/server/tools/my_widget_tool.py @@ -0,0 +1,30 @@ +from fastapps import BaseWidget, ConfigDict +# from fastapps import auth_required, no_auth, optional_auth, UserContext +from pydantic import BaseModel +from typing import Dict, Any + + +class MyWidgetInput(BaseModel): + model_config = ConfigDict(populate_by_name=True) + + +# Optional: Add authentication +# @auth_required(scopes=["user"]) +# @no_auth +# @optional_auth(scopes=["user"]) +class MyWidgetTool(BaseWidget): + identifier = "my_widget" + title = "My Widget" + input_schema = MyWidgetInput + invoking = "Loading widget..." + invoked = "Widget ready!" + + widget_csp = { + "connect_domains": [], + "resource_domains": [] + } + + async def execute(self, input_data: MyWidgetInput, context=None, user=None) -> Dict[str, Any]: + return { + "message": "Welcome to FastApps" + } diff --git a/examples/fastapps-demo/widgets/my_widget/index.jsx b/examples/fastapps-demo/widgets/my_widget/index.jsx new file mode 100644 index 0000000..f7c9cb4 --- /dev/null +++ b/examples/fastapps-demo/widgets/my_widget/index.jsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { useWidgetProps } from 'fastapps'; + +export default function MyWidget() { + const props = useWidgetProps(); + + return ( +
+

{props.message || 'Welcome to FastApps'}

+
+ ); +} From 1419f3aa8b675190af7d94bffdca4dddb6bb086f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EC=A7=80=ED=98=81?= Date: Tue, 28 Oct 2025 09:36:00 +0900 Subject: [PATCH 4/9] Updated CI pipeline to use UV Create uv.lock --- .github/workflows/ci.yml | 34 +- .github/workflows/publish.yml | 16 +- .github/workflows/test-publish.yml | 16 +- uv.lock | 1841 ++++++++++++++++++++++++++++ 4 files changed, 1874 insertions(+), 33 deletions(-) create mode 100644 uv.lock diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e3df977..c8e73f1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,10 +24,13 @@ jobs: with: python-version: ${{ matrix.python-version }} + - name: Install uv + uses: astral-sh/setup-uv@v2 + with: + enable-cache: true + - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -e ".[dev]" + run: uv sync --dev - name: Run tests with pytest run: | @@ -51,15 +54,16 @@ jobs: with: python-version: '3.11' + - name: Install uv + uses: astral-sh/setup-uv@v2 + with: + enable-cache: true + - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -e ".[dev]" - pip install mypy + run: uv sync --dev - name: Type check with mypy - run: | - mypy fastapps --ignore-missing-imports + run: uv run mypy fastapps --ignore-missing-imports continue-on-error: true build: @@ -73,16 +77,16 @@ jobs: with: python-version: '3.11' - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install build twine + - name: Install uv + uses: astral-sh/setup-uv@v2 + with: + enable-cache: true - name: Build package - run: python -m build + run: uv build - name: Check package - run: twine check dist/* + run: uv run twine check dist/* - name: Upload artifacts uses: actions/upload-artifact@v4 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 7709868..6deecab 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -19,17 +19,15 @@ jobs: with: python-version: '3.11' - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install build twine + - name: Install uv + uses: astral-sh/setup-uv@v2 + with: + enable-cache: true - name: Build package - run: python -m build + run: uv build - name: Publish to PyPI env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} - run: | - twine upload dist/* + UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }} + run: uv publish diff --git a/.github/workflows/test-publish.yml b/.github/workflows/test-publish.yml index 9a5075c..89c0e79 100644 --- a/.github/workflows/test-publish.yml +++ b/.github/workflows/test-publish.yml @@ -19,18 +19,16 @@ jobs: with: python-version: '3.11' - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install build twine + - name: Install uv + uses: astral-sh/setup-uv@v2 + with: + enable-cache: true - name: Build package - run: python -m build + run: uv build - name: Publish to Test PyPI env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.TEST_PYPI_API_TOKEN }} - run: | - twine upload --repository testpypi dist/* + UV_PUBLISH_TOKEN: ${{ secrets.TEST_PYPI_API_TOKEN }} + run: uv publish --repository https://test.pypi.org/legacy/ continue-on-error: true diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..ec7ca1a --- /dev/null +++ b/uv.lock @@ -0,0 +1,1841 @@ +version = 1 +revision = 3 +requires-python = ">=3.11" + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "anyio" +version = "4.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "sniffio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c6/78/7d432127c41b50bccba979505f272c16cbcadcc33645d5fa3a738110ae75/anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4", size = 219094, upload-time = "2025-09-23T09:19:12.58Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc", size = 109097, upload-time = "2025-09-23T09:19:10.601Z" }, +] + +[[package]] +name = "attrs" +version = "25.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, +] + +[[package]] +name = "authlib" +version = "1.6.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cd/3f/1d3bbd0bf23bdd99276d4def22f29c27a914067b4cf66f753ff9b8bbd0f3/authlib-1.6.5.tar.gz", hash = "sha256:6aaf9c79b7cc96c900f0b284061691c5d4e61221640a948fe690b556a6d6d10b", size = 164553, upload-time = "2025-10-02T13:36:09.489Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/aa/5082412d1ee302e9e7d80b6949bc4d2a8fa1149aaab610c5fc24709605d6/authlib-1.6.5-py2.py3-none-any.whl", hash = "sha256:3e0e0507807f842b02175507bdee8957a1d5707fd4afb17c32fb43fee90b6e3a", size = 243608, upload-time = "2025-10-02T13:36:07.637Z" }, +] + +[[package]] +name = "backports-tarfile" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/86/72/cd9b395f25e290e633655a100af28cb253e4393396264a98bd5f5951d50f/backports_tarfile-1.2.0.tar.gz", hash = "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991", size = 86406, upload-time = "2024-05-28T17:01:54.731Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/fa/123043af240e49752f1c4bd24da5053b6bd00cad78c2be53c0d1e8b975bc/backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34", size = 30181, upload-time = "2024-05-28T17:01:53.112Z" }, +] + +[[package]] +name = "beartype" +version = "0.22.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/77/af43bdf737723b28130f2cb595ec0f23e0e757d211fe068fd0ccdb77d786/beartype-0.22.4.tar.gz", hash = "sha256:68284c7803efd190b1b4639a0ab1a17677af9571b8a2ef5a169d10cb8955b01f", size = 1578210, upload-time = "2025-10-26T03:30:50.352Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2f/eb/f25ad1a7726b2fe21005c3580b35fa7bfe09646faf7c8f41867747987a35/beartype-0.22.4-py3-none-any.whl", hash = "sha256:7967a1cee01fee42e47da69c58c92da10ba5bcfb8072686e48487be5201e3d10", size = 1318387, upload-time = "2025-10-26T03:30:48.135Z" }, +] + +[[package]] +name = "black" +version = "25.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "mypy-extensions" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "platformdirs" }, + { name = "pytokens" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4b/43/20b5c90612d7bdb2bdbcceeb53d588acca3bb8f0e4c5d5c751a2c8fdd55a/black-25.9.0.tar.gz", hash = "sha256:0474bca9a0dd1b51791fcc507a4e02078a1c63f6d4e4ae5544b9848c7adfb619", size = 648393, upload-time = "2025-09-19T00:27:37.758Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/f4/7531d4a336d2d4ac6cc101662184c8e7d068b548d35d874415ed9f4116ef/black-25.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:456386fe87bad41b806d53c062e2974615825c7a52159cde7ccaeb0695fa28fa", size = 1698727, upload-time = "2025-09-19T00:31:14.264Z" }, + { url = "https://files.pythonhosted.org/packages/28/f9/66f26bfbbf84b949cc77a41a43e138d83b109502cd9c52dfc94070ca51f2/black-25.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a16b14a44c1af60a210d8da28e108e13e75a284bf21a9afa6b4571f96ab8bb9d", size = 1555679, upload-time = "2025-09-19T00:31:29.265Z" }, + { url = "https://files.pythonhosted.org/packages/bf/59/61475115906052f415f518a648a9ac679d7afbc8da1c16f8fdf68a8cebed/black-25.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aaf319612536d502fdd0e88ce52d8f1352b2c0a955cc2798f79eeca9d3af0608", size = 1617453, upload-time = "2025-09-19T00:30:42.24Z" }, + { url = "https://files.pythonhosted.org/packages/7f/5b/20fd5c884d14550c911e4fb1b0dae00d4abb60a4f3876b449c4d3a9141d5/black-25.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:c0372a93e16b3954208417bfe448e09b0de5cc721d521866cd9e0acac3c04a1f", size = 1333655, upload-time = "2025-09-19T00:30:56.715Z" }, + { url = "https://files.pythonhosted.org/packages/fb/8e/319cfe6c82f7e2d5bfb4d3353c6cc85b523d677ff59edc61fdb9ee275234/black-25.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1b9dc70c21ef8b43248f1d86aedd2aaf75ae110b958a7909ad8463c4aa0880b0", size = 1742012, upload-time = "2025-09-19T00:33:08.678Z" }, + { url = "https://files.pythonhosted.org/packages/94/cc/f562fe5d0a40cd2a4e6ae3f685e4c36e365b1f7e494af99c26ff7f28117f/black-25.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8e46eecf65a095fa62e53245ae2795c90bdecabd53b50c448d0a8bcd0d2e74c4", size = 1581421, upload-time = "2025-09-19T00:35:25.937Z" }, + { url = "https://files.pythonhosted.org/packages/84/67/6db6dff1ebc8965fd7661498aea0da5d7301074b85bba8606a28f47ede4d/black-25.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9101ee58ddc2442199a25cb648d46ba22cd580b00ca4b44234a324e3ec7a0f7e", size = 1655619, upload-time = "2025-09-19T00:30:49.241Z" }, + { url = "https://files.pythonhosted.org/packages/10/10/3faef9aa2a730306cf469d76f7f155a8cc1f66e74781298df0ba31f8b4c8/black-25.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:77e7060a00c5ec4b3367c55f39cf9b06e68965a4f2e61cecacd6d0d9b7ec945a", size = 1342481, upload-time = "2025-09-19T00:31:29.625Z" }, + { url = "https://files.pythonhosted.org/packages/48/99/3acfea65f5e79f45472c45f87ec13037b506522719cd9d4ac86484ff51ac/black-25.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0172a012f725b792c358d57fe7b6b6e8e67375dd157f64fa7a3097b3ed3e2175", size = 1742165, upload-time = "2025-09-19T00:34:10.402Z" }, + { url = "https://files.pythonhosted.org/packages/3a/18/799285282c8236a79f25d590f0222dbd6850e14b060dfaa3e720241fd772/black-25.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3bec74ee60f8dfef564b573a96b8930f7b6a538e846123d5ad77ba14a8d7a64f", size = 1581259, upload-time = "2025-09-19T00:32:49.685Z" }, + { url = "https://files.pythonhosted.org/packages/f1/ce/883ec4b6303acdeca93ee06b7622f1fa383c6b3765294824165d49b1a86b/black-25.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b756fc75871cb1bcac5499552d771822fd9db5a2bb8db2a7247936ca48f39831", size = 1655583, upload-time = "2025-09-19T00:30:44.505Z" }, + { url = "https://files.pythonhosted.org/packages/21/17/5c253aa80a0639ccc427a5c7144534b661505ae2b5a10b77ebe13fa25334/black-25.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:846d58e3ce7879ec1ffe816bb9df6d006cd9590515ed5d17db14e17666b2b357", size = 1343428, upload-time = "2025-09-19T00:32:13.839Z" }, + { url = "https://files.pythonhosted.org/packages/1b/46/863c90dcd3f9d41b109b7f19032ae0db021f0b2a81482ba0a1e28c84de86/black-25.9.0-py3-none-any.whl", hash = "sha256:474b34c1342cdc157d307b56c4c65bce916480c4a8f6551fdc6bf9b486a7c4ae", size = 203363, upload-time = "2025-09-19T00:27:35.724Z" }, +] + +[[package]] +name = "cachetools" +version = "6.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/7e/b975b5814bd36faf009faebe22c1072a1fa1168db34d285ef0ba071ad78c/cachetools-6.2.1.tar.gz", hash = "sha256:3f391e4bd8f8bf0931169baf7456cc822705f4e2a31f840d218f445b9a854201", size = 31325, upload-time = "2025-10-12T14:55:30.139Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/c5/1e741d26306c42e2bf6ab740b2202872727e0f606033c9dd713f8b93f5a8/cachetools-6.2.1-py3-none-any.whl", hash = "sha256:09868944b6dde876dfd44e1d47e18484541eaf12f26f29b7af91b26cc892d701", size = 11280, upload-time = "2025-10-12T14:55:28.382Z" }, +] + +[[package]] +name = "certifi" +version = "2025.10.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/5b/b6ce21586237c77ce67d01dc5507039d444b630dd76611bbca2d8e5dcd91/certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43", size = 164519, upload-time = "2025-10-05T04:12:15.808Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e4/37/af0d2ef3967ac0d6113837b44a4f0bfe1328c2b9763bd5b1744520e5cfed/certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de", size = 163286, upload-time = "2025-10-05T04:12:14.03Z" }, +] + +[[package]] +name = "cffi" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" }, + { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" }, + { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" }, + { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" }, + { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" }, + { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" }, + { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" }, + { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" }, + { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" }, + { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" }, + { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" }, + { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, + { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, + { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, + { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, + { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, + { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, + { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, + { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, + { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, + { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, + { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, + { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, + { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, + { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, + { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, + { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, + { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, + { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, + { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, + { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, + { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, + { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, + { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, + { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, + { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", size = 206988, upload-time = "2025-10-14T04:40:33.79Z" }, + { url = "https://files.pythonhosted.org/packages/94/59/2e87300fe67ab820b5428580a53cad894272dbb97f38a7a814a2a1ac1011/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", size = 147324, upload-time = "2025-10-14T04:40:34.961Z" }, + { url = "https://files.pythonhosted.org/packages/07/fb/0cf61dc84b2b088391830f6274cb57c82e4da8bbc2efeac8c025edb88772/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", size = 142742, upload-time = "2025-10-14T04:40:36.105Z" }, + { url = "https://files.pythonhosted.org/packages/62/8b/171935adf2312cd745d290ed93cf16cf0dfe320863ab7cbeeae1dcd6535f/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", size = 160863, upload-time = "2025-10-14T04:40:37.188Z" }, + { url = "https://files.pythonhosted.org/packages/09/73/ad875b192bda14f2173bfc1bc9a55e009808484a4b256748d931b6948442/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", size = 157837, upload-time = "2025-10-14T04:40:38.435Z" }, + { url = "https://files.pythonhosted.org/packages/6d/fc/de9cce525b2c5b94b47c70a4b4fb19f871b24995c728e957ee68ab1671ea/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", size = 151550, upload-time = "2025-10-14T04:40:40.053Z" }, + { url = "https://files.pythonhosted.org/packages/55/c2/43edd615fdfba8c6f2dfbd459b25a6b3b551f24ea21981e23fb768503ce1/charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", size = 149162, upload-time = "2025-10-14T04:40:41.163Z" }, + { url = "https://files.pythonhosted.org/packages/03/86/bde4ad8b4d0e9429a4e82c1e8f5c659993a9a863ad62c7df05cf7b678d75/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", size = 150019, upload-time = "2025-10-14T04:40:42.276Z" }, + { url = "https://files.pythonhosted.org/packages/1f/86/a151eb2af293a7e7bac3a739b81072585ce36ccfb4493039f49f1d3cae8c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", size = 143310, upload-time = "2025-10-14T04:40:43.439Z" }, + { url = "https://files.pythonhosted.org/packages/b5/fe/43dae6144a7e07b87478fdfc4dbe9efd5defb0e7ec29f5f58a55aeef7bf7/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", size = 162022, upload-time = "2025-10-14T04:40:44.547Z" }, + { url = "https://files.pythonhosted.org/packages/80/e6/7aab83774f5d2bca81f42ac58d04caf44f0cc2b65fc6db2b3b2e8a05f3b3/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", size = 149383, upload-time = "2025-10-14T04:40:46.018Z" }, + { url = "https://files.pythonhosted.org/packages/4f/e8/b289173b4edae05c0dde07f69f8db476a0b511eac556dfe0d6bda3c43384/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", size = 159098, upload-time = "2025-10-14T04:40:47.081Z" }, + { url = "https://files.pythonhosted.org/packages/d8/df/fe699727754cae3f8478493c7f45f777b17c3ef0600e28abfec8619eb49c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", size = 152991, upload-time = "2025-10-14T04:40:48.246Z" }, + { url = "https://files.pythonhosted.org/packages/1a/86/584869fe4ddb6ffa3bd9f491b87a01568797fb9bd8933f557dba9771beaf/charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", size = 99456, upload-time = "2025-10-14T04:40:49.376Z" }, + { url = "https://files.pythonhosted.org/packages/65/f6/62fdd5feb60530f50f7e38b4f6a1d5203f4d16ff4f9f0952962c044e919a/charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", size = 106978, upload-time = "2025-10-14T04:40:50.844Z" }, + { url = "https://files.pythonhosted.org/packages/7a/9d/0710916e6c82948b3be62d9d398cb4fcf4e97b56d6a6aeccd66c4b2f2bd5/charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", size = 99969, upload-time = "2025-10-14T04:40:52.272Z" }, + { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" }, + { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" }, + { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" }, + { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" }, + { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" }, + { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" }, + { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" }, + { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" }, + { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" }, + { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" }, + { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" }, + { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" }, + { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" }, + { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" }, + { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" }, + { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" }, + { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" }, + { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" }, + { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" }, + { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" }, + { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" }, + { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" }, + { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" }, + { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" }, + { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" }, + { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" }, + { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" }, + { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" }, + { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, + { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, + { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, + { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" }, + { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" }, + { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" }, + { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" }, + { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" }, + { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" }, + { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" }, + { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" }, + { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" }, + { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" }, + { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" }, + { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" }, + { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, + { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, + { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, + { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, +] + +[[package]] +name = "click" +version = "8.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/46/61/de6cd827efad202d7057d93e0fed9294b96952e188f7384832791c7b2254/click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4", size = 276943, upload-time = "2025-09-18T17:32:23.696Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/d3/9dcc0f5797f070ec8edf30fbadfb200e71d9db6b84d211e3b2085a7589a0/click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc", size = 107295, upload-time = "2025-09-18T17:32:22.42Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "coverage" +version = "7.11.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/38/ee22495420457259d2f3390309505ea98f98a5eed40901cf62196abad006/coverage-7.11.0.tar.gz", hash = "sha256:167bd504ac1ca2af7ff3b81d245dfea0292c5032ebef9d66cc08a7d28c1b8050", size = 811905, upload-time = "2025-10-15T15:15:08.542Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/3a/ee1074c15c408ddddddb1db7dd904f6b81bc524e01f5a1c5920e13dbde23/coverage-7.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d58ecaa865c5b9fa56e35efc51d1014d4c0d22838815b9fce57a27dd9576847", size = 215912, upload-time = "2025-10-15T15:12:40.665Z" }, + { url = "https://files.pythonhosted.org/packages/70/c4/9f44bebe5cb15f31608597b037d78799cc5f450044465bcd1ae8cb222fe1/coverage-7.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b679e171f1c104a5668550ada700e3c4937110dbdd153b7ef9055c4f1a1ee3cc", size = 216310, upload-time = "2025-10-15T15:12:42.461Z" }, + { url = "https://files.pythonhosted.org/packages/42/01/5e06077cfef92d8af926bdd86b84fb28bf9bc6ad27343d68be9b501d89f2/coverage-7.11.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ca61691ba8c5b6797deb221a0d09d7470364733ea9c69425a640f1f01b7c5bf0", size = 246706, upload-time = "2025-10-15T15:12:44.001Z" }, + { url = "https://files.pythonhosted.org/packages/40/b8/7a3f1f33b35cc4a6c37e759137533119560d06c0cc14753d1a803be0cd4a/coverage-7.11.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:aef1747ede4bd8ca9cfc04cc3011516500c6891f1b33a94add3253f6f876b7b7", size = 248634, upload-time = "2025-10-15T15:12:45.768Z" }, + { url = "https://files.pythonhosted.org/packages/7a/41/7f987eb33de386bc4c665ab0bf98d15fcf203369d6aacae74f5dd8ec489a/coverage-7.11.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1839d08406e4cba2953dcc0ffb312252f14d7c4c96919f70167611f4dee2623", size = 250741, upload-time = "2025-10-15T15:12:47.222Z" }, + { url = "https://files.pythonhosted.org/packages/23/c1/a4e0ca6a4e83069fb8216b49b30a7352061ca0cb38654bd2dc96b7b3b7da/coverage-7.11.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e0eb0a2dcc62478eb5b4cbb80b97bdee852d7e280b90e81f11b407d0b81c4287", size = 246837, upload-time = "2025-10-15T15:12:48.904Z" }, + { url = "https://files.pythonhosted.org/packages/5d/03/ced062a17f7c38b4728ff76c3acb40d8465634b20b4833cdb3cc3a74e115/coverage-7.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bc1fbea96343b53f65d5351d8fd3b34fd415a2670d7c300b06d3e14a5af4f552", size = 248429, upload-time = "2025-10-15T15:12:50.73Z" }, + { url = "https://files.pythonhosted.org/packages/97/af/a7c6f194bb8c5a2705ae019036b8fe7f49ea818d638eedb15fdb7bed227c/coverage-7.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:214b622259dd0cf435f10241f1333d32caa64dbc27f8790ab693428a141723de", size = 246490, upload-time = "2025-10-15T15:12:52.646Z" }, + { url = "https://files.pythonhosted.org/packages/ab/c3/aab4df02b04a8fde79068c3c41ad7a622b0ef2b12e1ed154da986a727c3f/coverage-7.11.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:258d9967520cca899695d4eb7ea38be03f06951d6ca2f21fb48b1235f791e601", size = 246208, upload-time = "2025-10-15T15:12:54.586Z" }, + { url = "https://files.pythonhosted.org/packages/30/d8/e282ec19cd658238d60ed404f99ef2e45eed52e81b866ab1518c0d4163cf/coverage-7.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cf9e6ff4ca908ca15c157c409d608da77a56a09877b97c889b98fb2c32b6465e", size = 247126, upload-time = "2025-10-15T15:12:56.485Z" }, + { url = "https://files.pythonhosted.org/packages/d1/17/a635fa07fac23adb1a5451ec756216768c2767efaed2e4331710342a3399/coverage-7.11.0-cp311-cp311-win32.whl", hash = "sha256:fcc15fc462707b0680cff6242c48625da7f9a16a28a41bb8fd7a4280920e676c", size = 218314, upload-time = "2025-10-15T15:12:58.365Z" }, + { url = "https://files.pythonhosted.org/packages/2a/29/2ac1dfcdd4ab9a70026edc8d715ece9b4be9a1653075c658ee6f271f394d/coverage-7.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:865965bf955d92790f1facd64fe7ff73551bd2c1e7e6b26443934e9701ba30b9", size = 219203, upload-time = "2025-10-15T15:12:59.902Z" }, + { url = "https://files.pythonhosted.org/packages/03/21/5ce8b3a0133179115af4c041abf2ee652395837cb896614beb8ce8ddcfd9/coverage-7.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:5693e57a065760dcbeb292d60cc4d0231a6d4b6b6f6a3191561e1d5e8820b745", size = 217879, upload-time = "2025-10-15T15:13:01.35Z" }, + { url = "https://files.pythonhosted.org/packages/c4/db/86f6906a7c7edc1a52b2c6682d6dd9be775d73c0dfe2b84f8923dfea5784/coverage-7.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9c49e77811cf9d024b95faf86c3f059b11c0c9be0b0d61bc598f453703bd6fd1", size = 216098, upload-time = "2025-10-15T15:13:02.916Z" }, + { url = "https://files.pythonhosted.org/packages/21/54/e7b26157048c7ba555596aad8569ff903d6cd67867d41b75287323678ede/coverage-7.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a61e37a403a778e2cda2a6a39abcc895f1d984071942a41074b5c7ee31642007", size = 216331, upload-time = "2025-10-15T15:13:04.403Z" }, + { url = "https://files.pythonhosted.org/packages/b9/19/1ce6bf444f858b83a733171306134a0544eaddf1ca8851ede6540a55b2ad/coverage-7.11.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c79cae102bb3b1801e2ef1511fb50e91ec83a1ce466b2c7c25010d884336de46", size = 247825, upload-time = "2025-10-15T15:13:05.92Z" }, + { url = "https://files.pythonhosted.org/packages/71/0b/d3bcbbc259fcced5fb67c5d78f6e7ee965f49760c14afd931e9e663a83b2/coverage-7.11.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:16ce17ceb5d211f320b62df002fa7016b7442ea0fd260c11cec8ce7730954893", size = 250573, upload-time = "2025-10-15T15:13:07.471Z" }, + { url = "https://files.pythonhosted.org/packages/58/8d/b0ff3641a320abb047258d36ed1c21d16be33beed4152628331a1baf3365/coverage-7.11.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:80027673e9d0bd6aef86134b0771845e2da85755cf686e7c7c59566cf5a89115", size = 251706, upload-time = "2025-10-15T15:13:09.4Z" }, + { url = "https://files.pythonhosted.org/packages/59/c8/5a586fe8c7b0458053d9c687f5cff515a74b66c85931f7fe17a1c958b4ac/coverage-7.11.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4d3ffa07a08657306cd2215b0da53761c4d73cb54d9143b9303a6481ec0cd415", size = 248221, upload-time = "2025-10-15T15:13:10.964Z" }, + { url = "https://files.pythonhosted.org/packages/d0/ff/3a25e3132804ba44cfa9a778cdf2b73dbbe63ef4b0945e39602fc896ba52/coverage-7.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a3b6a5f8b2524fd6c1066bc85bfd97e78709bb5e37b5b94911a6506b65f47186", size = 249624, upload-time = "2025-10-15T15:13:12.5Z" }, + { url = "https://files.pythonhosted.org/packages/c5/12/ff10c8ce3895e1b17a73485ea79ebc1896a9e466a9d0f4aef63e0d17b718/coverage-7.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fcc0a4aa589de34bc56e1a80a740ee0f8c47611bdfb28cd1849de60660f3799d", size = 247744, upload-time = "2025-10-15T15:13:14.554Z" }, + { url = "https://files.pythonhosted.org/packages/16/02/d500b91f5471b2975947e0629b8980e5e90786fe316b6d7299852c1d793d/coverage-7.11.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:dba82204769d78c3fd31b35c3d5f46e06511936c5019c39f98320e05b08f794d", size = 247325, upload-time = "2025-10-15T15:13:16.438Z" }, + { url = "https://files.pythonhosted.org/packages/77/11/dee0284fbbd9cd64cfce806b827452c6df3f100d9e66188e82dfe771d4af/coverage-7.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:81b335f03ba67309a95210caf3eb43bd6fe75a4e22ba653ef97b4696c56c7ec2", size = 249180, upload-time = "2025-10-15T15:13:17.959Z" }, + { url = "https://files.pythonhosted.org/packages/59/1b/cdf1def928f0a150a057cab03286774e73e29c2395f0d30ce3d9e9f8e697/coverage-7.11.0-cp312-cp312-win32.whl", hash = "sha256:037b2d064c2f8cc8716fe4d39cb705779af3fbf1ba318dc96a1af858888c7bb5", size = 218479, upload-time = "2025-10-15T15:13:19.608Z" }, + { url = "https://files.pythonhosted.org/packages/ff/55/e5884d55e031da9c15b94b90a23beccc9d6beee65e9835cd6da0a79e4f3a/coverage-7.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:d66c0104aec3b75e5fd897e7940188ea1892ca1d0235316bf89286d6a22568c0", size = 219290, upload-time = "2025-10-15T15:13:21.593Z" }, + { url = "https://files.pythonhosted.org/packages/23/a8/faa930cfc71c1d16bc78f9a19bb73700464f9c331d9e547bfbc1dbd3a108/coverage-7.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:d91ebeac603812a09cf6a886ba6e464f3bbb367411904ae3790dfe28311b15ad", size = 217924, upload-time = "2025-10-15T15:13:23.39Z" }, + { url = "https://files.pythonhosted.org/packages/60/7f/85e4dfe65e400645464b25c036a26ac226cf3a69d4a50c3934c532491cdd/coverage-7.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:cc3f49e65ea6e0d5d9bd60368684fe52a704d46f9e7fc413918f18d046ec40e1", size = 216129, upload-time = "2025-10-15T15:13:25.371Z" }, + { url = "https://files.pythonhosted.org/packages/96/5d/dc5fa98fea3c175caf9d360649cb1aa3715e391ab00dc78c4c66fabd7356/coverage-7.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f39ae2f63f37472c17b4990f794035c9890418b1b8cca75c01193f3c8d3e01be", size = 216380, upload-time = "2025-10-15T15:13:26.976Z" }, + { url = "https://files.pythonhosted.org/packages/b2/f5/3da9cc9596708273385189289c0e4d8197d37a386bdf17619013554b3447/coverage-7.11.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7db53b5cdd2917b6eaadd0b1251cf4e7d96f4a8d24e174bdbdf2f65b5ea7994d", size = 247375, upload-time = "2025-10-15T15:13:28.923Z" }, + { url = "https://files.pythonhosted.org/packages/65/6c/f7f59c342359a235559d2bc76b0c73cfc4bac7d61bb0df210965cb1ecffd/coverage-7.11.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10ad04ac3a122048688387828b4537bc9cf60c0bf4869c1e9989c46e45690b82", size = 249978, upload-time = "2025-10-15T15:13:30.525Z" }, + { url = "https://files.pythonhosted.org/packages/e7/8c/042dede2e23525e863bf1ccd2b92689692a148d8b5fd37c37899ba882645/coverage-7.11.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4036cc9c7983a2b1f2556d574d2eb2154ac6ed55114761685657e38782b23f52", size = 251253, upload-time = "2025-10-15T15:13:32.174Z" }, + { url = "https://files.pythonhosted.org/packages/7b/a9/3c58df67bfa809a7bddd786356d9c5283e45d693edb5f3f55d0986dd905a/coverage-7.11.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7ab934dd13b1c5e94b692b1e01bd87e4488cb746e3a50f798cb9464fd128374b", size = 247591, upload-time = "2025-10-15T15:13:34.147Z" }, + { url = "https://files.pythonhosted.org/packages/26/5b/c7f32efd862ee0477a18c41e4761305de6ddd2d49cdeda0c1116227570fd/coverage-7.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59a6e5a265f7cfc05f76e3bb53eca2e0dfe90f05e07e849930fecd6abb8f40b4", size = 249411, upload-time = "2025-10-15T15:13:38.425Z" }, + { url = "https://files.pythonhosted.org/packages/76/b5/78cb4f1e86c1611431c990423ec0768122905b03837e1b4c6a6f388a858b/coverage-7.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:df01d6c4c81e15a7c88337b795bb7595a8596e92310266b5072c7e301168efbd", size = 247303, upload-time = "2025-10-15T15:13:40.464Z" }, + { url = "https://files.pythonhosted.org/packages/87/c9/23c753a8641a330f45f221286e707c427e46d0ffd1719b080cedc984ec40/coverage-7.11.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8c934bd088eed6174210942761e38ee81d28c46de0132ebb1801dbe36a390dcc", size = 247157, upload-time = "2025-10-15T15:13:42.087Z" }, + { url = "https://files.pythonhosted.org/packages/c5/42/6e0cc71dc8a464486e944a4fa0d85bdec031cc2969e98ed41532a98336b9/coverage-7.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a03eaf7ec24078ad64a07f02e30060aaf22b91dedf31a6b24d0d98d2bba7f48", size = 248921, upload-time = "2025-10-15T15:13:43.715Z" }, + { url = "https://files.pythonhosted.org/packages/e8/1c/743c2ef665e6858cccb0f84377dfe3a4c25add51e8c7ef19249be92465b6/coverage-7.11.0-cp313-cp313-win32.whl", hash = "sha256:695340f698a5f56f795b2836abe6fb576e7c53d48cd155ad2f80fd24bc63a040", size = 218526, upload-time = "2025-10-15T15:13:45.336Z" }, + { url = "https://files.pythonhosted.org/packages/ff/d5/226daadfd1bf8ddbccefbd3aa3547d7b960fb48e1bdac124e2dd13a2b71a/coverage-7.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:2727d47fce3ee2bac648528e41455d1b0c46395a087a229deac75e9f88ba5a05", size = 219317, upload-time = "2025-10-15T15:13:47.401Z" }, + { url = "https://files.pythonhosted.org/packages/97/54/47db81dcbe571a48a298f206183ba8a7ba79200a37cd0d9f4788fcd2af4a/coverage-7.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:0efa742f431529699712b92ecdf22de8ff198df41e43aeaaadf69973eb93f17a", size = 217948, upload-time = "2025-10-15T15:13:49.096Z" }, + { url = "https://files.pythonhosted.org/packages/e5/8b/cb68425420154e7e2a82fd779a8cc01549b6fa83c2ad3679cd6c088ebd07/coverage-7.11.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:587c38849b853b157706407e9ebdca8fd12f45869edb56defbef2daa5fb0812b", size = 216837, upload-time = "2025-10-15T15:13:51.09Z" }, + { url = "https://files.pythonhosted.org/packages/33/55/9d61b5765a025685e14659c8d07037247de6383c0385757544ffe4606475/coverage-7.11.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b971bdefdd75096163dd4261c74be813c4508477e39ff7b92191dea19f24cd37", size = 217061, upload-time = "2025-10-15T15:13:52.747Z" }, + { url = "https://files.pythonhosted.org/packages/52/85/292459c9186d70dcec6538f06ea251bc968046922497377bf4a1dc9a71de/coverage-7.11.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:269bfe913b7d5be12ab13a95f3a76da23cf147be7fa043933320ba5625f0a8de", size = 258398, upload-time = "2025-10-15T15:13:54.45Z" }, + { url = "https://files.pythonhosted.org/packages/1f/e2/46edd73fb8bf51446c41148d81944c54ed224854812b6ca549be25113ee0/coverage-7.11.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:dadbcce51a10c07b7c72b0ce4a25e4b6dcb0c0372846afb8e5b6307a121eb99f", size = 260574, upload-time = "2025-10-15T15:13:56.145Z" }, + { url = "https://files.pythonhosted.org/packages/07/5e/1df469a19007ff82e2ca8fe509822820a31e251f80ee7344c34f6cd2ec43/coverage-7.11.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9ed43fa22c6436f7957df036331f8fe4efa7af132054e1844918866cd228af6c", size = 262797, upload-time = "2025-10-15T15:13:58.635Z" }, + { url = "https://files.pythonhosted.org/packages/f9/50/de216b31a1434b94d9b34a964c09943c6be45069ec704bfc379d8d89a649/coverage-7.11.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9516add7256b6713ec08359b7b05aeff8850c98d357784c7205b2e60aa2513fa", size = 257361, upload-time = "2025-10-15T15:14:00.409Z" }, + { url = "https://files.pythonhosted.org/packages/82/1e/3f9f8344a48111e152e0fd495b6fff13cc743e771a6050abf1627a7ba918/coverage-7.11.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb92e47c92fcbcdc692f428da67db33337fa213756f7adb6a011f7b5a7a20740", size = 260349, upload-time = "2025-10-15T15:14:02.188Z" }, + { url = "https://files.pythonhosted.org/packages/65/9b/3f52741f9e7d82124272f3070bbe316006a7de1bad1093f88d59bfc6c548/coverage-7.11.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:d06f4fc7acf3cabd6d74941d53329e06bab00a8fe10e4df2714f0b134bfc64ef", size = 258114, upload-time = "2025-10-15T15:14:03.907Z" }, + { url = "https://files.pythonhosted.org/packages/0b/8b/918f0e15f0365d50d3986bbd3338ca01178717ac5678301f3f547b6619e6/coverage-7.11.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:6fbcee1a8f056af07ecd344482f711f563a9eb1c2cad192e87df00338ec3cdb0", size = 256723, upload-time = "2025-10-15T15:14:06.324Z" }, + { url = "https://files.pythonhosted.org/packages/44/9e/7776829f82d3cf630878a7965a7d70cc6ca94f22c7d20ec4944f7148cb46/coverage-7.11.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dbbf012be5f32533a490709ad597ad8a8ff80c582a95adc8d62af664e532f9ca", size = 259238, upload-time = "2025-10-15T15:14:08.002Z" }, + { url = "https://files.pythonhosted.org/packages/9a/b8/49cf253e1e7a3bedb85199b201862dd7ca4859f75b6cf25ffa7298aa0760/coverage-7.11.0-cp313-cp313t-win32.whl", hash = "sha256:cee6291bb4fed184f1c2b663606a115c743df98a537c969c3c64b49989da96c2", size = 219180, upload-time = "2025-10-15T15:14:09.786Z" }, + { url = "https://files.pythonhosted.org/packages/ac/e1/1a541703826be7ae2125a0fb7f821af5729d56bb71e946e7b933cc7a89a4/coverage-7.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:a386c1061bf98e7ea4758e4313c0ab5ecf57af341ef0f43a0bf26c2477b5c268", size = 220241, upload-time = "2025-10-15T15:14:11.471Z" }, + { url = "https://files.pythonhosted.org/packages/d5/d1/5ee0e0a08621140fd418ec4020f595b4d52d7eb429ae6a0c6542b4ba6f14/coverage-7.11.0-cp313-cp313t-win_arm64.whl", hash = "sha256:f9ea02ef40bb83823b2b04964459d281688fe173e20643870bb5d2edf68bc836", size = 218510, upload-time = "2025-10-15T15:14:13.46Z" }, + { url = "https://files.pythonhosted.org/packages/f4/06/e923830c1985ce808e40a3fa3eb46c13350b3224b7da59757d37b6ce12b8/coverage-7.11.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c770885b28fb399aaf2a65bbd1c12bf6f307ffd112d6a76c5231a94276f0c497", size = 216110, upload-time = "2025-10-15T15:14:15.157Z" }, + { url = "https://files.pythonhosted.org/packages/42/82/cdeed03bfead45203fb651ed756dfb5266028f5f939e7f06efac4041dad5/coverage-7.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a3d0e2087dba64c86a6b254f43e12d264b636a39e88c5cc0a01a7c71bcfdab7e", size = 216395, upload-time = "2025-10-15T15:14:16.863Z" }, + { url = "https://files.pythonhosted.org/packages/fc/ba/e1c80caffc3199aa699813f73ff097bc2df7b31642bdbc7493600a8f1de5/coverage-7.11.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:73feb83bb41c32811973b8565f3705caf01d928d972b72042b44e97c71fd70d1", size = 247433, upload-time = "2025-10-15T15:14:18.589Z" }, + { url = "https://files.pythonhosted.org/packages/80/c0/5b259b029694ce0a5bbc1548834c7ba3db41d3efd3474489d7efce4ceb18/coverage-7.11.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c6f31f281012235ad08f9a560976cc2fc9c95c17604ff3ab20120fe480169bca", size = 249970, upload-time = "2025-10-15T15:14:20.307Z" }, + { url = "https://files.pythonhosted.org/packages/8c/86/171b2b5e1aac7e2fd9b43f7158b987dbeb95f06d1fbecad54ad8163ae3e8/coverage-7.11.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9570ad567f880ef675673992222746a124b9595506826b210fbe0ce3f0499cd", size = 251324, upload-time = "2025-10-15T15:14:22.419Z" }, + { url = "https://files.pythonhosted.org/packages/1a/7e/7e10414d343385b92024af3932a27a1caf75c6e27ee88ba211221ff1a145/coverage-7.11.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8badf70446042553a773547a61fecaa734b55dc738cacf20c56ab04b77425e43", size = 247445, upload-time = "2025-10-15T15:14:24.205Z" }, + { url = "https://files.pythonhosted.org/packages/c4/3b/e4f966b21f5be8c4bf86ad75ae94efa0de4c99c7bbb8114476323102e345/coverage-7.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a09c1211959903a479e389685b7feb8a17f59ec5a4ef9afde7650bd5eabc2777", size = 249324, upload-time = "2025-10-15T15:14:26.234Z" }, + { url = "https://files.pythonhosted.org/packages/00/a2/8479325576dfcd909244d0df215f077f47437ab852ab778cfa2f8bf4d954/coverage-7.11.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:5ef83b107f50db3f9ae40f69e34b3bd9337456c5a7fe3461c7abf8b75dd666a2", size = 247261, upload-time = "2025-10-15T15:14:28.42Z" }, + { url = "https://files.pythonhosted.org/packages/7b/d8/3a9e2db19d94d65771d0f2e21a9ea587d11b831332a73622f901157cc24b/coverage-7.11.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f91f927a3215b8907e214af77200250bb6aae36eca3f760f89780d13e495388d", size = 247092, upload-time = "2025-10-15T15:14:30.784Z" }, + { url = "https://files.pythonhosted.org/packages/b3/b1/bbca3c472544f9e2ad2d5116b2379732957048be4b93a9c543fcd0207e5f/coverage-7.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cdbcd376716d6b7fbfeedd687a6c4be019c5a5671b35f804ba76a4c0a778cba4", size = 248755, upload-time = "2025-10-15T15:14:32.585Z" }, + { url = "https://files.pythonhosted.org/packages/89/49/638d5a45a6a0f00af53d6b637c87007eb2297042186334e9923a61aa8854/coverage-7.11.0-cp314-cp314-win32.whl", hash = "sha256:bab7ec4bb501743edc63609320aaec8cd9188b396354f482f4de4d40a9d10721", size = 218793, upload-time = "2025-10-15T15:14:34.972Z" }, + { url = "https://files.pythonhosted.org/packages/30/cc/b675a51f2d068adb3cdf3799212c662239b0ca27f4691d1fff81b92ea850/coverage-7.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:3d4ba9a449e9364a936a27322b20d32d8b166553bfe63059bd21527e681e2fad", size = 219587, upload-time = "2025-10-15T15:14:37.047Z" }, + { url = "https://files.pythonhosted.org/packages/93/98/5ac886876026de04f00820e5094fe22166b98dcb8b426bf6827aaf67048c/coverage-7.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:ce37f215223af94ef0f75ac68ea096f9f8e8c8ec7d6e8c346ee45c0d363f0479", size = 218168, upload-time = "2025-10-15T15:14:38.861Z" }, + { url = "https://files.pythonhosted.org/packages/14/d1/b4145d35b3e3ecf4d917e97fc8895bcf027d854879ba401d9ff0f533f997/coverage-7.11.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:f413ce6e07e0d0dc9c433228727b619871532674b45165abafe201f200cc215f", size = 216850, upload-time = "2025-10-15T15:14:40.651Z" }, + { url = "https://files.pythonhosted.org/packages/ca/d1/7f645fc2eccd318369a8a9948acc447bb7c1ade2911e31d3c5620544c22b/coverage-7.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:05791e528a18f7072bf5998ba772fe29db4da1234c45c2087866b5ba4dea710e", size = 217071, upload-time = "2025-10-15T15:14:42.755Z" }, + { url = "https://files.pythonhosted.org/packages/54/7d/64d124649db2737ceced1dfcbdcb79898d5868d311730f622f8ecae84250/coverage-7.11.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cacb29f420cfeb9283b803263c3b9a068924474ff19ca126ba9103e1278dfa44", size = 258570, upload-time = "2025-10-15T15:14:44.542Z" }, + { url = "https://files.pythonhosted.org/packages/6c/3f/6f5922f80dc6f2d8b2c6f974835c43f53eb4257a7797727e6ca5b7b2ec1f/coverage-7.11.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:314c24e700d7027ae3ab0d95fbf8d53544fca1f20345fd30cd219b737c6e58d3", size = 260738, upload-time = "2025-10-15T15:14:46.436Z" }, + { url = "https://files.pythonhosted.org/packages/0e/5f/9e883523c4647c860b3812b417a2017e361eca5b635ee658387dc11b13c1/coverage-7.11.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:630d0bd7a293ad2fc8b4b94e5758c8b2536fdf36c05f1681270203e463cbfa9b", size = 262994, upload-time = "2025-10-15T15:14:48.3Z" }, + { url = "https://files.pythonhosted.org/packages/07/bb/43b5a8e94c09c8bf51743ffc65c4c841a4ca5d3ed191d0a6919c379a1b83/coverage-7.11.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e89641f5175d65e2dbb44db15fe4ea48fade5d5bbb9868fdc2b4fce22f4a469d", size = 257282, upload-time = "2025-10-15T15:14:50.236Z" }, + { url = "https://files.pythonhosted.org/packages/aa/e5/0ead8af411411330b928733e1d201384b39251a5f043c1612970310e8283/coverage-7.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c9f08ea03114a637dab06cedb2e914da9dc67fa52c6015c018ff43fdde25b9c2", size = 260430, upload-time = "2025-10-15T15:14:52.413Z" }, + { url = "https://files.pythonhosted.org/packages/ae/66/03dd8bb0ba5b971620dcaac145461950f6d8204953e535d2b20c6b65d729/coverage-7.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce9f3bde4e9b031eaf1eb61df95c1401427029ea1bfddb8621c1161dcb0fa02e", size = 258190, upload-time = "2025-10-15T15:14:54.268Z" }, + { url = "https://files.pythonhosted.org/packages/45/ae/28a9cce40bf3174426cb2f7e71ee172d98e7f6446dff936a7ccecee34b14/coverage-7.11.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:e4dc07e95495923d6fd4d6c27bf70769425b71c89053083843fd78f378558996", size = 256658, upload-time = "2025-10-15T15:14:56.436Z" }, + { url = "https://files.pythonhosted.org/packages/5c/7c/3a44234a8599513684bfc8684878fd7b126c2760f79712bb78c56f19efc4/coverage-7.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:424538266794db2861db4922b05d729ade0940ee69dcf0591ce8f69784db0e11", size = 259342, upload-time = "2025-10-15T15:14:58.538Z" }, + { url = "https://files.pythonhosted.org/packages/e1/e6/0108519cba871af0351725ebdb8660fd7a0fe2ba3850d56d32490c7d9b4b/coverage-7.11.0-cp314-cp314t-win32.whl", hash = "sha256:4c1eeb3fb8eb9e0190bebafd0462936f75717687117339f708f395fe455acc73", size = 219568, upload-time = "2025-10-15T15:15:00.382Z" }, + { url = "https://files.pythonhosted.org/packages/c9/76/44ba876e0942b4e62fdde23ccb029ddb16d19ba1bef081edd00857ba0b16/coverage-7.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b56efee146c98dbf2cf5cffc61b9829d1e94442df4d7398b26892a53992d3547", size = 220687, upload-time = "2025-10-15T15:15:02.322Z" }, + { url = "https://files.pythonhosted.org/packages/b9/0c/0df55ecb20d0d0ed5c322e10a441775e1a3a5d78c60f0c4e1abfe6fcf949/coverage-7.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:b5c2705afa83f49bd91962a4094b6b082f94aef7626365ab3f8f4bd159c5acf3", size = 218711, upload-time = "2025-10-15T15:15:04.575Z" }, + { url = "https://files.pythonhosted.org/packages/5f/04/642c1d8a448ae5ea1369eac8495740a79eb4e581a9fb0cbdce56bbf56da1/coverage-7.11.0-py3-none-any.whl", hash = "sha256:4b7589765348d78fb4e5fb6ea35d07564e387da2fc5efff62e0222971f155f68", size = 207761, upload-time = "2025-10-15T15:15:06.439Z" }, +] + +[package.optional-dependencies] +toml = [ + { name = "tomli", marker = "python_full_version <= '3.11'" }, +] + +[[package]] +name = "cryptography" +version = "46.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9f/33/c00162f49c0e2fe8064a62cb92b93e50c74a72bc370ab92f86112b33ff62/cryptography-46.0.3.tar.gz", hash = "sha256:a8b17438104fed022ce745b362294d9ce35b4c2e45c1d958ad4a4b019285f4a1", size = 749258, upload-time = "2025-10-15T23:18:31.74Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/42/9c391dd801d6cf0d561b5890549d4b27bafcc53b39c31a817e69d87c625b/cryptography-46.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:109d4ddfadf17e8e7779c39f9b18111a09efb969a301a31e987416a0191ed93a", size = 7225004, upload-time = "2025-10-15T23:16:52.239Z" }, + { url = "https://files.pythonhosted.org/packages/1c/67/38769ca6b65f07461eb200e85fc1639b438bdc667be02cf7f2cd6a64601c/cryptography-46.0.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09859af8466b69bc3c27bdf4f5d84a665e0f7ab5088412e9e2ec49758eca5cbc", size = 4296667, upload-time = "2025-10-15T23:16:54.369Z" }, + { url = "https://files.pythonhosted.org/packages/5c/49/498c86566a1d80e978b42f0d702795f69887005548c041636df6ae1ca64c/cryptography-46.0.3-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:01ca9ff2885f3acc98c29f1860552e37f6d7c7d013d7334ff2a9de43a449315d", size = 4450807, upload-time = "2025-10-15T23:16:56.414Z" }, + { url = "https://files.pythonhosted.org/packages/4b/0a/863a3604112174c8624a2ac3c038662d9e59970c7f926acdcfaed8d61142/cryptography-46.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6eae65d4c3d33da080cff9c4ab1f711b15c1d9760809dad6ea763f3812d254cb", size = 4299615, upload-time = "2025-10-15T23:16:58.442Z" }, + { url = "https://files.pythonhosted.org/packages/64/02/b73a533f6b64a69f3cd3872acb6ebc12aef924d8d103133bb3ea750dc703/cryptography-46.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5bf0ed4490068a2e72ac03d786693adeb909981cc596425d09032d372bcc849", size = 4016800, upload-time = "2025-10-15T23:17:00.378Z" }, + { url = "https://files.pythonhosted.org/packages/25/d5/16e41afbfa450cde85a3b7ec599bebefaef16b5c6ba4ec49a3532336ed72/cryptography-46.0.3-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5ecfccd2329e37e9b7112a888e76d9feca2347f12f37918facbb893d7bb88ee8", size = 4984707, upload-time = "2025-10-15T23:17:01.98Z" }, + { url = "https://files.pythonhosted.org/packages/c9/56/e7e69b427c3878352c2fb9b450bd0e19ed552753491d39d7d0a2f5226d41/cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a2c0cd47381a3229c403062f764160d57d4d175e022c1df84e168c6251a22eec", size = 4482541, upload-time = "2025-10-15T23:17:04.078Z" }, + { url = "https://files.pythonhosted.org/packages/78/f6/50736d40d97e8483172f1bb6e698895b92a223dba513b0ca6f06b2365339/cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91", size = 4299464, upload-time = "2025-10-15T23:17:05.483Z" }, + { url = "https://files.pythonhosted.org/packages/00/de/d8e26b1a855f19d9994a19c702fa2e93b0456beccbcfe437eda00e0701f2/cryptography-46.0.3-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:c0a7bb1a68a5d3471880e264621346c48665b3bf1c3759d682fc0864c540bd9e", size = 4950838, upload-time = "2025-10-15T23:17:07.425Z" }, + { url = "https://files.pythonhosted.org/packages/8f/29/798fc4ec461a1c9e9f735f2fc58741b0daae30688f41b2497dcbc9ed1355/cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:10b01676fc208c3e6feeb25a8b83d81767e8059e1fe86e1dc62d10a3018fa926", size = 4481596, upload-time = "2025-10-15T23:17:09.343Z" }, + { url = "https://files.pythonhosted.org/packages/15/8d/03cd48b20a573adfff7652b76271078e3045b9f49387920e7f1f631d125e/cryptography-46.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0abf1ffd6e57c67e92af68330d05760b7b7efb243aab8377e583284dbab72c71", size = 4426782, upload-time = "2025-10-15T23:17:11.22Z" }, + { url = "https://files.pythonhosted.org/packages/fa/b1/ebacbfe53317d55cf33165bda24c86523497a6881f339f9aae5c2e13e57b/cryptography-46.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a04bee9ab6a4da801eb9b51f1b708a1b5b5c9eb48c03f74198464c66f0d344ac", size = 4698381, upload-time = "2025-10-15T23:17:12.829Z" }, + { url = "https://files.pythonhosted.org/packages/96/92/8a6a9525893325fc057a01f654d7efc2c64b9de90413adcf605a85744ff4/cryptography-46.0.3-cp311-abi3-win32.whl", hash = "sha256:f260d0d41e9b4da1ed1e0f1ce571f97fe370b152ab18778e9e8f67d6af432018", size = 3055988, upload-time = "2025-10-15T23:17:14.65Z" }, + { url = "https://files.pythonhosted.org/packages/7e/bf/80fbf45253ea585a1e492a6a17efcb93467701fa79e71550a430c5e60df0/cryptography-46.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:a9a3008438615669153eb86b26b61e09993921ebdd75385ddd748702c5adfddb", size = 3514451, upload-time = "2025-10-15T23:17:16.142Z" }, + { url = "https://files.pythonhosted.org/packages/2e/af/9b302da4c87b0beb9db4e756386a7c6c5b8003cd0e742277888d352ae91d/cryptography-46.0.3-cp311-abi3-win_arm64.whl", hash = "sha256:5d7f93296ee28f68447397bf5198428c9aeeab45705a55d53a6343455dcb2c3c", size = 2928007, upload-time = "2025-10-15T23:17:18.04Z" }, + { url = "https://files.pythonhosted.org/packages/f5/e2/a510aa736755bffa9d2f75029c229111a1d02f8ecd5de03078f4c18d91a3/cryptography-46.0.3-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:00a5e7e87938e5ff9ff5447ab086a5706a957137e6e433841e9d24f38a065217", size = 7158012, upload-time = "2025-10-15T23:17:19.982Z" }, + { url = "https://files.pythonhosted.org/packages/73/dc/9aa866fbdbb95b02e7f9d086f1fccfeebf8953509b87e3f28fff927ff8a0/cryptography-46.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c8daeb2d2174beb4575b77482320303f3d39b8e81153da4f0fb08eb5fe86a6c5", size = 4288728, upload-time = "2025-10-15T23:17:21.527Z" }, + { url = "https://files.pythonhosted.org/packages/c5/fd/bc1daf8230eaa075184cbbf5f8cd00ba9db4fd32d63fb83da4671b72ed8a/cryptography-46.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:39b6755623145ad5eff1dab323f4eae2a32a77a7abef2c5089a04a3d04366715", size = 4435078, upload-time = "2025-10-15T23:17:23.042Z" }, + { url = "https://files.pythonhosted.org/packages/82/98/d3bd5407ce4c60017f8ff9e63ffee4200ab3e23fe05b765cab805a7db008/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:db391fa7c66df6762ee3f00c95a89e6d428f4d60e7abc8328f4fe155b5ac6e54", size = 4293460, upload-time = "2025-10-15T23:17:24.885Z" }, + { url = "https://files.pythonhosted.org/packages/26/e9/e23e7900983c2b8af7a08098db406cf989d7f09caea7897e347598d4cd5b/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:78a97cf6a8839a48c49271cdcbd5cf37ca2c1d6b7fdd86cc864f302b5e9bf459", size = 3995237, upload-time = "2025-10-15T23:17:26.449Z" }, + { url = "https://files.pythonhosted.org/packages/91/15/af68c509d4a138cfe299d0d7ddb14afba15233223ebd933b4bbdbc7155d3/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:dfb781ff7eaa91a6f7fd41776ec37c5853c795d3b358d4896fdbb5df168af422", size = 4967344, upload-time = "2025-10-15T23:17:28.06Z" }, + { url = "https://files.pythonhosted.org/packages/ca/e3/8643d077c53868b681af077edf6b3cb58288b5423610f21c62aadcbe99f4/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6f61efb26e76c45c4a227835ddeae96d83624fb0d29eb5df5b96e14ed1a0afb7", size = 4466564, upload-time = "2025-10-15T23:17:29.665Z" }, + { url = "https://files.pythonhosted.org/packages/0e/43/c1e8726fa59c236ff477ff2b5dc071e54b21e5a1e51aa2cee1676f1c986f/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:23b1a8f26e43f47ceb6d6a43115f33a5a37d57df4ea0ca295b780ae8546e8044", size = 4292415, upload-time = "2025-10-15T23:17:31.686Z" }, + { url = "https://files.pythonhosted.org/packages/42/f9/2f8fefdb1aee8a8e3256a0568cffc4e6d517b256a2fe97a029b3f1b9fe7e/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:b419ae593c86b87014b9be7396b385491ad7f320bde96826d0dd174459e54665", size = 4931457, upload-time = "2025-10-15T23:17:33.478Z" }, + { url = "https://files.pythonhosted.org/packages/79/30/9b54127a9a778ccd6d27c3da7563e9f2d341826075ceab89ae3b41bf5be2/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:50fc3343ac490c6b08c0cf0d704e881d0d660be923fd3076db3e932007e726e3", size = 4466074, upload-time = "2025-10-15T23:17:35.158Z" }, + { url = "https://files.pythonhosted.org/packages/ac/68/b4f4a10928e26c941b1b6a179143af9f4d27d88fe84a6a3c53592d2e76bf/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22d7e97932f511d6b0b04f2bfd818d73dcd5928db509460aaf48384778eb6d20", size = 4420569, upload-time = "2025-10-15T23:17:37.188Z" }, + { url = "https://files.pythonhosted.org/packages/a3/49/3746dab4c0d1979888f125226357d3262a6dd40e114ac29e3d2abdf1ec55/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d55f3dffadd674514ad19451161118fd010988540cee43d8bc20675e775925de", size = 4681941, upload-time = "2025-10-15T23:17:39.236Z" }, + { url = "https://files.pythonhosted.org/packages/fd/30/27654c1dbaf7e4a3531fa1fc77986d04aefa4d6d78259a62c9dc13d7ad36/cryptography-46.0.3-cp314-cp314t-win32.whl", hash = "sha256:8a6e050cb6164d3f830453754094c086ff2d0b2f3a897a1d9820f6139a1f0914", size = 3022339, upload-time = "2025-10-15T23:17:40.888Z" }, + { url = "https://files.pythonhosted.org/packages/f6/30/640f34ccd4d2a1bc88367b54b926b781b5a018d65f404d409aba76a84b1c/cryptography-46.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:760f83faa07f8b64e9c33fc963d790a2edb24efb479e3520c14a45741cd9b2db", size = 3494315, upload-time = "2025-10-15T23:17:42.769Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8b/88cc7e3bd0a8e7b861f26981f7b820e1f46aa9d26cc482d0feba0ecb4919/cryptography-46.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:516ea134e703e9fe26bcd1277a4b59ad30586ea90c365a87781d7887a646fe21", size = 2919331, upload-time = "2025-10-15T23:17:44.468Z" }, + { url = "https://files.pythonhosted.org/packages/fd/23/45fe7f376a7df8daf6da3556603b36f53475a99ce4faacb6ba2cf3d82021/cryptography-46.0.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:cb3d760a6117f621261d662bccc8ef5bc32ca673e037c83fbe565324f5c46936", size = 7218248, upload-time = "2025-10-15T23:17:46.294Z" }, + { url = "https://files.pythonhosted.org/packages/27/32/b68d27471372737054cbd34c84981f9edbc24fe67ca225d389799614e27f/cryptography-46.0.3-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4b7387121ac7d15e550f5cb4a43aef2559ed759c35df7336c402bb8275ac9683", size = 4294089, upload-time = "2025-10-15T23:17:48.269Z" }, + { url = "https://files.pythonhosted.org/packages/26/42/fa8389d4478368743e24e61eea78846a0006caffaf72ea24a15159215a14/cryptography-46.0.3-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15ab9b093e8f09daab0f2159bb7e47532596075139dd74365da52ecc9cb46c5d", size = 4440029, upload-time = "2025-10-15T23:17:49.837Z" }, + { url = "https://files.pythonhosted.org/packages/5f/eb/f483db0ec5ac040824f269e93dd2bd8a21ecd1027e77ad7bdf6914f2fd80/cryptography-46.0.3-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:46acf53b40ea38f9c6c229599a4a13f0d46a6c3fa9ef19fc1a124d62e338dfa0", size = 4297222, upload-time = "2025-10-15T23:17:51.357Z" }, + { url = "https://files.pythonhosted.org/packages/fd/cf/da9502c4e1912cb1da3807ea3618a6829bee8207456fbbeebc361ec38ba3/cryptography-46.0.3-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10ca84c4668d066a9878890047f03546f3ae0a6b8b39b697457b7757aaf18dbc", size = 4012280, upload-time = "2025-10-15T23:17:52.964Z" }, + { url = "https://files.pythonhosted.org/packages/6b/8f/9adb86b93330e0df8b3dcf03eae67c33ba89958fc2e03862ef1ac2b42465/cryptography-46.0.3-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:36e627112085bb3b81b19fed209c05ce2a52ee8b15d161b7c643a7d5a88491f3", size = 4978958, upload-time = "2025-10-15T23:17:54.965Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a0/5fa77988289c34bdb9f913f5606ecc9ada1adb5ae870bd0d1054a7021cc4/cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1000713389b75c449a6e979ffc7dcc8ac90b437048766cef052d4d30b8220971", size = 4473714, upload-time = "2025-10-15T23:17:56.754Z" }, + { url = "https://files.pythonhosted.org/packages/14/e5/fc82d72a58d41c393697aa18c9abe5ae1214ff6f2a5c18ac470f92777895/cryptography-46.0.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:b02cf04496f6576afffef5ddd04a0cb7d49cf6be16a9059d793a30b035f6b6ac", size = 4296970, upload-time = "2025-10-15T23:17:58.588Z" }, + { url = "https://files.pythonhosted.org/packages/78/06/5663ed35438d0b09056973994f1aec467492b33bd31da36e468b01ec1097/cryptography-46.0.3-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:71e842ec9bc7abf543b47cf86b9a743baa95f4677d22baa4c7d5c69e49e9bc04", size = 4940236, upload-time = "2025-10-15T23:18:00.897Z" }, + { url = "https://files.pythonhosted.org/packages/fc/59/873633f3f2dcd8a053b8dd1d38f783043b5fce589c0f6988bf55ef57e43e/cryptography-46.0.3-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:402b58fc32614f00980b66d6e56a5b4118e6cb362ae8f3fda141ba4689bd4506", size = 4472642, upload-time = "2025-10-15T23:18:02.749Z" }, + { url = "https://files.pythonhosted.org/packages/3d/39/8e71f3930e40f6877737d6f69248cf74d4e34b886a3967d32f919cc50d3b/cryptography-46.0.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef639cb3372f69ec44915fafcd6698b6cc78fbe0c2ea41be867f6ed612811963", size = 4423126, upload-time = "2025-10-15T23:18:04.85Z" }, + { url = "https://files.pythonhosted.org/packages/cd/c7/f65027c2810e14c3e7268353b1681932b87e5a48e65505d8cc17c99e36ae/cryptography-46.0.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b51b8ca4f1c6453d8829e1eb7299499ca7f313900dd4d89a24b8b87c0a780d4", size = 4686573, upload-time = "2025-10-15T23:18:06.908Z" }, + { url = "https://files.pythonhosted.org/packages/0a/6e/1c8331ddf91ca4730ab3086a0f1be19c65510a33b5a441cb334e7a2d2560/cryptography-46.0.3-cp38-abi3-win32.whl", hash = "sha256:6276eb85ef938dc035d59b87c8a7dc559a232f954962520137529d77b18ff1df", size = 3036695, upload-time = "2025-10-15T23:18:08.672Z" }, + { url = "https://files.pythonhosted.org/packages/90/45/b0d691df20633eff80955a0fc7695ff9051ffce8b69741444bd9ed7bd0db/cryptography-46.0.3-cp38-abi3-win_amd64.whl", hash = "sha256:416260257577718c05135c55958b674000baef9a1c7d9e8f306ec60d71db850f", size = 3501720, upload-time = "2025-10-15T23:18:10.632Z" }, + { url = "https://files.pythonhosted.org/packages/e8/cb/2da4cc83f5edb9c3257d09e1e7ab7b23f049c7962cae8d842bbef0a9cec9/cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372", size = 2918740, upload-time = "2025-10-15T23:18:12.277Z" }, + { url = "https://files.pythonhosted.org/packages/06/8a/e60e46adab4362a682cf142c7dcb5bf79b782ab2199b0dcb81f55970807f/cryptography-46.0.3-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7ce938a99998ed3c8aa7e7272dca1a610401ede816d36d0693907d863b10d9ea", size = 3698132, upload-time = "2025-10-15T23:18:17.056Z" }, + { url = "https://files.pythonhosted.org/packages/da/38/f59940ec4ee91e93d3311f7532671a5cef5570eb04a144bf203b58552d11/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:191bb60a7be5e6f54e30ba16fdfae78ad3a342a0599eb4193ba88e3f3d6e185b", size = 4243992, upload-time = "2025-10-15T23:18:18.695Z" }, + { url = "https://files.pythonhosted.org/packages/b0/0c/35b3d92ddebfdfda76bb485738306545817253d0a3ded0bfe80ef8e67aa5/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c70cc23f12726be8f8bc72e41d5065d77e4515efae3690326764ea1b07845cfb", size = 4409944, upload-time = "2025-10-15T23:18:20.597Z" }, + { url = "https://files.pythonhosted.org/packages/99/55/181022996c4063fc0e7666a47049a1ca705abb9c8a13830f074edb347495/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:9394673a9f4de09e28b5356e7fff97d778f8abad85c9d5ac4a4b7e25a0de7717", size = 4242957, upload-time = "2025-10-15T23:18:22.18Z" }, + { url = "https://files.pythonhosted.org/packages/ba/af/72cd6ef29f9c5f731251acadaeb821559fe25f10852f44a63374c9ca08c1/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:94cd0549accc38d1494e1f8de71eca837d0509d0d44bf11d158524b0e12cebf9", size = 4409447, upload-time = "2025-10-15T23:18:24.209Z" }, + { url = "https://files.pythonhosted.org/packages/0d/c3/e90f4a4feae6410f914f8ebac129b9ae7a8c92eb60a638012dde42030a9d/cryptography-46.0.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6b5063083824e5509fdba180721d55909ffacccc8adbec85268b48439423d78c", size = 3438528, upload-time = "2025-10-15T23:18:26.227Z" }, +] + +[[package]] +name = "cyclopts" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "docstring-parser" }, + { name = "rich" }, + { name = "rich-rst" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9a/d1/2f2b99ec5ea54ac18baadfc4a011e2a1743c1eaae1e39838ca520dcf4811/cyclopts-4.0.0.tar.gz", hash = "sha256:0dae712085e91d32cc099ea3d78f305b0100a3998b1dec693be9feb0b1be101f", size = 143546, upload-time = "2025-10-20T18:33:01.456Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/0e/0a22e076944600aeb06f40b7e03bbd762a42d56d43a2f5f4ab954aed9005/cyclopts-4.0.0-py3-none-any.whl", hash = "sha256:e64801a2c86b681f08323fd50110444ee961236a0bae402a66d2cc3feda33da7", size = 178837, upload-time = "2025-10-20T18:33:00.191Z" }, +] + +[[package]] +name = "diskcache" +version = "5.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916, upload-time = "2023-08-31T06:12:00.316Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550, upload-time = "2023-08-31T06:11:58.822Z" }, +] + +[[package]] +name = "dnspython" +version = "2.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, +] + +[[package]] +name = "docstring-parser" +version = "0.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/9d/c3b43da9515bd270df0f80548d9944e389870713cc1fe2b8fb35fe2bcefd/docstring_parser-0.17.0.tar.gz", hash = "sha256:583de4a309722b3315439bb31d64ba3eebada841f2e2cee23b99df001434c912", size = 27442, upload-time = "2025-07-21T07:35:01.868Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708", size = 36896, upload-time = "2025-07-21T07:35:00.684Z" }, +] + +[[package]] +name = "docutils" +version = "0.22.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4a/c0/89fe6215b443b919cb98a5002e107cb5026854ed1ccb6b5833e0768419d1/docutils-0.22.2.tar.gz", hash = "sha256:9fdb771707c8784c8f2728b67cb2c691305933d68137ef95a75db5f4dfbc213d", size = 2289092, upload-time = "2025-09-20T17:55:47.994Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/66/dd/f95350e853a4468ec37478414fc04ae2d61dad7a947b3015c3dcc51a09b9/docutils-0.22.2-py3-none-any.whl", hash = "sha256:b0e98d679283fc3bb0ead8a5da7f501baa632654e7056e9c5846842213d674d8", size = 632667, upload-time = "2025-09-20T17:55:43.052Z" }, +] + +[[package]] +name = "email-validator" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dnspython" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238, upload-time = "2025-08-26T13:09:06.831Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" }, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, +] + +[[package]] +name = "fastapps" +version = "1.1.1" +source = { editable = "." } +dependencies = [ + { name = "click" }, + { name = "cryptography" }, + { name = "fastmcp" }, + { name = "httpx" }, + { name = "pydantic" }, + { name = "pyjwt" }, + { name = "pyngrok" }, + { name = "rich" }, + { name = "uvicorn" }, +] + +[package.optional-dependencies] +dev = [ + { name = "black" }, + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "pytest-cov" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "black", marker = "extra == 'dev'", specifier = ">=23.0.0" }, + { name = "click", specifier = ">=8.0.0" }, + { name = "cryptography", specifier = ">=41.0.0" }, + { name = "fastmcp", specifier = ">=0.1.0" }, + { name = "httpx", specifier = ">=0.28.0" }, + { name = "pydantic", specifier = ">=2.0.0" }, + { name = "pyjwt", specifier = ">=2.8.0" }, + { name = "pyngrok", specifier = ">=7.4.0" }, + { name = "pytest", marker = "extra == 'dev'", specifier = ">=7.0.0" }, + { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.21.0" }, + { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=4.0.0" }, + { name = "rich", specifier = ">=13.0.0" }, + { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.1.0" }, + { name = "uvicorn", specifier = ">=0.20.0" }, +] +provides-extras = ["dev"] + +[[package]] +name = "fastmcp" +version = "2.13.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "authlib" }, + { name = "cyclopts" }, + { name = "exceptiongroup" }, + { name = "httpx" }, + { name = "mcp" }, + { name = "openapi-core" }, + { name = "openapi-pydantic" }, + { name = "platformdirs" }, + { name = "py-key-value-aio", extra = ["disk", "keyring", "memory"] }, + { name = "pydantic", extra = ["email"] }, + { name = "pyperclip" }, + { name = "python-dotenv" }, + { name = "rich" }, + { name = "websockets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f2/bd/ad8a0cc9ea3e8bfe8fb63a00be985d4c887c3c0a454d26c712c160af489f/fastmcp-2.13.0.1.tar.gz", hash = "sha256:d6dbd52a6b06fc1797db9fe0b487db966b4a4d34d9c7dd87b9918d5ec775dcb7", size = 7768846, upload-time = "2025-10-26T15:43:00.567Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/0c/7e966e01240f7a9347d22d09e9d60cb46eaad68ca2ba24830755dab2c55c/fastmcp-2.13.0.1-py3-none-any.whl", hash = "sha256:60a8313b48803a2ddfad2d0fe8b9dd1f231aba7564c0551cad8447527ffe46e2", size = 367504, upload-time = "2025-10-26T15:42:58.98Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "httpx-sse" +version = "0.4.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, +] + +[[package]] +name = "idna" +version = "3.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, +] + +[[package]] +name = "importlib-metadata" +version = "8.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "isodate" +version = "0.7.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/4d/e940025e2ce31a8ce1202635910747e5a87cc3a6a6bb2d00973375014749/isodate-0.7.2.tar.gz", hash = "sha256:4cd1aa0f43ca76f4a6c6c0292a85f40b35ec2e43e315b59f06e6d32171a953e6", size = 29705, upload-time = "2024-10-08T23:04:11.5Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl", hash = "sha256:28009937d8031054830160fce6d409ed342816b543597cece116d966c6d99e15", size = 22320, upload-time = "2024-10-08T23:04:09.501Z" }, +] + +[[package]] +name = "jaraco-classes" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "more-itertools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" }, +] + +[[package]] +name = "jaraco-context" +version = "6.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "backports-tarfile", marker = "python_full_version < '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/ad/f3777b81bf0b6e7bc7514a1656d3e637b2e8e15fab2ce3235730b3e7a4e6/jaraco_context-6.0.1.tar.gz", hash = "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3", size = 13912, upload-time = "2024-08-20T03:39:27.358Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/db/0c52c4cf5e4bd9f5d7135ec7669a3a767af21b3a308e1ed3674881e52b62/jaraco.context-6.0.1-py3-none-any.whl", hash = "sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4", size = 6825, upload-time = "2024-08-20T03:39:25.966Z" }, +] + +[[package]] +name = "jaraco-functools" +version = "4.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "more-itertools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f7/ed/1aa2d585304ec07262e1a83a9889880701079dde796ac7b1d1826f40c63d/jaraco_functools-4.3.0.tar.gz", hash = "sha256:cfd13ad0dd2c47a3600b439ef72d8615d482cedcff1632930d6f28924d92f294", size = 19755, upload-time = "2025-08-18T20:05:09.91Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b4/09/726f168acad366b11e420df31bf1c702a54d373a83f968d94141a8c3fde0/jaraco_functools-4.3.0-py3-none-any.whl", hash = "sha256:227ff8ed6f7b8f62c56deff101545fa7543cf2c8e7b82a7c2116e672f29c26e8", size = 10408, upload-time = "2025-08-18T20:05:08.69Z" }, +] + +[[package]] +name = "jeepney" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758, upload-time = "2025-02-27T18:51:01.684Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" }, +] + +[[package]] +name = "jsonschema" +version = "4.25.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/74/69/f7185de793a29082a9f3c7728268ffb31cb5095131a9c139a74078e27336/jsonschema-4.25.1.tar.gz", hash = "sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85", size = 357342, upload-time = "2025-08-18T17:03:50.038Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63", size = 90040, upload-time = "2025-08-18T17:03:48.373Z" }, +] + +[[package]] +name = "jsonschema-path" +version = "0.3.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pathable" }, + { name = "pyyaml" }, + { name = "referencing" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6e/45/41ebc679c2a4fced6a722f624c18d658dee42612b83ea24c1caf7c0eb3a8/jsonschema_path-0.3.4.tar.gz", hash = "sha256:8365356039f16cc65fddffafda5f58766e34bebab7d6d105616ab52bc4297001", size = 11159, upload-time = "2025-01-24T14:33:16.547Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/58/3485da8cb93d2f393bce453adeef16896751f14ba3e2024bc21dc9597646/jsonschema_path-0.3.4-py3-none-any.whl", hash = "sha256:f502191fdc2b22050f9a81c9237be9d27145b9001c55842bece5e94e382e52f8", size = 14810, upload-time = "2025-01-24T14:33:14.652Z" }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2025.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, +] + +[[package]] +name = "keyring" +version = "25.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata", marker = "python_full_version < '3.12'" }, + { name = "jaraco-classes" }, + { name = "jaraco-context" }, + { name = "jaraco-functools" }, + { name = "jeepney", marker = "sys_platform == 'linux'" }, + { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, + { name = "secretstorage", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/70/09/d904a6e96f76ff214be59e7aa6ef7190008f52a0ab6689760a98de0bf37d/keyring-25.6.0.tar.gz", hash = "sha256:0b39998aa941431eb3d9b0d4b2460bc773b9df6fed7621c2dfb291a7e0187a66", size = 62750, upload-time = "2024-12-25T15:26:45.782Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/32/da7f44bcb1105d3e88a0b74ebdca50c59121d2ddf71c9e34ba47df7f3a56/keyring-25.6.0-py3-none-any.whl", hash = "sha256:552a3f7af126ece7ed5c89753650eec89c7eaae8617d0aa4d9ad2b75111266bd", size = 39085, upload-time = "2024-12-25T15:26:44.377Z" }, +] + +[[package]] +name = "lazy-object-proxy" +version = "1.12.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/08/a2/69df9c6ba6d316cfd81fe2381e464db3e6de5db45f8c43c6a23504abf8cb/lazy_object_proxy-1.12.0.tar.gz", hash = "sha256:1f5a462d92fd0cfb82f1fab28b51bfb209fabbe6aabf7f0d51472c0c124c0c61", size = 43681, upload-time = "2025-08-22T13:50:06.783Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/01/b3/4684b1e128a87821e485f5a901b179790e6b5bc02f89b7ee19c23be36ef3/lazy_object_proxy-1.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1cf69cd1a6c7fe2dbcc3edaa017cf010f4192e53796538cc7d5e1fedbfa4bcff", size = 26656, upload-time = "2025-08-22T13:42:30.605Z" }, + { url = "https://files.pythonhosted.org/packages/3a/03/1bdc21d9a6df9ff72d70b2ff17d8609321bea4b0d3cffd2cea92fb2ef738/lazy_object_proxy-1.12.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:efff4375a8c52f55a145dc8487a2108c2140f0bec4151ab4e1843e52eb9987ad", size = 68832, upload-time = "2025-08-22T13:42:31.675Z" }, + { url = "https://files.pythonhosted.org/packages/3d/4b/5788e5e8bd01d19af71e50077ab020bc5cce67e935066cd65e1215a09ff9/lazy_object_proxy-1.12.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1192e8c2f1031a6ff453ee40213afa01ba765b3dc861302cd91dbdb2e2660b00", size = 69148, upload-time = "2025-08-22T13:42:32.876Z" }, + { url = "https://files.pythonhosted.org/packages/79/0e/090bf070f7a0de44c61659cb7f74c2fe02309a77ca8c4b43adfe0b695f66/lazy_object_proxy-1.12.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3605b632e82a1cbc32a1e5034278a64db555b3496e0795723ee697006b980508", size = 67800, upload-time = "2025-08-22T13:42:34.054Z" }, + { url = "https://files.pythonhosted.org/packages/cf/d2/b320325adbb2d119156f7c506a5fbfa37fcab15c26d13cf789a90a6de04e/lazy_object_proxy-1.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a61095f5d9d1a743e1e20ec6d6db6c2ca511961777257ebd9b288951b23b44fa", size = 68085, upload-time = "2025-08-22T13:42:35.197Z" }, + { url = "https://files.pythonhosted.org/packages/6a/48/4b718c937004bf71cd82af3713874656bcb8d0cc78600bf33bb9619adc6c/lazy_object_proxy-1.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:997b1d6e10ecc6fb6fe0f2c959791ae59599f41da61d652f6c903d1ee58b7370", size = 26535, upload-time = "2025-08-22T13:42:36.521Z" }, + { url = "https://files.pythonhosted.org/packages/0d/1b/b5f5bd6bda26f1e15cd3232b223892e4498e34ec70a7f4f11c401ac969f1/lazy_object_proxy-1.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8ee0d6027b760a11cc18281e702c0309dd92da458a74b4c15025d7fc490deede", size = 26746, upload-time = "2025-08-22T13:42:37.572Z" }, + { url = "https://files.pythonhosted.org/packages/55/64/314889b618075c2bfc19293ffa9153ce880ac6153aacfd0a52fcabf21a66/lazy_object_proxy-1.12.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4ab2c584e3cc8be0dfca422e05ad30a9abe3555ce63e9ab7a559f62f8dbc6ff9", size = 71457, upload-time = "2025-08-22T13:42:38.743Z" }, + { url = "https://files.pythonhosted.org/packages/11/53/857fc2827fc1e13fbdfc0ba2629a7d2579645a06192d5461809540b78913/lazy_object_proxy-1.12.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:14e348185adbd03ec17d051e169ec45686dcd840a3779c9d4c10aabe2ca6e1c0", size = 71036, upload-time = "2025-08-22T13:42:40.184Z" }, + { url = "https://files.pythonhosted.org/packages/2b/24/e581ffed864cd33c1b445b5763d617448ebb880f48675fc9de0471a95cbc/lazy_object_proxy-1.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c4fcbe74fb85df8ba7825fa05eddca764138da752904b378f0ae5ab33a36c308", size = 69329, upload-time = "2025-08-22T13:42:41.311Z" }, + { url = "https://files.pythonhosted.org/packages/78/be/15f8f5a0b0b2e668e756a152257d26370132c97f2f1943329b08f057eff0/lazy_object_proxy-1.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:563d2ec8e4d4b68ee7848c5ab4d6057a6d703cb7963b342968bb8758dda33a23", size = 70690, upload-time = "2025-08-22T13:42:42.51Z" }, + { url = "https://files.pythonhosted.org/packages/5d/aa/f02be9bbfb270e13ee608c2b28b8771f20a5f64356c6d9317b20043c6129/lazy_object_proxy-1.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:53c7fd99eb156bbb82cbc5d5188891d8fdd805ba6c1e3b92b90092da2a837073", size = 26563, upload-time = "2025-08-22T13:42:43.685Z" }, + { url = "https://files.pythonhosted.org/packages/f4/26/b74c791008841f8ad896c7f293415136c66cc27e7c7577de4ee68040c110/lazy_object_proxy-1.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:86fd61cb2ba249b9f436d789d1356deae69ad3231dc3c0f17293ac535162672e", size = 26745, upload-time = "2025-08-22T13:42:44.982Z" }, + { url = "https://files.pythonhosted.org/packages/9b/52/641870d309e5d1fb1ea7d462a818ca727e43bfa431d8c34b173eb090348c/lazy_object_proxy-1.12.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:81d1852fb30fab81696f93db1b1e55a5d1ff7940838191062f5f56987d5fcc3e", size = 71537, upload-time = "2025-08-22T13:42:46.141Z" }, + { url = "https://files.pythonhosted.org/packages/47/b6/919118e99d51c5e76e8bf5a27df406884921c0acf2c7b8a3b38d847ab3e9/lazy_object_proxy-1.12.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be9045646d83f6c2664c1330904b245ae2371b5c57a3195e4028aedc9f999655", size = 71141, upload-time = "2025-08-22T13:42:47.375Z" }, + { url = "https://files.pythonhosted.org/packages/e5/47/1d20e626567b41de085cf4d4fb3661a56c159feaa73c825917b3b4d4f806/lazy_object_proxy-1.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:67f07ab742f1adfb3966c40f630baaa7902be4222a17941f3d85fd1dae5565ff", size = 69449, upload-time = "2025-08-22T13:42:48.49Z" }, + { url = "https://files.pythonhosted.org/packages/58/8d/25c20ff1a1a8426d9af2d0b6f29f6388005fc8cd10d6ee71f48bff86fdd0/lazy_object_proxy-1.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:75ba769017b944fcacbf6a80c18b2761a1795b03f8899acdad1f1c39db4409be", size = 70744, upload-time = "2025-08-22T13:42:49.608Z" }, + { url = "https://files.pythonhosted.org/packages/c0/67/8ec9abe15c4f8a4bcc6e65160a2c667240d025cbb6591b879bea55625263/lazy_object_proxy-1.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:7b22c2bbfb155706b928ac4d74c1a63ac8552a55ba7fff4445155523ea4067e1", size = 26568, upload-time = "2025-08-22T13:42:57.719Z" }, + { url = "https://files.pythonhosted.org/packages/23/12/cd2235463f3469fd6c62d41d92b7f120e8134f76e52421413a0ad16d493e/lazy_object_proxy-1.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4a79b909aa16bde8ae606f06e6bbc9d3219d2e57fb3e0076e17879072b742c65", size = 27391, upload-time = "2025-08-22T13:42:50.62Z" }, + { url = "https://files.pythonhosted.org/packages/60/9e/f1c53e39bbebad2e8609c67d0830cc275f694d0ea23d78e8f6db526c12d3/lazy_object_proxy-1.12.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:338ab2f132276203e404951205fe80c3fd59429b3a724e7b662b2eb539bb1be9", size = 80552, upload-time = "2025-08-22T13:42:51.731Z" }, + { url = "https://files.pythonhosted.org/packages/4c/b6/6c513693448dcb317d9d8c91d91f47addc09553613379e504435b4cc8b3e/lazy_object_proxy-1.12.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c40b3c9faee2e32bfce0df4ae63f4e73529766893258eca78548bac801c8f66", size = 82857, upload-time = "2025-08-22T13:42:53.225Z" }, + { url = "https://files.pythonhosted.org/packages/12/1c/d9c4aaa4c75da11eb7c22c43d7c90a53b4fca0e27784a5ab207768debea7/lazy_object_proxy-1.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:717484c309df78cedf48396e420fa57fc8a2b1f06ea889df7248fdd156e58847", size = 80833, upload-time = "2025-08-22T13:42:54.391Z" }, + { url = "https://files.pythonhosted.org/packages/0b/ae/29117275aac7d7d78ae4f5a4787f36ff33262499d486ac0bf3e0b97889f6/lazy_object_proxy-1.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a6b7ea5ea1ffe15059eb44bcbcb258f97bcb40e139b88152c40d07b1a1dfc9ac", size = 79516, upload-time = "2025-08-22T13:42:55.812Z" }, + { url = "https://files.pythonhosted.org/packages/19/40/b4e48b2c38c69392ae702ae7afa7b6551e0ca5d38263198b7c79de8b3bdf/lazy_object_proxy-1.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:08c465fb5cd23527512f9bd7b4c7ba6cec33e28aad36fbbe46bf7b858f9f3f7f", size = 27656, upload-time = "2025-08-22T13:42:56.793Z" }, + { url = "https://files.pythonhosted.org/packages/ef/3a/277857b51ae419a1574557c0b12e0d06bf327b758ba94cafc664cb1e2f66/lazy_object_proxy-1.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c9defba70ab943f1df98a656247966d7729da2fe9c2d5d85346464bf320820a3", size = 26582, upload-time = "2025-08-22T13:49:49.366Z" }, + { url = "https://files.pythonhosted.org/packages/1a/b6/c5e0fa43535bb9c87880e0ba037cdb1c50e01850b0831e80eb4f4762f270/lazy_object_proxy-1.12.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6763941dbf97eea6b90f5b06eb4da9418cc088fce0e3883f5816090f9afcde4a", size = 71059, upload-time = "2025-08-22T13:49:50.488Z" }, + { url = "https://files.pythonhosted.org/packages/06/8a/7dcad19c685963c652624702f1a968ff10220b16bfcc442257038216bf55/lazy_object_proxy-1.12.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fdc70d81235fc586b9e3d1aeef7d1553259b62ecaae9db2167a5d2550dcc391a", size = 71034, upload-time = "2025-08-22T13:49:54.224Z" }, + { url = "https://files.pythonhosted.org/packages/12/ac/34cbfb433a10e28c7fd830f91c5a348462ba748413cbb950c7f259e67aa7/lazy_object_proxy-1.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0a83c6f7a6b2bfc11ef3ed67f8cbe99f8ff500b05655d8e7df9aab993a6abc95", size = 69529, upload-time = "2025-08-22T13:49:55.29Z" }, + { url = "https://files.pythonhosted.org/packages/6f/6a/11ad7e349307c3ca4c0175db7a77d60ce42a41c60bcb11800aabd6a8acb8/lazy_object_proxy-1.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:256262384ebd2a77b023ad02fbcc9326282bcfd16484d5531154b02bc304f4c5", size = 70391, upload-time = "2025-08-22T13:49:56.35Z" }, + { url = "https://files.pythonhosted.org/packages/59/97/9b410ed8fbc6e79c1ee8b13f8777a80137d4bc189caf2c6202358e66192c/lazy_object_proxy-1.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:7601ec171c7e8584f8ff3f4e440aa2eebf93e854f04639263875b8c2971f819f", size = 26988, upload-time = "2025-08-22T13:49:57.302Z" }, + { url = "https://files.pythonhosted.org/packages/41/a0/b91504515c1f9a299fc157967ffbd2f0321bce0516a3d5b89f6f4cad0355/lazy_object_proxy-1.12.0-pp39.pp310.pp311.graalpy311-none-any.whl", hash = "sha256:c3b2e0af1f7f77c4263759c4824316ce458fabe0fceadcd24ef8ca08b2d1e402", size = 15072, upload-time = "2025-08-22T13:50:05.498Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" }, + { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" }, + { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" }, + { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" }, + { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" }, + { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" }, + { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" }, + { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" }, + { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" }, + { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" }, + { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" }, + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, +] + +[[package]] +name = "mcp" +version = "1.19.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "httpx" }, + { name = "httpx-sse" }, + { name = "jsonschema" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "python-multipart" }, + { name = "pywin32", marker = "sys_platform == 'win32'" }, + { name = "sse-starlette" }, + { name = "starlette" }, + { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/69/2b/916852a5668f45d8787378461eaa1244876d77575ffef024483c94c0649c/mcp-1.19.0.tar.gz", hash = "sha256:213de0d3cd63f71bc08ffe9cc8d4409cc87acffd383f6195d2ce0457c021b5c1", size = 444163, upload-time = "2025-10-24T01:11:15.839Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/a3/3e71a875a08b6a830b88c40bc413bff01f1650f1efe8a054b5e90a9d4f56/mcp-1.19.0-py3-none-any.whl", hash = "sha256:f5907fe1c0167255f916718f376d05f09a830a215327a3ccdd5ec8a519f2e572", size = 170105, upload-time = "2025-10-24T01:11:14.151Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + +[[package]] +name = "more-itertools" +version = "10.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ea/5d/38b681d3fce7a266dd9ab73c66959406d565b3e85f21d5e66e1181d93721/more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd", size = 137431, upload-time = "2025-09-02T15:23:11.018Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", size = 69667, upload-time = "2025-09-02T15:23:09.635Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + +[[package]] +name = "openapi-core" +version = "0.19.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "isodate" }, + { name = "jsonschema" }, + { name = "jsonschema-path" }, + { name = "more-itertools" }, + { name = "openapi-schema-validator" }, + { name = "openapi-spec-validator" }, + { name = "parse" }, + { name = "typing-extensions" }, + { name = "werkzeug" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/35/1acaa5f2fcc6e54eded34a2ec74b479439c4e469fc4e8d0e803fda0234db/openapi_core-0.19.5.tar.gz", hash = "sha256:421e753da56c391704454e66afe4803a290108590ac8fa6f4a4487f4ec11f2d3", size = 103264, upload-time = "2025-03-20T20:17:28.193Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/6f/83ead0e2e30a90445ee4fc0135f43741aebc30cca5b43f20968b603e30b6/openapi_core-0.19.5-py3-none-any.whl", hash = "sha256:ef7210e83a59394f46ce282639d8d26ad6fc8094aa904c9c16eb1bac8908911f", size = 106595, upload-time = "2025-03-20T20:17:26.77Z" }, +] + +[[package]] +name = "openapi-pydantic" +version = "0.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/02/2e/58d83848dd1a79cb92ed8e63f6ba901ca282c5f09d04af9423ec26c56fd7/openapi_pydantic-0.5.1.tar.gz", hash = "sha256:ff6835af6bde7a459fb93eb93bb92b8749b754fc6e51b2f1590a19dc3005ee0d", size = 60892, upload-time = "2025-01-08T19:29:27.083Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/cf/03675d8bd8ecbf4445504d8071adab19f5f993676795708e36402ab38263/openapi_pydantic-0.5.1-py3-none-any.whl", hash = "sha256:a3a09ef4586f5bd760a8df7f43028b60cafb6d9f61de2acba9574766255ab146", size = 96381, upload-time = "2025-01-08T19:29:25.275Z" }, +] + +[[package]] +name = "openapi-schema-validator" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jsonschema" }, + { name = "jsonschema-specifications" }, + { name = "rfc3339-validator" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/f3/5507ad3325169347cd8ced61c232ff3df70e2b250c49f0fe140edb4973c6/openapi_schema_validator-0.6.3.tar.gz", hash = "sha256:f37bace4fc2a5d96692f4f8b31dc0f8d7400fd04f3a937798eaf880d425de6ee", size = 11550, upload-time = "2025-01-10T18:08:22.268Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/21/c6/ad0fba32775ae749016829dace42ed80f4407b171da41313d1a3a5f102e4/openapi_schema_validator-0.6.3-py3-none-any.whl", hash = "sha256:f3b9870f4e556b5a62a1c39da72a6b4b16f3ad9c73dc80084b1b11e74ba148a3", size = 8755, upload-time = "2025-01-10T18:08:19.758Z" }, +] + +[[package]] +name = "openapi-spec-validator" +version = "0.7.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jsonschema" }, + { name = "jsonschema-path" }, + { name = "lazy-object-proxy" }, + { name = "openapi-schema-validator" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/82/af/fe2d7618d6eae6fb3a82766a44ed87cd8d6d82b4564ed1c7cfb0f6378e91/openapi_spec_validator-0.7.2.tar.gz", hash = "sha256:cc029309b5c5dbc7859df0372d55e9d1ff43e96d678b9ba087f7c56fc586f734", size = 36855, upload-time = "2025-06-07T14:48:56.299Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/dd/b3fd642260cb17532f66cc1e8250f3507d1e580483e209dc1e9d13bd980d/openapi_spec_validator-0.7.2-py3-none-any.whl", hash = "sha256:4bbdc0894ec85f1d1bea1d6d9c8b2c3c8d7ccaa13577ef40da9c006c9fd0eb60", size = 39713, upload-time = "2025-06-07T14:48:54.077Z" }, +] + +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, +] + +[[package]] +name = "parse" +version = "1.20.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4f/78/d9b09ba24bb36ef8b83b71be547e118d46214735b6dfb39e4bfde0e9b9dd/parse-1.20.2.tar.gz", hash = "sha256:b41d604d16503c79d81af5165155c0b20f6c8d6c559efa66b4b695c3e5a0a0ce", size = 29391, upload-time = "2024-06-11T04:41:57.34Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/31/ba45bf0b2aa7898d81cbbfac0e88c267befb59ad91a19e36e1bc5578ddb1/parse-1.20.2-py2.py3-none-any.whl", hash = "sha256:967095588cb802add9177d0c0b6133b5ba33b1ea9007ca800e526f42a85af558", size = 20126, upload-time = "2024-06-11T04:41:55.057Z" }, +] + +[[package]] +name = "pathable" +version = "0.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/67/93/8f2c2075b180c12c1e9f6a09d1a985bc2036906b13dff1d8917e395f2048/pathable-0.4.4.tar.gz", hash = "sha256:6905a3cd17804edfac7875b5f6c9142a218c7caef78693c2dbbbfbac186d88b2", size = 8124, upload-time = "2025-01-10T18:43:13.247Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/eb/b6260b31b1a96386c0a880edebe26f89669098acea8e0318bff6adb378fd/pathable-0.4.4-py3-none-any.whl", hash = "sha256:5ae9e94793b6ef5a4cbe0a7ce9dbbefc1eec38df253763fd0aeeacf2762dbbc2", size = 9592, upload-time = "2025-01-10T18:43:11.88Z" }, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, +] + +[[package]] +name = "pathvalidate" +version = "3.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/2a/52a8da6fe965dea6192eb716b357558e103aea0a1e9a8352ad575a8406ca/pathvalidate-3.3.1.tar.gz", hash = "sha256:b18c07212bfead624345bb8e1d6141cdcf15a39736994ea0b94035ad2b1ba177", size = 63262, upload-time = "2025-06-15T09:07:20.736Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/70/875f4a23bfc4731703a5835487d0d2fb999031bd415e7d17c0ae615c18b7/pathvalidate-3.3.1-py3-none-any.whl", hash = "sha256:5263baab691f8e1af96092fa5137ee17df5bdfbd6cff1fcac4d6ef4bc2e1735f", size = 24305, upload-time = "2025-06-15T09:07:19.117Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/61/33/9611380c2bdb1225fdef633e2a9610622310fed35ab11dac9620972ee088/platformdirs-4.5.0.tar.gz", hash = "sha256:70ddccdd7c99fc5942e9fc25636a8b34d04c24b335100223152c2803e4063312", size = 21632, upload-time = "2025-10-08T17:44:48.791Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/cb/ac7874b3e5d58441674fb70742e6c374b28b0c7cb988d37d991cde47166c/platformdirs-4.5.0-py3-none-any.whl", hash = "sha256:e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3", size = 18651, upload-time = "2025-10-08T17:44:47.223Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "py-key-value-aio" +version = "0.2.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "beartype" }, + { name = "py-key-value-shared" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ca/35/65310a4818acec0f87a46e5565e341c5a96fc062a9a03495ad28828ff4d7/py_key_value_aio-0.2.8.tar.gz", hash = "sha256:c0cfbb0bd4e962a3fa1a9fa6db9ba9df812899bd9312fa6368aaea7b26008b36", size = 32853, upload-time = "2025-10-24T13:31:04.688Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cd/5a/e56747d87a97ad2aff0f3700d77f186f0704c90c2da03bfed9e113dae284/py_key_value_aio-0.2.8-py3-none-any.whl", hash = "sha256:561565547ce8162128fd2bd0b9d70ce04a5f4586da8500cce79a54dfac78c46a", size = 69200, upload-time = "2025-10-24T13:31:03.81Z" }, +] + +[package.optional-dependencies] +disk = [ + { name = "diskcache" }, + { name = "pathvalidate" }, +] +keyring = [ + { name = "keyring" }, +] +memory = [ + { name = "cachetools" }, +] + +[[package]] +name = "py-key-value-shared" +version = "0.2.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "beartype" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/26/79/05a1f9280cfa0709479319cbfd2b1c5beb23d5034624f548c83fb65b0b61/py_key_value_shared-0.2.8.tar.gz", hash = "sha256:703b4d3c61af124f0d528ba85995c3c8d78f8bd3d2b217377bd3278598070cc1", size = 8216, upload-time = "2025-10-24T13:31:03.601Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/7a/1726ceaa3343874f322dd83c9ec376ad81f533df8422b8b1e1233a59f8ce/py_key_value_shared-0.2.8-py3-none-any.whl", hash = "sha256:aff1bbfd46d065b2d67897d298642e80e5349eae588c6d11b48452b46b8d46ba", size = 14586, upload-time = "2025-10-24T13:31:02.838Z" }, +] + +[[package]] +name = "pycparser" +version = "2.23" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" }, +] + +[[package]] +name = "pydantic" +version = "2.12.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/1e/4f0a3233767010308f2fd6bd0814597e3f63f1dc98304a9112b8759df4ff/pydantic-2.12.3.tar.gz", hash = "sha256:1da1c82b0fc140bb0103bc1441ffe062154c8d38491189751ee00fd8ca65ce74", size = 819383, upload-time = "2025-10-17T15:04:21.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a1/6b/83661fa77dcefa195ad5f8cd9af3d1a7450fd57cc883ad04d65446ac2029/pydantic-2.12.3-py3-none-any.whl", hash = "sha256:6986454a854bc3bc6e5443e1369e06a3a456af9d339eda45510f517d9ea5c6bf", size = 462431, upload-time = "2025-10-17T15:04:19.346Z" }, +] + +[package.optional-dependencies] +email = [ + { name = "email-validator" }, +] + +[[package]] +name = "pydantic-core" +version = "2.41.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/18/d0944e8eaaa3efd0a91b0f1fc537d3be55ad35091b6a87638211ba691964/pydantic_core-2.41.4.tar.gz", hash = "sha256:70e47929a9d4a1905a67e4b687d5946026390568a8e952b92824118063cee4d5", size = 457557, upload-time = "2025-10-14T10:23:47.909Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/4c/f6cbfa1e8efacd00b846764e8484fe173d25b8dab881e277a619177f3384/pydantic_core-2.41.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:28ff11666443a1a8cf2a044d6a545ebffa8382b5f7973f22c36109205e65dc80", size = 2109062, upload-time = "2025-10-14T10:20:04.486Z" }, + { url = "https://files.pythonhosted.org/packages/21/f8/40b72d3868896bfcd410e1bd7e516e762d326201c48e5b4a06446f6cf9e8/pydantic_core-2.41.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:61760c3925d4633290292bad462e0f737b840508b4f722247d8729684f6539ae", size = 1916301, upload-time = "2025-10-14T10:20:06.857Z" }, + { url = "https://files.pythonhosted.org/packages/94/4d/d203dce8bee7faeca791671c88519969d98d3b4e8f225da5b96dad226fc8/pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eae547b7315d055b0de2ec3965643b0ab82ad0106a7ffd29615ee9f266a02827", size = 1968728, upload-time = "2025-10-14T10:20:08.353Z" }, + { url = "https://files.pythonhosted.org/packages/65/f5/6a66187775df87c24d526985b3a5d78d861580ca466fbd9d4d0e792fcf6c/pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ef9ee5471edd58d1fcce1c80ffc8783a650e3e3a193fe90d52e43bb4d87bff1f", size = 2050238, upload-time = "2025-10-14T10:20:09.766Z" }, + { url = "https://files.pythonhosted.org/packages/5e/b9/78336345de97298cf53236b2f271912ce11f32c1e59de25a374ce12f9cce/pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:15dd504af121caaf2c95cb90c0ebf71603c53de98305621b94da0f967e572def", size = 2249424, upload-time = "2025-10-14T10:20:11.732Z" }, + { url = "https://files.pythonhosted.org/packages/99/bb/a4584888b70ee594c3d374a71af5075a68654d6c780369df269118af7402/pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a926768ea49a8af4d36abd6a8968b8790f7f76dd7cbd5a4c180db2b4ac9a3a2", size = 2366047, upload-time = "2025-10-14T10:20:13.647Z" }, + { url = "https://files.pythonhosted.org/packages/5f/8d/17fc5de9d6418e4d2ae8c675f905cdafdc59d3bf3bf9c946b7ab796a992a/pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6916b9b7d134bff5440098a4deb80e4cb623e68974a87883299de9124126c2a8", size = 2071163, upload-time = "2025-10-14T10:20:15.307Z" }, + { url = "https://files.pythonhosted.org/packages/54/e7/03d2c5c0b8ed37a4617430db68ec5e7dbba66358b629cd69e11b4d564367/pydantic_core-2.41.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5cf90535979089df02e6f17ffd076f07237efa55b7343d98760bde8743c4b265", size = 2190585, upload-time = "2025-10-14T10:20:17.3Z" }, + { url = "https://files.pythonhosted.org/packages/be/fc/15d1c9fe5ad9266a5897d9b932b7f53d7e5cfc800573917a2c5d6eea56ec/pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7533c76fa647fade2d7ec75ac5cc079ab3f34879626dae5689b27790a6cf5a5c", size = 2150109, upload-time = "2025-10-14T10:20:19.143Z" }, + { url = "https://files.pythonhosted.org/packages/26/ef/e735dd008808226c83ba56972566138665b71477ad580fa5a21f0851df48/pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:37e516bca9264cbf29612539801ca3cd5d1be465f940417b002905e6ed79d38a", size = 2315078, upload-time = "2025-10-14T10:20:20.742Z" }, + { url = "https://files.pythonhosted.org/packages/90/00/806efdcf35ff2ac0f938362350cd9827b8afb116cc814b6b75cf23738c7c/pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0c19cb355224037c83642429b8ce261ae108e1c5fbf5c028bac63c77b0f8646e", size = 2318737, upload-time = "2025-10-14T10:20:22.306Z" }, + { url = "https://files.pythonhosted.org/packages/41/7e/6ac90673fe6cb36621a2283552897838c020db343fa86e513d3f563b196f/pydantic_core-2.41.4-cp311-cp311-win32.whl", hash = "sha256:09c2a60e55b357284b5f31f5ab275ba9f7f70b7525e18a132ec1f9160b4f1f03", size = 1974160, upload-time = "2025-10-14T10:20:23.817Z" }, + { url = "https://files.pythonhosted.org/packages/e0/9d/7c5e24ee585c1f8b6356e1d11d40ab807ffde44d2db3b7dfd6d20b09720e/pydantic_core-2.41.4-cp311-cp311-win_amd64.whl", hash = "sha256:711156b6afb5cb1cb7c14a2cc2c4a8b4c717b69046f13c6b332d8a0a8f41ca3e", size = 2021883, upload-time = "2025-10-14T10:20:25.48Z" }, + { url = "https://files.pythonhosted.org/packages/33/90/5c172357460fc28b2871eb4a0fb3843b136b429c6fa827e4b588877bf115/pydantic_core-2.41.4-cp311-cp311-win_arm64.whl", hash = "sha256:6cb9cf7e761f4f8a8589a45e49ed3c0d92d1d696a45a6feaee8c904b26efc2db", size = 1968026, upload-time = "2025-10-14T10:20:27.039Z" }, + { url = "https://files.pythonhosted.org/packages/e9/81/d3b3e95929c4369d30b2a66a91db63c8ed0a98381ae55a45da2cd1cc1288/pydantic_core-2.41.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ab06d77e053d660a6faaf04894446df7b0a7e7aba70c2797465a0a1af00fc887", size = 2099043, upload-time = "2025-10-14T10:20:28.561Z" }, + { url = "https://files.pythonhosted.org/packages/58/da/46fdac49e6717e3a94fc9201403e08d9d61aa7a770fab6190b8740749047/pydantic_core-2.41.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c53ff33e603a9c1179a9364b0a24694f183717b2e0da2b5ad43c316c956901b2", size = 1910699, upload-time = "2025-10-14T10:20:30.217Z" }, + { url = "https://files.pythonhosted.org/packages/1e/63/4d948f1b9dd8e991a5a98b77dd66c74641f5f2e5225fee37994b2e07d391/pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:304c54176af2c143bd181d82e77c15c41cbacea8872a2225dd37e6544dce9999", size = 1952121, upload-time = "2025-10-14T10:20:32.246Z" }, + { url = "https://files.pythonhosted.org/packages/b2/a7/e5fc60a6f781fc634ecaa9ecc3c20171d238794cef69ae0af79ac11b89d7/pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:025ba34a4cf4fb32f917d5d188ab5e702223d3ba603be4d8aca2f82bede432a4", size = 2041590, upload-time = "2025-10-14T10:20:34.332Z" }, + { url = "https://files.pythonhosted.org/packages/70/69/dce747b1d21d59e85af433428978a1893c6f8a7068fa2bb4a927fba7a5ff/pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9f5f30c402ed58f90c70e12eff65547d3ab74685ffe8283c719e6bead8ef53f", size = 2219869, upload-time = "2025-10-14T10:20:35.965Z" }, + { url = "https://files.pythonhosted.org/packages/83/6a/c070e30e295403bf29c4df1cb781317b6a9bac7cd07b8d3acc94d501a63c/pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd96e5d15385d301733113bcaa324c8bcf111275b7675a9c6e88bfb19fc05e3b", size = 2345169, upload-time = "2025-10-14T10:20:37.627Z" }, + { url = "https://files.pythonhosted.org/packages/f0/83/06d001f8043c336baea7fd202a9ac7ad71f87e1c55d8112c50b745c40324/pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98f348cbb44fae6e9653c1055db7e29de67ea6a9ca03a5fa2c2e11a47cff0e47", size = 2070165, upload-time = "2025-10-14T10:20:39.246Z" }, + { url = "https://files.pythonhosted.org/packages/14/0a/e567c2883588dd12bcbc110232d892cf385356f7c8a9910311ac997ab715/pydantic_core-2.41.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec22626a2d14620a83ca583c6f5a4080fa3155282718b6055c2ea48d3ef35970", size = 2189067, upload-time = "2025-10-14T10:20:41.015Z" }, + { url = "https://files.pythonhosted.org/packages/f4/1d/3d9fca34273ba03c9b1c5289f7618bc4bd09c3ad2289b5420481aa051a99/pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3a95d4590b1f1a43bf33ca6d647b990a88f4a3824a8c4572c708f0b45a5290ed", size = 2132997, upload-time = "2025-10-14T10:20:43.106Z" }, + { url = "https://files.pythonhosted.org/packages/52/70/d702ef7a6cd41a8afc61f3554922b3ed8d19dd54c3bd4bdbfe332e610827/pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:f9672ab4d398e1b602feadcffcdd3af44d5f5e6ddc15bc7d15d376d47e8e19f8", size = 2307187, upload-time = "2025-10-14T10:20:44.849Z" }, + { url = "https://files.pythonhosted.org/packages/68/4c/c06be6e27545d08b802127914156f38d10ca287a9e8489342793de8aae3c/pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:84d8854db5f55fead3b579f04bda9a36461dab0730c5d570e1526483e7bb8431", size = 2305204, upload-time = "2025-10-14T10:20:46.781Z" }, + { url = "https://files.pythonhosted.org/packages/b0/e5/35ae4919bcd9f18603419e23c5eaf32750224a89d41a8df1a3704b69f77e/pydantic_core-2.41.4-cp312-cp312-win32.whl", hash = "sha256:9be1c01adb2ecc4e464392c36d17f97e9110fbbc906bcbe1c943b5b87a74aabd", size = 1972536, upload-time = "2025-10-14T10:20:48.39Z" }, + { url = "https://files.pythonhosted.org/packages/1e/c2/49c5bb6d2a49eb2ee3647a93e3dae7080c6409a8a7558b075027644e879c/pydantic_core-2.41.4-cp312-cp312-win_amd64.whl", hash = "sha256:d682cf1d22bab22a5be08539dca3d1593488a99998f9f412137bc323179067ff", size = 2031132, upload-time = "2025-10-14T10:20:50.421Z" }, + { url = "https://files.pythonhosted.org/packages/06/23/936343dbcba6eec93f73e95eb346810fc732f71ba27967b287b66f7b7097/pydantic_core-2.41.4-cp312-cp312-win_arm64.whl", hash = "sha256:833eebfd75a26d17470b58768c1834dfc90141b7afc6eb0429c21fc5a21dcfb8", size = 1969483, upload-time = "2025-10-14T10:20:52.35Z" }, + { url = "https://files.pythonhosted.org/packages/13/d0/c20adabd181a029a970738dfe23710b52a31f1258f591874fcdec7359845/pydantic_core-2.41.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:85e050ad9e5f6fe1004eec65c914332e52f429bc0ae12d6fa2092407a462c746", size = 2105688, upload-time = "2025-10-14T10:20:54.448Z" }, + { url = "https://files.pythonhosted.org/packages/00/b6/0ce5c03cec5ae94cca220dfecddc453c077d71363b98a4bbdb3c0b22c783/pydantic_core-2.41.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7393f1d64792763a48924ba31d1e44c2cfbc05e3b1c2c9abb4ceeadd912cced", size = 1910807, upload-time = "2025-10-14T10:20:56.115Z" }, + { url = "https://files.pythonhosted.org/packages/68/3e/800d3d02c8beb0b5c069c870cbb83799d085debf43499c897bb4b4aaff0d/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94dab0940b0d1fb28bcab847adf887c66a27a40291eedf0b473be58761c9799a", size = 1956669, upload-time = "2025-10-14T10:20:57.874Z" }, + { url = "https://files.pythonhosted.org/packages/60/a4/24271cc71a17f64589be49ab8bd0751f6a0a03046c690df60989f2f95c2c/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:de7c42f897e689ee6f9e93c4bec72b99ae3b32a2ade1c7e4798e690ff5246e02", size = 2051629, upload-time = "2025-10-14T10:21:00.006Z" }, + { url = "https://files.pythonhosted.org/packages/68/de/45af3ca2f175d91b96bfb62e1f2d2f1f9f3b14a734afe0bfeff079f78181/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:664b3199193262277b8b3cd1e754fb07f2c6023289c815a1e1e8fb415cb247b1", size = 2224049, upload-time = "2025-10-14T10:21:01.801Z" }, + { url = "https://files.pythonhosted.org/packages/af/8f/ae4e1ff84672bf869d0a77af24fd78387850e9497753c432875066b5d622/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d95b253b88f7d308b1c0b417c4624f44553ba4762816f94e6986819b9c273fb2", size = 2342409, upload-time = "2025-10-14T10:21:03.556Z" }, + { url = "https://files.pythonhosted.org/packages/18/62/273dd70b0026a085c7b74b000394e1ef95719ea579c76ea2f0cc8893736d/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1351f5bbdbbabc689727cb91649a00cb9ee7203e0a6e54e9f5ba9e22e384b84", size = 2069635, upload-time = "2025-10-14T10:21:05.385Z" }, + { url = "https://files.pythonhosted.org/packages/30/03/cf485fff699b4cdaea469bc481719d3e49f023241b4abb656f8d422189fc/pydantic_core-2.41.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1affa4798520b148d7182da0615d648e752de4ab1a9566b7471bc803d88a062d", size = 2194284, upload-time = "2025-10-14T10:21:07.122Z" }, + { url = "https://files.pythonhosted.org/packages/f9/7e/c8e713db32405dfd97211f2fc0a15d6bf8adb7640f3d18544c1f39526619/pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7b74e18052fea4aa8dea2fb7dbc23d15439695da6cbe6cfc1b694af1115df09d", size = 2137566, upload-time = "2025-10-14T10:21:08.981Z" }, + { url = "https://files.pythonhosted.org/packages/04/f7/db71fd4cdccc8b75990f79ccafbbd66757e19f6d5ee724a6252414483fb4/pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:285b643d75c0e30abda9dc1077395624f314a37e3c09ca402d4015ef5979f1a2", size = 2316809, upload-time = "2025-10-14T10:21:10.805Z" }, + { url = "https://files.pythonhosted.org/packages/76/63/a54973ddb945f1bca56742b48b144d85c9fc22f819ddeb9f861c249d5464/pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:f52679ff4218d713b3b33f88c89ccbf3a5c2c12ba665fb80ccc4192b4608dbab", size = 2311119, upload-time = "2025-10-14T10:21:12.583Z" }, + { url = "https://files.pythonhosted.org/packages/f8/03/5d12891e93c19218af74843a27e32b94922195ded2386f7b55382f904d2f/pydantic_core-2.41.4-cp313-cp313-win32.whl", hash = "sha256:ecde6dedd6fff127c273c76821bb754d793be1024bc33314a120f83a3c69460c", size = 1981398, upload-time = "2025-10-14T10:21:14.584Z" }, + { url = "https://files.pythonhosted.org/packages/be/d8/fd0de71f39db91135b7a26996160de71c073d8635edfce8b3c3681be0d6d/pydantic_core-2.41.4-cp313-cp313-win_amd64.whl", hash = "sha256:d081a1f3800f05409ed868ebb2d74ac39dd0c1ff6c035b5162356d76030736d4", size = 2030735, upload-time = "2025-10-14T10:21:16.432Z" }, + { url = "https://files.pythonhosted.org/packages/72/86/c99921c1cf6650023c08bfab6fe2d7057a5142628ef7ccfa9921f2dda1d5/pydantic_core-2.41.4-cp313-cp313-win_arm64.whl", hash = "sha256:f8e49c9c364a7edcbe2a310f12733aad95b022495ef2a8d653f645e5d20c1564", size = 1973209, upload-time = "2025-10-14T10:21:18.213Z" }, + { url = "https://files.pythonhosted.org/packages/36/0d/b5706cacb70a8414396efdda3d72ae0542e050b591119e458e2490baf035/pydantic_core-2.41.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ed97fd56a561f5eb5706cebe94f1ad7c13b84d98312a05546f2ad036bafe87f4", size = 1877324, upload-time = "2025-10-14T10:21:20.363Z" }, + { url = "https://files.pythonhosted.org/packages/de/2d/cba1fa02cfdea72dfb3a9babb067c83b9dff0bbcb198368e000a6b756ea7/pydantic_core-2.41.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a870c307bf1ee91fc58a9a61338ff780d01bfae45922624816878dce784095d2", size = 1884515, upload-time = "2025-10-14T10:21:22.339Z" }, + { url = "https://files.pythonhosted.org/packages/07/ea/3df927c4384ed9b503c9cc2d076cf983b4f2adb0c754578dfb1245c51e46/pydantic_core-2.41.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d25e97bc1f5f8f7985bdc2335ef9e73843bb561eb1fa6831fdfc295c1c2061cf", size = 2042819, upload-time = "2025-10-14T10:21:26.683Z" }, + { url = "https://files.pythonhosted.org/packages/6a/ee/df8e871f07074250270a3b1b82aad4cd0026b588acd5d7d3eb2fcb1471a3/pydantic_core-2.41.4-cp313-cp313t-win_amd64.whl", hash = "sha256:d405d14bea042f166512add3091c1af40437c2e7f86988f3915fabd27b1e9cd2", size = 1995866, upload-time = "2025-10-14T10:21:28.951Z" }, + { url = "https://files.pythonhosted.org/packages/fc/de/b20f4ab954d6d399499c33ec4fafc46d9551e11dc1858fb7f5dca0748ceb/pydantic_core-2.41.4-cp313-cp313t-win_arm64.whl", hash = "sha256:19f3684868309db5263a11bace3c45d93f6f24afa2ffe75a647583df22a2ff89", size = 1970034, upload-time = "2025-10-14T10:21:30.869Z" }, + { url = "https://files.pythonhosted.org/packages/54/28/d3325da57d413b9819365546eb9a6e8b7cbd9373d9380efd5f74326143e6/pydantic_core-2.41.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:e9205d97ed08a82ebb9a307e92914bb30e18cdf6f6b12ca4bedadb1588a0bfe1", size = 2102022, upload-time = "2025-10-14T10:21:32.809Z" }, + { url = "https://files.pythonhosted.org/packages/9e/24/b58a1bc0d834bf1acc4361e61233ee217169a42efbdc15a60296e13ce438/pydantic_core-2.41.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:82df1f432b37d832709fbcc0e24394bba04a01b6ecf1ee87578145c19cde12ac", size = 1905495, upload-time = "2025-10-14T10:21:34.812Z" }, + { url = "https://files.pythonhosted.org/packages/fb/a4/71f759cc41b7043e8ecdaab81b985a9b6cad7cec077e0b92cff8b71ecf6b/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc3b4cc4539e055cfa39a3763c939f9d409eb40e85813257dcd761985a108554", size = 1956131, upload-time = "2025-10-14T10:21:36.924Z" }, + { url = "https://files.pythonhosted.org/packages/b0/64/1e79ac7aa51f1eec7c4cda8cbe456d5d09f05fdd68b32776d72168d54275/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b1eb1754fce47c63d2ff57fdb88c351a6c0150995890088b33767a10218eaa4e", size = 2052236, upload-time = "2025-10-14T10:21:38.927Z" }, + { url = "https://files.pythonhosted.org/packages/e9/e3/a3ffc363bd4287b80f1d43dc1c28ba64831f8dfc237d6fec8f2661138d48/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6ab5ab30ef325b443f379ddb575a34969c333004fca5a1daa0133a6ffaad616", size = 2223573, upload-time = "2025-10-14T10:21:41.574Z" }, + { url = "https://files.pythonhosted.org/packages/28/27/78814089b4d2e684a9088ede3790763c64693c3d1408ddc0a248bc789126/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:31a41030b1d9ca497634092b46481b937ff9397a86f9f51bd41c4767b6fc04af", size = 2342467, upload-time = "2025-10-14T10:21:44.018Z" }, + { url = "https://files.pythonhosted.org/packages/92/97/4de0e2a1159cb85ad737e03306717637842c88c7fd6d97973172fb183149/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a44ac1738591472c3d020f61c6df1e4015180d6262ebd39bf2aeb52571b60f12", size = 2063754, upload-time = "2025-10-14T10:21:46.466Z" }, + { url = "https://files.pythonhosted.org/packages/0f/50/8cb90ce4b9efcf7ae78130afeb99fd1c86125ccdf9906ef64b9d42f37c25/pydantic_core-2.41.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d72f2b5e6e82ab8f94ea7d0d42f83c487dc159c5240d8f83beae684472864e2d", size = 2196754, upload-time = "2025-10-14T10:21:48.486Z" }, + { url = "https://files.pythonhosted.org/packages/34/3b/ccdc77af9cd5082723574a1cc1bcae7a6acacc829d7c0a06201f7886a109/pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:c4d1e854aaf044487d31143f541f7aafe7b482ae72a022c664b2de2e466ed0ad", size = 2137115, upload-time = "2025-10-14T10:21:50.63Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ba/e7c7a02651a8f7c52dc2cff2b64a30c313e3b57c7d93703cecea76c09b71/pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b568af94267729d76e6ee5ececda4e283d07bbb28e8148bb17adad93d025d25a", size = 2317400, upload-time = "2025-10-14T10:21:52.959Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ba/6c533a4ee8aec6b812c643c49bb3bd88d3f01e3cebe451bb85512d37f00f/pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:6d55fb8b1e8929b341cc313a81a26e0d48aa3b519c1dbaadec3a6a2b4fcad025", size = 2312070, upload-time = "2025-10-14T10:21:55.419Z" }, + { url = "https://files.pythonhosted.org/packages/22/ae/f10524fcc0ab8d7f96cf9a74c880243576fd3e72bd8ce4f81e43d22bcab7/pydantic_core-2.41.4-cp314-cp314-win32.whl", hash = "sha256:5b66584e549e2e32a1398df11da2e0a7eff45d5c2d9db9d5667c5e6ac764d77e", size = 1982277, upload-time = "2025-10-14T10:21:57.474Z" }, + { url = "https://files.pythonhosted.org/packages/b4/dc/e5aa27aea1ad4638f0c3fb41132f7eb583bd7420ee63204e2d4333a3bbf9/pydantic_core-2.41.4-cp314-cp314-win_amd64.whl", hash = "sha256:557a0aab88664cc552285316809cab897716a372afaf8efdbef756f8b890e894", size = 2024608, upload-time = "2025-10-14T10:21:59.557Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/51d89cc2612bd147198e120a13f150afbf0bcb4615cddb049ab10b81b79e/pydantic_core-2.41.4-cp314-cp314-win_arm64.whl", hash = "sha256:3f1ea6f48a045745d0d9f325989d8abd3f1eaf47dd00485912d1a3a63c623a8d", size = 1967614, upload-time = "2025-10-14T10:22:01.847Z" }, + { url = "https://files.pythonhosted.org/packages/0d/c2/472f2e31b95eff099961fa050c376ab7156a81da194f9edb9f710f68787b/pydantic_core-2.41.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6c1fe4c5404c448b13188dd8bd2ebc2bdd7e6727fa61ff481bcc2cca894018da", size = 1876904, upload-time = "2025-10-14T10:22:04.062Z" }, + { url = "https://files.pythonhosted.org/packages/4a/07/ea8eeb91173807ecdae4f4a5f4b150a520085b35454350fc219ba79e66a3/pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:523e7da4d43b113bf8e7b49fa4ec0c35bf4fe66b2230bfc5c13cc498f12c6c3e", size = 1882538, upload-time = "2025-10-14T10:22:06.39Z" }, + { url = "https://files.pythonhosted.org/packages/1e/29/b53a9ca6cd366bfc928823679c6a76c7a4c69f8201c0ba7903ad18ebae2f/pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5729225de81fb65b70fdb1907fcf08c75d498f4a6f15af005aabb1fdadc19dfa", size = 2041183, upload-time = "2025-10-14T10:22:08.812Z" }, + { url = "https://files.pythonhosted.org/packages/c7/3d/f8c1a371ceebcaf94d6dd2d77c6cf4b1c078e13a5837aee83f760b4f7cfd/pydantic_core-2.41.4-cp314-cp314t-win_amd64.whl", hash = "sha256:de2cfbb09e88f0f795fd90cf955858fc2c691df65b1f21f0aa00b99f3fbc661d", size = 1993542, upload-time = "2025-10-14T10:22:11.332Z" }, + { url = "https://files.pythonhosted.org/packages/8a/ac/9fc61b4f9d079482a290afe8d206b8f490e9fd32d4fc03ed4fc698214e01/pydantic_core-2.41.4-cp314-cp314t-win_arm64.whl", hash = "sha256:d34f950ae05a83e0ede899c595f312ca976023ea1db100cd5aa188f7005e3ab0", size = 1973897, upload-time = "2025-10-14T10:22:13.444Z" }, + { url = "https://files.pythonhosted.org/packages/b0/12/5ba58daa7f453454464f92b3ca7b9d7c657d8641c48e370c3ebc9a82dd78/pydantic_core-2.41.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:a1b2cfec3879afb742a7b0bcfa53e4f22ba96571c9e54d6a3afe1052d17d843b", size = 2122139, upload-time = "2025-10-14T10:22:47.288Z" }, + { url = "https://files.pythonhosted.org/packages/21/fb/6860126a77725c3108baecd10fd3d75fec25191d6381b6eb2ac660228eac/pydantic_core-2.41.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:d175600d975b7c244af6eb9c9041f10059f20b8bbffec9e33fdd5ee3f67cdc42", size = 1936674, upload-time = "2025-10-14T10:22:49.555Z" }, + { url = "https://files.pythonhosted.org/packages/de/be/57dcaa3ed595d81f8757e2b44a38240ac5d37628bce25fb20d02c7018776/pydantic_core-2.41.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f184d657fa4947ae5ec9c47bd7e917730fa1cbb78195037e32dcbab50aca5ee", size = 1956398, upload-time = "2025-10-14T10:22:52.19Z" }, + { url = "https://files.pythonhosted.org/packages/2f/1d/679a344fadb9695f1a6a294d739fbd21d71fa023286daeea8c0ed49e7c2b/pydantic_core-2.41.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ed810568aeffed3edc78910af32af911c835cc39ebbfacd1f0ab5dd53028e5c", size = 2138674, upload-time = "2025-10-14T10:22:54.499Z" }, + { url = "https://files.pythonhosted.org/packages/c4/48/ae937e5a831b7c0dc646b2ef788c27cd003894882415300ed21927c21efa/pydantic_core-2.41.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:4f5d640aeebb438517150fdeec097739614421900e4a08db4a3ef38898798537", size = 2112087, upload-time = "2025-10-14T10:22:56.818Z" }, + { url = "https://files.pythonhosted.org/packages/5e/db/6db8073e3d32dae017da7e0d16a9ecb897d0a4d92e00634916e486097961/pydantic_core-2.41.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:4a9ab037b71927babc6d9e7fc01aea9e66dc2a4a34dff06ef0724a4049629f94", size = 1920387, upload-time = "2025-10-14T10:22:59.342Z" }, + { url = "https://files.pythonhosted.org/packages/0d/c1/dd3542d072fcc336030d66834872f0328727e3b8de289c662faa04aa270e/pydantic_core-2.41.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4dab9484ec605c3016df9ad4fd4f9a390bc5d816a3b10c6550f8424bb80b18c", size = 1951495, upload-time = "2025-10-14T10:23:02.089Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c6/db8d13a1f8ab3f1eb08c88bd00fd62d44311e3456d1e85c0e59e0a0376e7/pydantic_core-2.41.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8a5028425820731d8c6c098ab642d7b8b999758e24acae03ed38a66eca8335", size = 2139008, upload-time = "2025-10-14T10:23:04.539Z" }, + { url = "https://files.pythonhosted.org/packages/7e/7d/138e902ed6399b866f7cfe4435d22445e16fff888a1c00560d9dc79a780f/pydantic_core-2.41.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:491535d45cd7ad7e4a2af4a5169b0d07bebf1adfd164b0368da8aa41e19907a5", size = 2104721, upload-time = "2025-10-14T10:23:26.906Z" }, + { url = "https://files.pythonhosted.org/packages/47/13/0525623cf94627f7b53b4c2034c81edc8491cbfc7c28d5447fa318791479/pydantic_core-2.41.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:54d86c0cada6aba4ec4c047d0e348cbad7063b87ae0f005d9f8c9ad04d4a92a2", size = 1931608, upload-time = "2025-10-14T10:23:29.306Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f9/744bc98137d6ef0a233f808bfc9b18cf94624bf30836a18d3b05d08bf418/pydantic_core-2.41.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eca1124aced216b2500dc2609eade086d718e8249cb9696660ab447d50a758bd", size = 2132986, upload-time = "2025-10-14T10:23:32.057Z" }, + { url = "https://files.pythonhosted.org/packages/17/c8/629e88920171173f6049386cc71f893dff03209a9ef32b4d2f7e7c264bcf/pydantic_core-2.41.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6c9024169becccf0cb470ada03ee578d7348c119a0d42af3dcf9eda96e3a247c", size = 2187516, upload-time = "2025-10-14T10:23:34.871Z" }, + { url = "https://files.pythonhosted.org/packages/2e/0f/4f2734688d98488782218ca61bcc118329bf5de05bb7fe3adc7dd79b0b86/pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:26895a4268ae5a2849269f4991cdc97236e4b9c010e51137becf25182daac405", size = 2146146, upload-time = "2025-10-14T10:23:37.342Z" }, + { url = "https://files.pythonhosted.org/packages/ed/f2/ab385dbd94a052c62224b99cf99002eee99dbec40e10006c78575aead256/pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:ca4df25762cf71308c446e33c9b1fdca2923a3f13de616e2a949f38bf21ff5a8", size = 2311296, upload-time = "2025-10-14T10:23:40.145Z" }, + { url = "https://files.pythonhosted.org/packages/fc/8e/e4f12afe1beeb9823bba5375f8f258df0cc61b056b0195fb1cf9f62a1a58/pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:5a28fcedd762349519276c36634e71853b4541079cab4acaaac60c4421827308", size = 2315386, upload-time = "2025-10-14T10:23:42.624Z" }, + { url = "https://files.pythonhosted.org/packages/48/f7/925f65d930802e3ea2eb4d5afa4cb8730c8dc0d2cb89a59dc4ed2fcb2d74/pydantic_core-2.41.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c173ddcd86afd2535e2b695217e82191580663a1d1928239f877f5a1649ef39f", size = 2147775, upload-time = "2025-10-14T10:23:45.406Z" }, +] + +[[package]] +name = "pydantic-settings" +version = "2.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/20/c5/dbbc27b814c71676593d1c3f718e6cd7d4f00652cefa24b75f7aa3efb25e/pydantic_settings-2.11.0.tar.gz", hash = "sha256:d0e87a1c7d33593beb7194adb8470fc426e95ba02af83a0f23474a04c9a08180", size = 188394, upload-time = "2025-09-24T14:19:11.764Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/d6/887a1ff844e64aa823fb4905978d882a633cfe295c32eacad582b78a7d8b/pydantic_settings-2.11.0-py3-none-any.whl", hash = "sha256:fe2cea3413b9530d10f3a5875adffb17ada5c1e1bab0b2885546d7310415207c", size = 48608, upload-time = "2025-09-24T14:19:10.015Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + +[[package]] +name = "pyjwt" +version = "2.10.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/46/bd74733ff231675599650d3e47f361794b22ef3e3770998dda30d3b63726/pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", size = 87785, upload-time = "2024-11-28T03:43:29.933Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997, upload-time = "2024-11-28T03:43:27.893Z" }, +] + +[[package]] +name = "pyngrok" +version = "7.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3f/64/5ab436dd78db3bcfdbae5965c48e21a6ee3fa6ec87859e44442e2fb361c3/pyngrok-7.4.1.tar.gz", hash = "sha256:ad8637738ced5bdb88c28b087fea39ca552860c2d30004ac01033c0f8eb4f36e", size = 44612, upload-time = "2025-10-23T14:32:50.524Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/5d/231c395723ee72bae6c272831642304067fda24a4d4f008e18b1088348f4/pyngrok-7.4.1-py3-none-any.whl", hash = "sha256:0325e34f26f7a5a9324df414eebbfaec5e5388f77e7439ea45a3358f645bc840", size = 25464, upload-time = "2025-10-23T14:32:49.147Z" }, +] + +[[package]] +name = "pyperclip" +version = "1.11.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/52/d87eba7cb129b81563019d1679026e7a112ef76855d6159d24754dbd2a51/pyperclip-1.11.0.tar.gz", hash = "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6", size = 12185, upload-time = "2025-09-26T14:40:37.245Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/80/fc9d01d5ed37ba4c42ca2b55b4339ae6e200b456be3a1aaddf4a9fa99b8c/pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273", size = 11063, upload-time = "2025-09-26T14:40:36.069Z" }, +] + +[[package]] +name = "pytest" +version = "8.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, +] + +[[package]] +name = "pytest-asyncio" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/86/9e3c5f48f7b7b638b216e4b9e645f54d199d7abbbab7a64a13b4e12ba10f/pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57", size = 50119, upload-time = "2025-09-12T07:33:53.816Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/93/2fa34714b7a4ae72f2f8dad66ba17dd9a2c793220719e736dda28b7aec27/pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99", size = 15095, upload-time = "2025-09-12T07:33:52.639Z" }, +] + +[[package]] +name = "pytest-cov" +version = "7.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage", extra = ["toml"] }, + { name = "pluggy" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", size = 54328, upload-time = "2025-09-09T10:57:02.113Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" }, +] + +[[package]] +name = "python-dotenv" +version = "1.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" }, +] + +[[package]] +name = "python-multipart" +version = "0.0.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158, upload-time = "2024-12-16T19:45:46.972Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546, upload-time = "2024-12-16T19:45:44.423Z" }, +] + +[[package]] +name = "pytokens" +version = "0.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/c2/dbadcdddb412a267585459142bfd7cc241e6276db69339353ae6e241ab2b/pytokens-0.2.0.tar.gz", hash = "sha256:532d6421364e5869ea57a9523bf385f02586d4662acbcc0342afd69511b4dd43", size = 15368, upload-time = "2025-10-15T08:02:42.738Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/5a/c269ea6b348b6f2c32686635df89f32dbe05df1088dd4579302a6f8f99af/pytokens-0.2.0-py3-none-any.whl", hash = "sha256:74d4b318c67f4295c13782ddd9abcb7e297ec5630ad060eb90abf7ebbefe59f8", size = 12038, upload-time = "2025-10-15T08:02:41.694Z" }, +] + +[[package]] +name = "pywin32" +version = "311" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/af/449a6a91e5d6db51420875c54f6aff7c97a86a3b13a0b4f1a5c13b988de3/pywin32-311-cp311-cp311-win32.whl", hash = "sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151", size = 8697031, upload-time = "2025-07-14T20:13:13.266Z" }, + { url = "https://files.pythonhosted.org/packages/51/8f/9bb81dd5bb77d22243d33c8397f09377056d5c687aa6d4042bea7fbf8364/pywin32-311-cp311-cp311-win_amd64.whl", hash = "sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503", size = 9508308, upload-time = "2025-07-14T20:13:15.147Z" }, + { url = "https://files.pythonhosted.org/packages/44/7b/9c2ab54f74a138c491aba1b1cd0795ba61f144c711daea84a88b63dc0f6c/pywin32-311-cp311-cp311-win_arm64.whl", hash = "sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2", size = 8703930, upload-time = "2025-07-14T20:13:16.945Z" }, + { url = "https://files.pythonhosted.org/packages/e7/ab/01ea1943d4eba0f850c3c61e78e8dd59757ff815ff3ccd0a84de5f541f42/pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31", size = 8706543, upload-time = "2025-07-14T20:13:20.765Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a8/a0e8d07d4d051ec7502cd58b291ec98dcc0c3fff027caad0470b72cfcc2f/pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067", size = 9495040, upload-time = "2025-07-14T20:13:22.543Z" }, + { url = "https://files.pythonhosted.org/packages/ba/3a/2ae996277b4b50f17d61f0603efd8253cb2d79cc7ae159468007b586396d/pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852", size = 8710102, upload-time = "2025-07-14T20:13:24.682Z" }, + { url = "https://files.pythonhosted.org/packages/a5/be/3fd5de0979fcb3994bfee0d65ed8ca9506a8a1260651b86174f6a86f52b3/pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d", size = 8705700, upload-time = "2025-07-14T20:13:26.471Z" }, + { url = "https://files.pythonhosted.org/packages/e3/28/e0a1909523c6890208295a29e05c2adb2126364e289826c0a8bc7297bd5c/pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d", size = 9494700, upload-time = "2025-07-14T20:13:28.243Z" }, + { url = "https://files.pythonhosted.org/packages/04/bf/90339ac0f55726dce7d794e6d79a18a91265bdf3aa70b6b9ca52f35e022a/pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a", size = 8709318, upload-time = "2025-07-14T20:13:30.348Z" }, + { url = "https://files.pythonhosted.org/packages/c9/31/097f2e132c4f16d99a22bfb777e0fd88bd8e1c634304e102f313af69ace5/pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee", size = 8840714, upload-time = "2025-07-14T20:13:32.449Z" }, + { url = "https://files.pythonhosted.org/packages/90/4b/07c77d8ba0e01349358082713400435347df8426208171ce297da32c313d/pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87", size = 9656800, upload-time = "2025-07-14T20:13:34.312Z" }, + { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" }, +] + +[[package]] +name = "pywin32-ctypes" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, + { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, + { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, +] + +[[package]] +name = "referencing" +version = "0.36.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744, upload-time = "2025-01-25T08:48:16.138Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775, upload-time = "2025-01-25T08:48:14.241Z" }, +] + +[[package]] +name = "requests" +version = "2.32.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, +] + +[[package]] +name = "rfc3339-validator" +version = "0.1.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size = 5513, upload-time = "2021-05-12T16:37:54.178Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa", size = 3490, upload-time = "2021-05-12T16:37:52.536Z" }, +] + +[[package]] +name = "rich" +version = "14.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990, upload-time = "2025-10-09T14:16:53.064Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393, upload-time = "2025-10-09T14:16:51.245Z" }, +] + +[[package]] +name = "rich-rst" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docutils" }, + { name = "rich" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bc/6d/a506aaa4a9eaa945ed8ab2b7347859f53593864289853c5d6d62b77246e0/rich_rst-1.3.2.tar.gz", hash = "sha256:a1196fdddf1e364b02ec68a05e8ff8f6914fee10fbca2e6b6735f166bb0da8d4", size = 14936, upload-time = "2025-10-14T16:49:45.332Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/2f/b4530fbf948867702d0a3f27de4a6aab1d156f406d72852ab902c4d04de9/rich_rst-1.3.2-py3-none-any.whl", hash = "sha256:a99b4907cbe118cf9d18b0b44de272efa61f15117c61e39ebdc431baf5df722a", size = 12567, upload-time = "2025-10-14T16:49:42.953Z" }, +] + +[[package]] +name = "rpds-py" +version = "0.28.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/48/dc/95f074d43452b3ef5d06276696ece4b3b5d696e7c9ad7173c54b1390cd70/rpds_py-0.28.0.tar.gz", hash = "sha256:abd4df20485a0983e2ca334a216249b6186d6e3c1627e106651943dbdb791aea", size = 27419, upload-time = "2025-10-22T22:24:29.327Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/34/058d0db5471c6be7bef82487ad5021ff8d1d1d27794be8730aad938649cf/rpds_py-0.28.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:03065002fd2e287725d95fbc69688e0c6daf6c6314ba38bdbaa3895418e09296", size = 362344, upload-time = "2025-10-22T22:21:39.713Z" }, + { url = "https://files.pythonhosted.org/packages/5d/67/9503f0ec8c055a0782880f300c50a2b8e5e72eb1f94dfc2053da527444dd/rpds_py-0.28.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:28ea02215f262b6d078daec0b45344c89e161eab9526b0d898221d96fdda5f27", size = 348440, upload-time = "2025-10-22T22:21:41.056Z" }, + { url = "https://files.pythonhosted.org/packages/68/2e/94223ee9b32332a41d75b6f94b37b4ce3e93878a556fc5f152cbd856a81f/rpds_py-0.28.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25dbade8fbf30bcc551cb352376c0ad64b067e4fc56f90e22ba70c3ce205988c", size = 379068, upload-time = "2025-10-22T22:21:42.593Z" }, + { url = "https://files.pythonhosted.org/packages/b4/25/54fd48f9f680cfc44e6a7f39a5fadf1d4a4a1fd0848076af4a43e79f998c/rpds_py-0.28.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c03002f54cc855860bfdc3442928ffdca9081e73b5b382ed0b9e8efe6e5e205", size = 390518, upload-time = "2025-10-22T22:21:43.998Z" }, + { url = "https://files.pythonhosted.org/packages/1b/85/ac258c9c27f2ccb1bd5d0697e53a82ebcf8088e3186d5d2bf8498ee7ed44/rpds_py-0.28.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9699fa7990368b22032baf2b2dce1f634388e4ffc03dfefaaac79f4695edc95", size = 525319, upload-time = "2025-10-22T22:21:45.645Z" }, + { url = "https://files.pythonhosted.org/packages/40/cb/c6734774789566d46775f193964b76627cd5f42ecf246d257ce84d1912ed/rpds_py-0.28.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9b06fe1a75e05e0713f06ea0c89ecb6452210fd60e2f1b6ddc1067b990e08d9", size = 404896, upload-time = "2025-10-22T22:21:47.544Z" }, + { url = "https://files.pythonhosted.org/packages/1f/53/14e37ce83202c632c89b0691185dca9532288ff9d390eacae3d2ff771bae/rpds_py-0.28.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac9f83e7b326a3f9ec3ef84cda98fb0a74c7159f33e692032233046e7fd15da2", size = 382862, upload-time = "2025-10-22T22:21:49.176Z" }, + { url = "https://files.pythonhosted.org/packages/6a/83/f3642483ca971a54d60caa4449f9d6d4dbb56a53e0072d0deff51b38af74/rpds_py-0.28.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:0d3259ea9ad8743a75a43eb7819324cdab393263c91be86e2d1901ee65c314e0", size = 398848, upload-time = "2025-10-22T22:21:51.024Z" }, + { url = "https://files.pythonhosted.org/packages/44/09/2d9c8b2f88e399b4cfe86efdf2935feaf0394e4f14ab30c6c5945d60af7d/rpds_py-0.28.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a7548b345f66f6695943b4ef6afe33ccd3f1b638bd9afd0f730dd255c249c9e", size = 412030, upload-time = "2025-10-22T22:21:52.665Z" }, + { url = "https://files.pythonhosted.org/packages/dd/f5/e1cec473d4bde6df1fd3738be8e82d64dd0600868e76e92dfeaebbc2d18f/rpds_py-0.28.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c9a40040aa388b037eb39416710fbcce9443498d2eaab0b9b45ae988b53f5c67", size = 559700, upload-time = "2025-10-22T22:21:54.123Z" }, + { url = "https://files.pythonhosted.org/packages/8d/be/73bb241c1649edbf14e98e9e78899c2c5e52bbe47cb64811f44d2cc11808/rpds_py-0.28.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8f60c7ea34e78c199acd0d3cda37a99be2c861dd2b8cf67399784f70c9f8e57d", size = 584581, upload-time = "2025-10-22T22:21:56.102Z" }, + { url = "https://files.pythonhosted.org/packages/9c/9c/ffc6e9218cd1eb5c2c7dbd276c87cd10e8c2232c456b554169eb363381df/rpds_py-0.28.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1571ae4292649100d743b26d5f9c63503bb1fedf538a8f29a98dce2d5ba6b4e6", size = 549981, upload-time = "2025-10-22T22:21:58.253Z" }, + { url = "https://files.pythonhosted.org/packages/5f/50/da8b6d33803a94df0149345ee33e5d91ed4d25fc6517de6a25587eae4133/rpds_py-0.28.0-cp311-cp311-win32.whl", hash = "sha256:5cfa9af45e7c1140af7321fa0bef25b386ee9faa8928c80dc3a5360971a29e8c", size = 214729, upload-time = "2025-10-22T22:21:59.625Z" }, + { url = "https://files.pythonhosted.org/packages/12/fd/b0f48c4c320ee24c8c20df8b44acffb7353991ddf688af01eef5f93d7018/rpds_py-0.28.0-cp311-cp311-win_amd64.whl", hash = "sha256:dd8d86b5d29d1b74100982424ba53e56033dc47720a6de9ba0259cf81d7cecaa", size = 223977, upload-time = "2025-10-22T22:22:01.092Z" }, + { url = "https://files.pythonhosted.org/packages/b4/21/c8e77a2ac66e2ec4e21f18a04b4e9a0417ecf8e61b5eaeaa9360a91713b4/rpds_py-0.28.0-cp311-cp311-win_arm64.whl", hash = "sha256:4e27d3a5709cc2b3e013bf93679a849213c79ae0573f9b894b284b55e729e120", size = 217326, upload-time = "2025-10-22T22:22:02.944Z" }, + { url = "https://files.pythonhosted.org/packages/b8/5c/6c3936495003875fe7b14f90ea812841a08fca50ab26bd840e924097d9c8/rpds_py-0.28.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6b4f28583a4f247ff60cd7bdda83db8c3f5b05a7a82ff20dd4b078571747708f", size = 366439, upload-time = "2025-10-22T22:22:04.525Z" }, + { url = "https://files.pythonhosted.org/packages/56/f9/a0f1ca194c50aa29895b442771f036a25b6c41a35e4f35b1a0ea713bedae/rpds_py-0.28.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d678e91b610c29c4b3d52a2c148b641df2b4676ffe47c59f6388d58b99cdc424", size = 348170, upload-time = "2025-10-22T22:22:06.397Z" }, + { url = "https://files.pythonhosted.org/packages/18/ea/42d243d3a586beb72c77fa5def0487daf827210069a95f36328e869599ea/rpds_py-0.28.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e819e0e37a44a78e1383bf1970076e2ccc4dc8c2bbaa2f9bd1dc987e9afff628", size = 378838, upload-time = "2025-10-22T22:22:07.932Z" }, + { url = "https://files.pythonhosted.org/packages/e7/78/3de32e18a94791af8f33601402d9d4f39613136398658412a4e0b3047327/rpds_py-0.28.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5ee514e0f0523db5d3fb171f397c54875dbbd69760a414dccf9d4d7ad628b5bd", size = 393299, upload-time = "2025-10-22T22:22:09.435Z" }, + { url = "https://files.pythonhosted.org/packages/13/7e/4bdb435afb18acea2eb8a25ad56b956f28de7c59f8a1d32827effa0d4514/rpds_py-0.28.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5f3fa06d27fdcee47f07a39e02862da0100cb4982508f5ead53ec533cd5fe55e", size = 518000, upload-time = "2025-10-22T22:22:11.326Z" }, + { url = "https://files.pythonhosted.org/packages/31/d0/5f52a656875cdc60498ab035a7a0ac8f399890cc1ee73ebd567bac4e39ae/rpds_py-0.28.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:46959ef2e64f9e4a41fc89aa20dbca2b85531f9a72c21099a3360f35d10b0d5a", size = 408746, upload-time = "2025-10-22T22:22:13.143Z" }, + { url = "https://files.pythonhosted.org/packages/3e/cd/49ce51767b879cde77e7ad9fae164ea15dce3616fe591d9ea1df51152706/rpds_py-0.28.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8455933b4bcd6e83fde3fefc987a023389c4b13f9a58c8d23e4b3f6d13f78c84", size = 386379, upload-time = "2025-10-22T22:22:14.602Z" }, + { url = "https://files.pythonhosted.org/packages/6a/99/e4e1e1ee93a98f72fc450e36c0e4d99c35370220e815288e3ecd2ec36a2a/rpds_py-0.28.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:ad50614a02c8c2962feebe6012b52f9802deec4263946cddea37aaf28dd25a66", size = 401280, upload-time = "2025-10-22T22:22:16.063Z" }, + { url = "https://files.pythonhosted.org/packages/61/35/e0c6a57488392a8b319d2200d03dad2b29c0db9996f5662c3b02d0b86c02/rpds_py-0.28.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e5deca01b271492553fdb6c7fd974659dce736a15bae5dad7ab8b93555bceb28", size = 412365, upload-time = "2025-10-22T22:22:17.504Z" }, + { url = "https://files.pythonhosted.org/packages/ff/6a/841337980ea253ec797eb084665436007a1aad0faac1ba097fb906c5f69c/rpds_py-0.28.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:735f8495a13159ce6a0d533f01e8674cec0c57038c920495f87dcb20b3ddb48a", size = 559573, upload-time = "2025-10-22T22:22:19.108Z" }, + { url = "https://files.pythonhosted.org/packages/e7/5e/64826ec58afd4c489731f8b00729c5f6afdb86f1df1df60bfede55d650bb/rpds_py-0.28.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:961ca621ff10d198bbe6ba4957decca61aa2a0c56695384c1d6b79bf61436df5", size = 583973, upload-time = "2025-10-22T22:22:20.768Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ee/44d024b4843f8386a4eeaa4c171b3d31d55f7177c415545fd1a24c249b5d/rpds_py-0.28.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2374e16cc9131022e7d9a8f8d65d261d9ba55048c78f3b6e017971a4f5e6353c", size = 553800, upload-time = "2025-10-22T22:22:22.25Z" }, + { url = "https://files.pythonhosted.org/packages/7d/89/33e675dccff11a06d4d85dbb4d1865f878d5020cbb69b2c1e7b2d3f82562/rpds_py-0.28.0-cp312-cp312-win32.whl", hash = "sha256:d15431e334fba488b081d47f30f091e5d03c18527c325386091f31718952fe08", size = 216954, upload-time = "2025-10-22T22:22:24.105Z" }, + { url = "https://files.pythonhosted.org/packages/af/36/45f6ebb3210887e8ee6dbf1bc710ae8400bb417ce165aaf3024b8360d999/rpds_py-0.28.0-cp312-cp312-win_amd64.whl", hash = "sha256:a410542d61fc54710f750d3764380b53bf09e8c4edbf2f9141a82aa774a04f7c", size = 227844, upload-time = "2025-10-22T22:22:25.551Z" }, + { url = "https://files.pythonhosted.org/packages/57/91/f3fb250d7e73de71080f9a221d19bd6a1c1eb0d12a1ea26513f6c1052ad6/rpds_py-0.28.0-cp312-cp312-win_arm64.whl", hash = "sha256:1f0cfd1c69e2d14f8c892b893997fa9a60d890a0c8a603e88dca4955f26d1edd", size = 217624, upload-time = "2025-10-22T22:22:26.914Z" }, + { url = "https://files.pythonhosted.org/packages/d3/03/ce566d92611dfac0085c2f4b048cd53ed7c274a5c05974b882a908d540a2/rpds_py-0.28.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e9e184408a0297086f880556b6168fa927d677716f83d3472ea333b42171ee3b", size = 366235, upload-time = "2025-10-22T22:22:28.397Z" }, + { url = "https://files.pythonhosted.org/packages/00/34/1c61da1b25592b86fd285bd7bd8422f4c9d748a7373b46126f9ae792a004/rpds_py-0.28.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:edd267266a9b0448f33dc465a97cfc5d467594b600fe28e7fa2f36450e03053a", size = 348241, upload-time = "2025-10-22T22:22:30.171Z" }, + { url = "https://files.pythonhosted.org/packages/fc/00/ed1e28616848c61c493a067779633ebf4b569eccaacf9ccbdc0e7cba2b9d/rpds_py-0.28.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85beb8b3f45e4e32f6802fb6cd6b17f615ef6c6a52f265371fb916fae02814aa", size = 378079, upload-time = "2025-10-22T22:22:31.644Z" }, + { url = "https://files.pythonhosted.org/packages/11/b2/ccb30333a16a470091b6e50289adb4d3ec656fd9951ba8c5e3aaa0746a67/rpds_py-0.28.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d2412be8d00a1b895f8ad827cc2116455196e20ed994bb704bf138fe91a42724", size = 393151, upload-time = "2025-10-22T22:22:33.453Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d0/73e2217c3ee486d555cb84920597480627d8c0240ff3062005c6cc47773e/rpds_py-0.28.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cf128350d384b777da0e68796afdcebc2e9f63f0e9f242217754e647f6d32491", size = 517520, upload-time = "2025-10-22T22:22:34.949Z" }, + { url = "https://files.pythonhosted.org/packages/c4/91/23efe81c700427d0841a4ae7ea23e305654381831e6029499fe80be8a071/rpds_py-0.28.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a2036d09b363aa36695d1cc1a97b36865597f4478470b0697b5ee9403f4fe399", size = 408699, upload-time = "2025-10-22T22:22:36.584Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ee/a324d3198da151820a326c1f988caaa4f37fc27955148a76fff7a2d787a9/rpds_py-0.28.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8e1e9be4fa6305a16be628959188e4fd5cd6f1b0e724d63c6d8b2a8adf74ea6", size = 385720, upload-time = "2025-10-22T22:22:38.014Z" }, + { url = "https://files.pythonhosted.org/packages/19/ad/e68120dc05af8b7cab4a789fccd8cdcf0fe7e6581461038cc5c164cd97d2/rpds_py-0.28.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0a403460c9dd91a7f23fc3188de6d8977f1d9603a351d5db6cf20aaea95b538d", size = 401096, upload-time = "2025-10-22T22:22:39.869Z" }, + { url = "https://files.pythonhosted.org/packages/99/90/c1e070620042459d60df6356b666bb1f62198a89d68881816a7ed121595a/rpds_py-0.28.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d7366b6553cdc805abcc512b849a519167db8f5e5c3472010cd1228b224265cb", size = 411465, upload-time = "2025-10-22T22:22:41.395Z" }, + { url = "https://files.pythonhosted.org/packages/68/61/7c195b30d57f1b8d5970f600efee72a4fad79ec829057972e13a0370fd24/rpds_py-0.28.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5b43c6a3726efd50f18d8120ec0551241c38785b68952d240c45ea553912ac41", size = 558832, upload-time = "2025-10-22T22:22:42.871Z" }, + { url = "https://files.pythonhosted.org/packages/b0/3d/06f3a718864773f69941d4deccdf18e5e47dd298b4628062f004c10f3b34/rpds_py-0.28.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0cb7203c7bc69d7c1585ebb33a2e6074492d2fc21ad28a7b9d40457ac2a51ab7", size = 583230, upload-time = "2025-10-22T22:22:44.877Z" }, + { url = "https://files.pythonhosted.org/packages/66/df/62fc783781a121e77fee9a21ead0a926f1b652280a33f5956a5e7833ed30/rpds_py-0.28.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7a52a5169c664dfb495882adc75c304ae1d50df552fbd68e100fdc719dee4ff9", size = 553268, upload-time = "2025-10-22T22:22:46.441Z" }, + { url = "https://files.pythonhosted.org/packages/84/85/d34366e335140a4837902d3dea89b51f087bd6a63c993ebdff59e93ee61d/rpds_py-0.28.0-cp313-cp313-win32.whl", hash = "sha256:2e42456917b6687215b3e606ab46aa6bca040c77af7df9a08a6dcfe8a4d10ca5", size = 217100, upload-time = "2025-10-22T22:22:48.342Z" }, + { url = "https://files.pythonhosted.org/packages/3c/1c/f25a3f3752ad7601476e3eff395fe075e0f7813fbb9862bd67c82440e880/rpds_py-0.28.0-cp313-cp313-win_amd64.whl", hash = "sha256:e0a0311caedc8069d68fc2bf4c9019b58a2d5ce3cd7cb656c845f1615b577e1e", size = 227759, upload-time = "2025-10-22T22:22:50.219Z" }, + { url = "https://files.pythonhosted.org/packages/e0/d6/5f39b42b99615b5bc2f36ab90423ea404830bdfee1c706820943e9a645eb/rpds_py-0.28.0-cp313-cp313-win_arm64.whl", hash = "sha256:04c1b207ab8b581108801528d59ad80aa83bb170b35b0ddffb29c20e411acdc1", size = 217326, upload-time = "2025-10-22T22:22:51.647Z" }, + { url = "https://files.pythonhosted.org/packages/5c/8b/0c69b72d1cee20a63db534be0df271effe715ef6c744fdf1ff23bb2b0b1c/rpds_py-0.28.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:f296ea3054e11fc58ad42e850e8b75c62d9a93a9f981ad04b2e5ae7d2186ff9c", size = 355736, upload-time = "2025-10-22T22:22:53.211Z" }, + { url = "https://files.pythonhosted.org/packages/f7/6d/0c2ee773cfb55c31a8514d2cece856dd299170a49babd50dcffb15ddc749/rpds_py-0.28.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5a7306c19b19005ad98468fcefeb7100b19c79fc23a5f24a12e06d91181193fa", size = 342677, upload-time = "2025-10-22T22:22:54.723Z" }, + { url = "https://files.pythonhosted.org/packages/e2/1c/22513ab25a27ea205144414724743e305e8153e6abe81833b5e678650f5a/rpds_py-0.28.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5d9b86aa501fed9862a443c5c3116f6ead8bc9296185f369277c42542bd646b", size = 371847, upload-time = "2025-10-22T22:22:56.295Z" }, + { url = "https://files.pythonhosted.org/packages/60/07/68e6ccdb4b05115ffe61d31afc94adef1833d3a72f76c9632d4d90d67954/rpds_py-0.28.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e5bbc701eff140ba0e872691d573b3d5d30059ea26e5785acba9132d10c8c31d", size = 381800, upload-time = "2025-10-22T22:22:57.808Z" }, + { url = "https://files.pythonhosted.org/packages/73/bf/6d6d15df80781d7f9f368e7c1a00caf764436518c4877fb28b029c4624af/rpds_py-0.28.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a5690671cd672a45aa8616d7374fdf334a1b9c04a0cac3c854b1136e92374fe", size = 518827, upload-time = "2025-10-22T22:22:59.826Z" }, + { url = "https://files.pythonhosted.org/packages/7b/d3/2decbb2976cc452cbf12a2b0aaac5f1b9dc5dd9d1f7e2509a3ee00421249/rpds_py-0.28.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9f1d92ecea4fa12f978a367c32a5375a1982834649cdb96539dcdc12e609ab1a", size = 399471, upload-time = "2025-10-22T22:23:01.968Z" }, + { url = "https://files.pythonhosted.org/packages/b1/2c/f30892f9e54bd02e5faca3f6a26d6933c51055e67d54818af90abed9748e/rpds_py-0.28.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d252db6b1a78d0a3928b6190156042d54c93660ce4d98290d7b16b5296fb7cc", size = 377578, upload-time = "2025-10-22T22:23:03.52Z" }, + { url = "https://files.pythonhosted.org/packages/f0/5d/3bce97e5534157318f29ac06bf2d279dae2674ec12f7cb9c12739cee64d8/rpds_py-0.28.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:d61b355c3275acb825f8777d6c4505f42b5007e357af500939d4a35b19177259", size = 390482, upload-time = "2025-10-22T22:23:05.391Z" }, + { url = "https://files.pythonhosted.org/packages/e3/f0/886bd515ed457b5bd93b166175edb80a0b21a210c10e993392127f1e3931/rpds_py-0.28.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:acbe5e8b1026c0c580d0321c8aae4b0a1e1676861d48d6e8c6586625055b606a", size = 402447, upload-time = "2025-10-22T22:23:06.93Z" }, + { url = "https://files.pythonhosted.org/packages/42/b5/71e8777ac55e6af1f4f1c05b47542a1eaa6c33c1cf0d300dca6a1c6e159a/rpds_py-0.28.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8aa23b6f0fc59b85b4c7d89ba2965af274346f738e8d9fc2455763602e62fd5f", size = 552385, upload-time = "2025-10-22T22:23:08.557Z" }, + { url = "https://files.pythonhosted.org/packages/5d/cb/6ca2d70cbda5a8e36605e7788c4aa3bea7c17d71d213465a5a675079b98d/rpds_py-0.28.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7b14b0c680286958817c22d76fcbca4800ddacef6f678f3a7c79a1fe7067fe37", size = 575642, upload-time = "2025-10-22T22:23:10.348Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d4/407ad9960ca7856d7b25c96dcbe019270b5ffdd83a561787bc682c797086/rpds_py-0.28.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:bcf1d210dfee61a6c86551d67ee1031899c0fdbae88b2d44a569995d43797712", size = 544507, upload-time = "2025-10-22T22:23:12.434Z" }, + { url = "https://files.pythonhosted.org/packages/51/31/2f46fe0efcac23fbf5797c6b6b7e1c76f7d60773e525cb65fcbc582ee0f2/rpds_py-0.28.0-cp313-cp313t-win32.whl", hash = "sha256:3aa4dc0fdab4a7029ac63959a3ccf4ed605fee048ba67ce89ca3168da34a1342", size = 205376, upload-time = "2025-10-22T22:23:13.979Z" }, + { url = "https://files.pythonhosted.org/packages/92/e4/15947bda33cbedfc134490a41841ab8870a72a867a03d4969d886f6594a2/rpds_py-0.28.0-cp313-cp313t-win_amd64.whl", hash = "sha256:7b7d9d83c942855e4fdcfa75d4f96f6b9e272d42fffcb72cd4bb2577db2e2907", size = 215907, upload-time = "2025-10-22T22:23:15.5Z" }, + { url = "https://files.pythonhosted.org/packages/08/47/ffe8cd7a6a02833b10623bf765fbb57ce977e9a4318ca0e8cf97e9c3d2b3/rpds_py-0.28.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:dcdcb890b3ada98a03f9f2bb108489cdc7580176cb73b4f2d789e9a1dac1d472", size = 353830, upload-time = "2025-10-22T22:23:17.03Z" }, + { url = "https://files.pythonhosted.org/packages/f9/9f/890f36cbd83a58491d0d91ae0db1702639edb33fb48eeb356f80ecc6b000/rpds_py-0.28.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f274f56a926ba2dc02976ca5b11c32855cbd5925534e57cfe1fda64e04d1add2", size = 341819, upload-time = "2025-10-22T22:23:18.57Z" }, + { url = "https://files.pythonhosted.org/packages/09/e3/921eb109f682aa24fb76207698fbbcf9418738f35a40c21652c29053f23d/rpds_py-0.28.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fe0438ac4a29a520ea94c8c7f1754cdd8feb1bc490dfda1bfd990072363d527", size = 373127, upload-time = "2025-10-22T22:23:20.216Z" }, + { url = "https://files.pythonhosted.org/packages/23/13/bce4384d9f8f4989f1a9599c71b7a2d877462e5fd7175e1f69b398f729f4/rpds_py-0.28.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8a358a32dd3ae50e933347889b6af9a1bdf207ba5d1a3f34e1a38cd3540e6733", size = 382767, upload-time = "2025-10-22T22:23:21.787Z" }, + { url = "https://files.pythonhosted.org/packages/23/e1/579512b2d89a77c64ccef5a0bc46a6ef7f72ae0cf03d4b26dcd52e57ee0a/rpds_py-0.28.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e80848a71c78aa328fefaba9c244d588a342c8e03bda518447b624ea64d1ff56", size = 517585, upload-time = "2025-10-22T22:23:23.699Z" }, + { url = "https://files.pythonhosted.org/packages/62/3c/ca704b8d324a2591b0b0adcfcaadf9c862375b11f2f667ac03c61b4fd0a6/rpds_py-0.28.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f586db2e209d54fe177e58e0bc4946bea5fb0102f150b1b2f13de03e1f0976f8", size = 399828, upload-time = "2025-10-22T22:23:25.713Z" }, + { url = "https://files.pythonhosted.org/packages/da/37/e84283b9e897e3adc46b4c88bb3f6ec92a43bd4d2f7ef5b13459963b2e9c/rpds_py-0.28.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ae8ee156d6b586e4292491e885d41483136ab994e719a13458055bec14cf370", size = 375509, upload-time = "2025-10-22T22:23:27.32Z" }, + { url = "https://files.pythonhosted.org/packages/1a/c2/a980beab869d86258bf76ec42dec778ba98151f253a952b02fe36d72b29c/rpds_py-0.28.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:a805e9b3973f7e27f7cab63a6b4f61d90f2e5557cff73b6e97cd5b8540276d3d", size = 392014, upload-time = "2025-10-22T22:23:29.332Z" }, + { url = "https://files.pythonhosted.org/packages/da/b5/b1d3c5f9d3fa5aeef74265f9c64de3c34a0d6d5cd3c81c8b17d5c8f10ed4/rpds_py-0.28.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5d3fd16b6dc89c73a4da0b4ac8b12a7ecc75b2864b95c9e5afed8003cb50a728", size = 402410, upload-time = "2025-10-22T22:23:31.14Z" }, + { url = "https://files.pythonhosted.org/packages/74/ae/cab05ff08dfcc052afc73dcb38cbc765ffc86f94e966f3924cd17492293c/rpds_py-0.28.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6796079e5d24fdaba6d49bda28e2c47347e89834678f2bc2c1b4fc1489c0fb01", size = 553593, upload-time = "2025-10-22T22:23:32.834Z" }, + { url = "https://files.pythonhosted.org/packages/70/80/50d5706ea2a9bfc9e9c5f401d91879e7c790c619969369800cde202da214/rpds_py-0.28.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:76500820c2af232435cbe215e3324c75b950a027134e044423f59f5b9a1ba515", size = 576925, upload-time = "2025-10-22T22:23:34.47Z" }, + { url = "https://files.pythonhosted.org/packages/ab/12/85a57d7a5855a3b188d024b099fd09c90db55d32a03626d0ed16352413ff/rpds_py-0.28.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bbdc5640900a7dbf9dd707fe6388972f5bbd883633eb68b76591044cfe346f7e", size = 542444, upload-time = "2025-10-22T22:23:36.093Z" }, + { url = "https://files.pythonhosted.org/packages/6c/65/10643fb50179509150eb94d558e8837c57ca8b9adc04bd07b98e57b48f8c/rpds_py-0.28.0-cp314-cp314-win32.whl", hash = "sha256:adc8aa88486857d2b35d75f0640b949759f79dc105f50aa2c27816b2e0dd749f", size = 207968, upload-time = "2025-10-22T22:23:37.638Z" }, + { url = "https://files.pythonhosted.org/packages/b4/84/0c11fe4d9aaea784ff4652499e365963222481ac647bcd0251c88af646eb/rpds_py-0.28.0-cp314-cp314-win_amd64.whl", hash = "sha256:66e6fa8e075b58946e76a78e69e1a124a21d9a48a5b4766d15ba5b06869d1fa1", size = 218876, upload-time = "2025-10-22T22:23:39.179Z" }, + { url = "https://files.pythonhosted.org/packages/0f/e0/3ab3b86ded7bb18478392dc3e835f7b754cd446f62f3fc96f4fe2aca78f6/rpds_py-0.28.0-cp314-cp314-win_arm64.whl", hash = "sha256:a6fe887c2c5c59413353b7c0caff25d0e566623501ccfff88957fa438a69377d", size = 212506, upload-time = "2025-10-22T22:23:40.755Z" }, + { url = "https://files.pythonhosted.org/packages/51/ec/d5681bb425226c3501eab50fc30e9d275de20c131869322c8a1729c7b61c/rpds_py-0.28.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7a69df082db13c7070f7b8b1f155fa9e687f1d6aefb7b0e3f7231653b79a067b", size = 355433, upload-time = "2025-10-22T22:23:42.259Z" }, + { url = "https://files.pythonhosted.org/packages/be/ec/568c5e689e1cfb1ea8b875cffea3649260955f677fdd7ddc6176902d04cd/rpds_py-0.28.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b1cde22f2c30ebb049a9e74c5374994157b9b70a16147d332f89c99c5960737a", size = 342601, upload-time = "2025-10-22T22:23:44.372Z" }, + { url = "https://files.pythonhosted.org/packages/32/fe/51ada84d1d2a1d9d8f2c902cfddd0133b4a5eb543196ab5161d1c07ed2ad/rpds_py-0.28.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5338742f6ba7a51012ea470bd4dc600a8c713c0c72adaa0977a1b1f4327d6592", size = 372039, upload-time = "2025-10-22T22:23:46.025Z" }, + { url = "https://files.pythonhosted.org/packages/07/c1/60144a2f2620abade1a78e0d91b298ac2d9b91bc08864493fa00451ef06e/rpds_py-0.28.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e1460ebde1bcf6d496d80b191d854adedcc619f84ff17dc1c6d550f58c9efbba", size = 382407, upload-time = "2025-10-22T22:23:48.098Z" }, + { url = "https://files.pythonhosted.org/packages/45/ed/091a7bbdcf4038a60a461df50bc4c82a7ed6d5d5e27649aab61771c17585/rpds_py-0.28.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e3eb248f2feba84c692579257a043a7699e28a77d86c77b032c1d9fbb3f0219c", size = 518172, upload-time = "2025-10-22T22:23:50.16Z" }, + { url = "https://files.pythonhosted.org/packages/54/dd/02cc90c2fd9c2ef8016fd7813bfacd1c3a1325633ec8f244c47b449fc868/rpds_py-0.28.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3bbba5def70b16cd1c1d7255666aad3b290fbf8d0fe7f9f91abafb73611a91", size = 399020, upload-time = "2025-10-22T22:23:51.81Z" }, + { url = "https://files.pythonhosted.org/packages/ab/81/5d98cc0329bbb911ccecd0b9e19fbf7f3a5de8094b4cda5e71013b2dd77e/rpds_py-0.28.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3114f4db69ac5a1f32e7e4d1cbbe7c8f9cf8217f78e6e002cedf2d54c2a548ed", size = 377451, upload-time = "2025-10-22T22:23:53.711Z" }, + { url = "https://files.pythonhosted.org/packages/b4/07/4d5bcd49e3dfed2d38e2dcb49ab6615f2ceb9f89f5a372c46dbdebb4e028/rpds_py-0.28.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:4b0cb8a906b1a0196b863d460c0222fb8ad0f34041568da5620f9799b83ccf0b", size = 390355, upload-time = "2025-10-22T22:23:55.299Z" }, + { url = "https://files.pythonhosted.org/packages/3f/79/9f14ba9010fee74e4f40bf578735cfcbb91d2e642ffd1abe429bb0b96364/rpds_py-0.28.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf681ac76a60b667106141e11a92a3330890257e6f559ca995fbb5265160b56e", size = 403146, upload-time = "2025-10-22T22:23:56.929Z" }, + { url = "https://files.pythonhosted.org/packages/39/4c/f08283a82ac141331a83a40652830edd3a4a92c34e07e2bbe00baaea2f5f/rpds_py-0.28.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1e8ee6413cfc677ce8898d9cde18cc3a60fc2ba756b0dec5b71eb6eb21c49fa1", size = 552656, upload-time = "2025-10-22T22:23:58.62Z" }, + { url = "https://files.pythonhosted.org/packages/61/47/d922fc0666f0dd8e40c33990d055f4cc6ecff6f502c2d01569dbed830f9b/rpds_py-0.28.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:b3072b16904d0b5572a15eb9d31c1954e0d3227a585fc1351aa9878729099d6c", size = 576782, upload-time = "2025-10-22T22:24:00.312Z" }, + { url = "https://files.pythonhosted.org/packages/d3/0c/5bafdd8ccf6aa9d3bfc630cfece457ff5b581af24f46a9f3590f790e3df2/rpds_py-0.28.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b670c30fd87a6aec281c3c9896d3bae4b205fd75d79d06dc87c2503717e46092", size = 544671, upload-time = "2025-10-22T22:24:02.297Z" }, + { url = "https://files.pythonhosted.org/packages/2c/37/dcc5d8397caa924988693519069d0beea077a866128719351a4ad95e82fc/rpds_py-0.28.0-cp314-cp314t-win32.whl", hash = "sha256:8014045a15b4d2b3476f0a287fcc93d4f823472d7d1308d47884ecac9e612be3", size = 205749, upload-time = "2025-10-22T22:24:03.848Z" }, + { url = "https://files.pythonhosted.org/packages/d7/69/64d43b21a10d72b45939a28961216baeb721cc2a430f5f7c3bfa21659a53/rpds_py-0.28.0-cp314-cp314t-win_amd64.whl", hash = "sha256:7a4e59c90d9c27c561eb3160323634a9ff50b04e4f7820600a2beb0ac90db578", size = 216233, upload-time = "2025-10-22T22:24:05.471Z" }, + { url = "https://files.pythonhosted.org/packages/ae/bc/b43f2ea505f28119bd551ae75f70be0c803d2dbcd37c1b3734909e40620b/rpds_py-0.28.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f5e7101145427087e493b9c9b959da68d357c28c562792300dd21a095118ed16", size = 363913, upload-time = "2025-10-22T22:24:07.129Z" }, + { url = "https://files.pythonhosted.org/packages/28/f2/db318195d324c89a2c57dc5195058cbadd71b20d220685c5bd1da79ee7fe/rpds_py-0.28.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:31eb671150b9c62409a888850aaa8e6533635704fe2b78335f9aaf7ff81eec4d", size = 350452, upload-time = "2025-10-22T22:24:08.754Z" }, + { url = "https://files.pythonhosted.org/packages/ae/f2/1391c819b8573a4898cedd6b6c5ec5bc370ce59e5d6bdcebe3c9c1db4588/rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48b55c1f64482f7d8bd39942f376bfdf2f6aec637ee8c805b5041e14eeb771db", size = 380957, upload-time = "2025-10-22T22:24:10.826Z" }, + { url = "https://files.pythonhosted.org/packages/5a/5c/e5de68ee7eb7248fce93269833d1b329a196d736aefb1a7481d1e99d1222/rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:24743a7b372e9a76171f6b69c01aedf927e8ac3e16c474d9fe20d552a8cb45c7", size = 391919, upload-time = "2025-10-22T22:24:12.559Z" }, + { url = "https://files.pythonhosted.org/packages/fb/4f/2376336112cbfeb122fd435d608ad8d5041b3aed176f85a3cb32c262eb80/rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:389c29045ee8bbb1627ea190b4976a310a295559eaf9f1464a1a6f2bf84dde78", size = 528541, upload-time = "2025-10-22T22:24:14.197Z" }, + { url = "https://files.pythonhosted.org/packages/68/53/5ae232e795853dd20da7225c5dd13a09c0a905b1a655e92bdf8d78a99fd9/rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23690b5827e643150cf7b49569679ec13fe9a610a15949ed48b85eb7f98f34ec", size = 405629, upload-time = "2025-10-22T22:24:16.001Z" }, + { url = "https://files.pythonhosted.org/packages/b9/2d/351a3b852b683ca9b6b8b38ed9efb2347596973849ba6c3a0e99877c10aa/rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f0c9266c26580e7243ad0d72fc3e01d6b33866cfab5084a6da7576bcf1c4f72", size = 384123, upload-time = "2025-10-22T22:24:17.585Z" }, + { url = "https://files.pythonhosted.org/packages/e0/15/870804daa00202728cc91cb8e2385fa9f1f4eb49857c49cfce89e304eae6/rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:4c6c4db5d73d179746951486df97fd25e92396be07fc29ee8ff9a8f5afbdfb27", size = 400923, upload-time = "2025-10-22T22:24:19.512Z" }, + { url = "https://files.pythonhosted.org/packages/53/25/3706b83c125fa2a0bccceac951de3f76631f6bd0ee4d02a0ed780712ef1b/rpds_py-0.28.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a3b695a8fa799dd2cfdb4804b37096c5f6dba1ac7f48a7fbf6d0485bcd060316", size = 413767, upload-time = "2025-10-22T22:24:21.316Z" }, + { url = "https://files.pythonhosted.org/packages/ef/f9/ce43dbe62767432273ed2584cef71fef8411bddfb64125d4c19128015018/rpds_py-0.28.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:6aa1bfce3f83baf00d9c5fcdbba93a3ab79958b4c7d7d1f55e7fe68c20e63912", size = 561530, upload-time = "2025-10-22T22:24:22.958Z" }, + { url = "https://files.pythonhosted.org/packages/46/c9/ffe77999ed8f81e30713dd38fd9ecaa161f28ec48bb80fa1cd9118399c27/rpds_py-0.28.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:7b0f9dceb221792b3ee6acb5438eb1f02b0cb2c247796a72b016dcc92c6de829", size = 585453, upload-time = "2025-10-22T22:24:24.779Z" }, + { url = "https://files.pythonhosted.org/packages/ed/d2/4a73b18821fd4669762c855fd1f4e80ceb66fb72d71162d14da58444a763/rpds_py-0.28.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:5d0145edba8abd3db0ab22b5300c99dc152f5c9021fab861be0f0544dc3cbc5f", size = 552199, upload-time = "2025-10-22T22:24:26.54Z" }, +] + +[[package]] +name = "ruff" +version = "0.14.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/34/8218a19b2055b80601e8fd201ec723c74c7fe1ca06d525a43ed07b6d8e85/ruff-0.14.2.tar.gz", hash = "sha256:98da787668f239313d9c902ca7c523fe11b8ec3f39345553a51b25abc4629c96", size = 5539663, upload-time = "2025-10-23T19:37:00.956Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/dd/23eb2db5ad9acae7c845700493b72d3ae214dce0b226f27df89216110f2b/ruff-0.14.2-py3-none-linux_armv6l.whl", hash = "sha256:7cbe4e593505bdec5884c2d0a4d791a90301bc23e49a6b1eb642dd85ef9c64f1", size = 12533390, upload-time = "2025-10-23T19:36:18.044Z" }, + { url = "https://files.pythonhosted.org/packages/5a/8c/5f9acff43ddcf3f85130d0146d0477e28ccecc495f9f684f8f7119b74c0d/ruff-0.14.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:8d54b561729cee92f8d89c316ad7a3f9705533f5903b042399b6ae0ddfc62e11", size = 12887187, upload-time = "2025-10-23T19:36:22.664Z" }, + { url = "https://files.pythonhosted.org/packages/99/fa/047646491479074029665022e9f3dc6f0515797f40a4b6014ea8474c539d/ruff-0.14.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5c8753dfa44ebb2cde10ce5b4d2ef55a41fb9d9b16732a2c5df64620dbda44a3", size = 11925177, upload-time = "2025-10-23T19:36:24.778Z" }, + { url = "https://files.pythonhosted.org/packages/15/8b/c44cf7fe6e59ab24a9d939493a11030b503bdc2a16622cede8b7b1df0114/ruff-0.14.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d0bbeffb8d9f4fccf7b5198d566d0bad99a9cb622f1fc3467af96cb8773c9e3", size = 12358285, upload-time = "2025-10-23T19:36:26.979Z" }, + { url = "https://files.pythonhosted.org/packages/45/01/47701b26254267ef40369aea3acb62a7b23e921c27372d127e0f3af48092/ruff-0.14.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7047f0c5a713a401e43a88d36843d9c83a19c584e63d664474675620aaa634a8", size = 12303832, upload-time = "2025-10-23T19:36:29.192Z" }, + { url = "https://files.pythonhosted.org/packages/2d/5c/ae7244ca4fbdf2bee9d6405dcd5bc6ae51ee1df66eb7a9884b77b8af856d/ruff-0.14.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bf8d2f9aa1602599217d82e8e0af7fd33e5878c4d98f37906b7c93f46f9a839", size = 13036995, upload-time = "2025-10-23T19:36:31.861Z" }, + { url = "https://files.pythonhosted.org/packages/27/4c/0860a79ce6fd4c709ac01173f76f929d53f59748d0dcdd662519835dae43/ruff-0.14.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1c505b389e19c57a317cf4b42db824e2fca96ffb3d86766c1c9f8b96d32048a7", size = 14512649, upload-time = "2025-10-23T19:36:33.915Z" }, + { url = "https://files.pythonhosted.org/packages/7f/7f/d365de998069720a3abfc250ddd876fc4b81a403a766c74ff9bde15b5378/ruff-0.14.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a307fc45ebd887b3f26b36d9326bb70bf69b01561950cdcc6c0bdf7bb8e0f7cc", size = 14088182, upload-time = "2025-10-23T19:36:36.983Z" }, + { url = "https://files.pythonhosted.org/packages/6c/ea/d8e3e6b209162000a7be1faa41b0a0c16a133010311edc3329753cc6596a/ruff-0.14.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:61ae91a32c853172f832c2f40bd05fd69f491db7289fb85a9b941ebdd549781a", size = 13599516, upload-time = "2025-10-23T19:36:39.208Z" }, + { url = "https://files.pythonhosted.org/packages/fa/ea/c7810322086db68989fb20a8d5221dd3b79e49e396b01badca07b433ab45/ruff-0.14.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1967e40286f63ee23c615e8e7e98098dedc7301568bd88991f6e544d8ae096", size = 13272690, upload-time = "2025-10-23T19:36:41.453Z" }, + { url = "https://files.pythonhosted.org/packages/a9/39/10b05acf8c45786ef501d454e00937e1b97964f846bf28883d1f9619928a/ruff-0.14.2-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:2877f02119cdebf52a632d743a2e302dea422bfae152ebe2f193d3285a3a65df", size = 13496497, upload-time = "2025-10-23T19:36:43.61Z" }, + { url = "https://files.pythonhosted.org/packages/59/a1/1f25f8301e13751c30895092485fada29076e5e14264bdacc37202e85d24/ruff-0.14.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e681c5bc777de5af898decdcb6ba3321d0d466f4cb43c3e7cc2c3b4e7b843a05", size = 12266116, upload-time = "2025-10-23T19:36:45.625Z" }, + { url = "https://files.pythonhosted.org/packages/5c/fa/0029bfc9ce16ae78164e6923ef392e5f173b793b26cc39aa1d8b366cf9dc/ruff-0.14.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e21be42d72e224736f0c992cdb9959a2fa53c7e943b97ef5d081e13170e3ffc5", size = 12281345, upload-time = "2025-10-23T19:36:47.618Z" }, + { url = "https://files.pythonhosted.org/packages/a5/ab/ece7baa3c0f29b7683be868c024f0838770c16607bea6852e46b202f1ff6/ruff-0.14.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b8264016f6f209fac16262882dbebf3f8be1629777cf0f37e7aff071b3e9b92e", size = 12629296, upload-time = "2025-10-23T19:36:49.789Z" }, + { url = "https://files.pythonhosted.org/packages/a4/7f/638f54b43f3d4e48c6a68062794e5b367ddac778051806b9e235dfb7aa81/ruff-0.14.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5ca36b4cb4db3067a3b24444463ceea5565ea78b95fe9a07ca7cb7fd16948770", size = 13371610, upload-time = "2025-10-23T19:36:51.882Z" }, + { url = "https://files.pythonhosted.org/packages/8d/35/3654a973ebe5b32e1fd4a08ed2d46755af7267da7ac710d97420d7b8657d/ruff-0.14.2-py3-none-win32.whl", hash = "sha256:41775927d287685e08f48d8eb3f765625ab0b7042cc9377e20e64f4eb0056ee9", size = 12415318, upload-time = "2025-10-23T19:36:53.961Z" }, + { url = "https://files.pythonhosted.org/packages/71/30/3758bcf9e0b6a4193a6f51abf84254aba00887dfa8c20aba18aa366c5f57/ruff-0.14.2-py3-none-win_amd64.whl", hash = "sha256:0df3424aa5c3c08b34ed8ce099df1021e3adaca6e90229273496b839e5a7e1af", size = 13565279, upload-time = "2025-10-23T19:36:56.578Z" }, + { url = "https://files.pythonhosted.org/packages/2e/5d/aa883766f8ef9ffbe6aa24f7192fb71632f31a30e77eb39aa2b0dc4290ac/ruff-0.14.2-py3-none-win_arm64.whl", hash = "sha256:ea9d635e83ba21569fbacda7e78afbfeb94911c9434aff06192d9bc23fd5495a", size = 12554956, upload-time = "2025-10-23T19:36:58.714Z" }, +] + +[[package]] +name = "secretstorage" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "jeepney" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/31/9f/11ef35cf1027c1339552ea7bfe6aaa74a8516d8b5caf6e7d338daf54fd80/secretstorage-3.4.0.tar.gz", hash = "sha256:c46e216d6815aff8a8a18706a2fbfd8d53fcbb0dce99301881687a1b0289ef7c", size = 19748, upload-time = "2025-09-09T16:42:13.859Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/ff/2e2eed29e02c14a5cb6c57f09b2d5b40e65d6cc71f45b52e0be295ccbc2f/secretstorage-3.4.0-py3-none-any.whl", hash = "sha256:0e3b6265c2c63509fb7415717607e4b2c9ab767b7f344a57473b779ca13bd02e", size = 15272, upload-time = "2025-09-09T16:42:12.744Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, +] + +[[package]] +name = "sse-starlette" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/6f/22ed6e33f8a9e76ca0a412405f31abb844b779d52c5f96660766edcd737c/sse_starlette-3.0.2.tar.gz", hash = "sha256:ccd60b5765ebb3584d0de2d7a6e4f745672581de4f5005ab31c3a25d10b52b3a", size = 20985, upload-time = "2025-07-27T09:07:44.565Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/10/c78f463b4ef22eef8491f218f692be838282cd65480f6e423d7730dfd1fb/sse_starlette-3.0.2-py3-none-any.whl", hash = "sha256:16b7cbfddbcd4eaca11f7b586f3b8a080f1afe952c15813455b162edea619e5a", size = 11297, upload-time = "2025-07-27T09:07:43.268Z" }, +] + +[[package]] +name = "starlette" +version = "0.48.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a7/a5/d6f429d43394057b67a6b5bbe6eae2f77a6bf7459d961fdb224bf206eee6/starlette-0.48.0.tar.gz", hash = "sha256:7e8cee469a8ab2352911528110ce9088fdc6a37d9876926e73da7ce4aa4c7a46", size = 2652949, upload-time = "2025-09-13T08:41:05.699Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/72/2db2f49247d0a18b4f1bb9a5a39a0162869acf235f3a96418363947b3d46/starlette-0.48.0-py3-none-any.whl", hash = "sha256:0764ca97b097582558ecb498132ed0c7d942f233f365b86ba37770e026510659", size = 73736, upload-time = "2025-09-13T08:41:03.869Z" }, +] + +[[package]] +name = "tomli" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392, upload-time = "2025-10-08T22:01:47.119Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/2e/299f62b401438d5fe1624119c723f5d877acc86a4c2492da405626665f12/tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45", size = 153236, upload-time = "2025-10-08T22:01:00.137Z" }, + { url = "https://files.pythonhosted.org/packages/86/7f/d8fffe6a7aefdb61bced88fcb5e280cfd71e08939da5894161bd71bea022/tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba", size = 148084, upload-time = "2025-10-08T22:01:01.63Z" }, + { url = "https://files.pythonhosted.org/packages/47/5c/24935fb6a2ee63e86d80e4d3b58b222dafaf438c416752c8b58537c8b89a/tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf", size = 234832, upload-time = "2025-10-08T22:01:02.543Z" }, + { url = "https://files.pythonhosted.org/packages/89/da/75dfd804fc11e6612846758a23f13271b76d577e299592b4371a4ca4cd09/tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441", size = 242052, upload-time = "2025-10-08T22:01:03.836Z" }, + { url = "https://files.pythonhosted.org/packages/70/8c/f48ac899f7b3ca7eb13af73bacbc93aec37f9c954df3c08ad96991c8c373/tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845", size = 239555, upload-time = "2025-10-08T22:01:04.834Z" }, + { url = "https://files.pythonhosted.org/packages/ba/28/72f8afd73f1d0e7829bfc093f4cb98ce0a40ffc0cc997009ee1ed94ba705/tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c", size = 245128, upload-time = "2025-10-08T22:01:05.84Z" }, + { url = "https://files.pythonhosted.org/packages/b6/eb/a7679c8ac85208706d27436e8d421dfa39d4c914dcf5fa8083a9305f58d9/tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456", size = 96445, upload-time = "2025-10-08T22:01:06.896Z" }, + { url = "https://files.pythonhosted.org/packages/0a/fe/3d3420c4cb1ad9cb462fb52967080575f15898da97e21cb6f1361d505383/tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be", size = 107165, upload-time = "2025-10-08T22:01:08.107Z" }, + { url = "https://files.pythonhosted.org/packages/ff/b7/40f36368fcabc518bb11c8f06379a0fd631985046c038aca08c6d6a43c6e/tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac", size = 154891, upload-time = "2025-10-08T22:01:09.082Z" }, + { url = "https://files.pythonhosted.org/packages/f9/3f/d9dd692199e3b3aab2e4e4dd948abd0f790d9ded8cd10cbaae276a898434/tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22", size = 148796, upload-time = "2025-10-08T22:01:10.266Z" }, + { url = "https://files.pythonhosted.org/packages/60/83/59bff4996c2cf9f9387a0f5a3394629c7efa5ef16142076a23a90f1955fa/tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f", size = 242121, upload-time = "2025-10-08T22:01:11.332Z" }, + { url = "https://files.pythonhosted.org/packages/45/e5/7c5119ff39de8693d6baab6c0b6dcb556d192c165596e9fc231ea1052041/tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52", size = 250070, upload-time = "2025-10-08T22:01:12.498Z" }, + { url = "https://files.pythonhosted.org/packages/45/12/ad5126d3a278f27e6701abde51d342aa78d06e27ce2bb596a01f7709a5a2/tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8", size = 245859, upload-time = "2025-10-08T22:01:13.551Z" }, + { url = "https://files.pythonhosted.org/packages/fb/a1/4d6865da6a71c603cfe6ad0e6556c73c76548557a8d658f9e3b142df245f/tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6", size = 250296, upload-time = "2025-10-08T22:01:14.614Z" }, + { url = "https://files.pythonhosted.org/packages/a0/b7/a7a7042715d55c9ba6e8b196d65d2cb662578b4d8cd17d882d45322b0d78/tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876", size = 97124, upload-time = "2025-10-08T22:01:15.629Z" }, + { url = "https://files.pythonhosted.org/packages/06/1e/f22f100db15a68b520664eb3328fb0ae4e90530887928558112c8d1f4515/tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878", size = 107698, upload-time = "2025-10-08T22:01:16.51Z" }, + { url = "https://files.pythonhosted.org/packages/89/48/06ee6eabe4fdd9ecd48bf488f4ac783844fd777f547b8d1b61c11939974e/tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b", size = 154819, upload-time = "2025-10-08T22:01:17.964Z" }, + { url = "https://files.pythonhosted.org/packages/f1/01/88793757d54d8937015c75dcdfb673c65471945f6be98e6a0410fba167ed/tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae", size = 148766, upload-time = "2025-10-08T22:01:18.959Z" }, + { url = "https://files.pythonhosted.org/packages/42/17/5e2c956f0144b812e7e107f94f1cc54af734eb17b5191c0bbfb72de5e93e/tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b", size = 240771, upload-time = "2025-10-08T22:01:20.106Z" }, + { url = "https://files.pythonhosted.org/packages/d5/f4/0fbd014909748706c01d16824eadb0307115f9562a15cbb012cd9b3512c5/tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf", size = 248586, upload-time = "2025-10-08T22:01:21.164Z" }, + { url = "https://files.pythonhosted.org/packages/30/77/fed85e114bde5e81ecf9bc5da0cc69f2914b38f4708c80ae67d0c10180c5/tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f", size = 244792, upload-time = "2025-10-08T22:01:22.417Z" }, + { url = "https://files.pythonhosted.org/packages/55/92/afed3d497f7c186dc71e6ee6d4fcb0acfa5f7d0a1a2878f8beae379ae0cc/tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05", size = 248909, upload-time = "2025-10-08T22:01:23.859Z" }, + { url = "https://files.pythonhosted.org/packages/f8/84/ef50c51b5a9472e7265ce1ffc7f24cd4023d289e109f669bdb1553f6a7c2/tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606", size = 96946, upload-time = "2025-10-08T22:01:24.893Z" }, + { url = "https://files.pythonhosted.org/packages/b2/b7/718cd1da0884f281f95ccfa3a6cc572d30053cba64603f79d431d3c9b61b/tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999", size = 107705, upload-time = "2025-10-08T22:01:26.153Z" }, + { url = "https://files.pythonhosted.org/packages/19/94/aeafa14a52e16163008060506fcb6aa1949d13548d13752171a755c65611/tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e", size = 154244, upload-time = "2025-10-08T22:01:27.06Z" }, + { url = "https://files.pythonhosted.org/packages/db/e4/1e58409aa78eefa47ccd19779fc6f36787edbe7d4cd330eeeedb33a4515b/tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3", size = 148637, upload-time = "2025-10-08T22:01:28.059Z" }, + { url = "https://files.pythonhosted.org/packages/26/b6/d1eccb62f665e44359226811064596dd6a366ea1f985839c566cd61525ae/tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc", size = 241925, upload-time = "2025-10-08T22:01:29.066Z" }, + { url = "https://files.pythonhosted.org/packages/70/91/7cdab9a03e6d3d2bb11beae108da5bdc1c34bdeb06e21163482544ddcc90/tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0", size = 249045, upload-time = "2025-10-08T22:01:31.98Z" }, + { url = "https://files.pythonhosted.org/packages/15/1b/8c26874ed1f6e4f1fcfeb868db8a794cbe9f227299402db58cfcc858766c/tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879", size = 245835, upload-time = "2025-10-08T22:01:32.989Z" }, + { url = "https://files.pythonhosted.org/packages/fd/42/8e3c6a9a4b1a1360c1a2a39f0b972cef2cc9ebd56025168c4137192a9321/tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005", size = 253109, upload-time = "2025-10-08T22:01:34.052Z" }, + { url = "https://files.pythonhosted.org/packages/22/0c/b4da635000a71b5f80130937eeac12e686eefb376b8dee113b4a582bba42/tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463", size = 97930, upload-time = "2025-10-08T22:01:35.082Z" }, + { url = "https://files.pythonhosted.org/packages/b9/74/cb1abc870a418ae99cd5c9547d6bce30701a954e0e721821df483ef7223c/tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8", size = 107964, upload-time = "2025-10-08T22:01:36.057Z" }, + { url = "https://files.pythonhosted.org/packages/54/78/5c46fff6432a712af9f792944f4fcd7067d8823157949f4e40c56b8b3c83/tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77", size = 163065, upload-time = "2025-10-08T22:01:37.27Z" }, + { url = "https://files.pythonhosted.org/packages/39/67/f85d9bd23182f45eca8939cd2bc7050e1f90c41f4a2ecbbd5963a1d1c486/tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf", size = 159088, upload-time = "2025-10-08T22:01:38.235Z" }, + { url = "https://files.pythonhosted.org/packages/26/5a/4b546a0405b9cc0659b399f12b6adb750757baf04250b148d3c5059fc4eb/tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530", size = 268193, upload-time = "2025-10-08T22:01:39.712Z" }, + { url = "https://files.pythonhosted.org/packages/42/4f/2c12a72ae22cf7b59a7fe75b3465b7aba40ea9145d026ba41cb382075b0e/tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b", size = 275488, upload-time = "2025-10-08T22:01:40.773Z" }, + { url = "https://files.pythonhosted.org/packages/92/04/a038d65dbe160c3aa5a624e93ad98111090f6804027d474ba9c37c8ae186/tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67", size = 272669, upload-time = "2025-10-08T22:01:41.824Z" }, + { url = "https://files.pythonhosted.org/packages/be/2f/8b7c60a9d1612a7cbc39ffcca4f21a73bf368a80fc25bccf8253e2563267/tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f", size = 279709, upload-time = "2025-10-08T22:01:43.177Z" }, + { url = "https://files.pythonhosted.org/packages/7e/46/cc36c679f09f27ded940281c38607716c86cf8ba4a518d524e349c8b4874/tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0", size = 107563, upload-time = "2025-10-08T22:01:44.233Z" }, + { url = "https://files.pythonhosted.org/packages/84/ff/426ca8683cf7b753614480484f6437f568fd2fda2edbdf57a2d3d8b27a0b/tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba", size = 119756, upload-time = "2025-10-08T22:01:45.234Z" }, + { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, +] + +[[package]] +name = "urllib3" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, +] + +[[package]] +name = "uvicorn" +version = "0.38.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cb/ce/f06b84e2697fef4688ca63bdb2fdf113ca0a3be33f94488f2cadb690b0cf/uvicorn-0.38.0.tar.gz", hash = "sha256:fd97093bdd120a2609fc0d3afe931d4d4ad688b6e75f0f929fde1bc36fe0e91d", size = 80605, upload-time = "2025-10-18T13:46:44.63Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ee/d9/d88e73ca598f4f6ff671fb5fde8a32925c2e08a637303a1d12883c7305fa/uvicorn-0.38.0-py3-none-any.whl", hash = "sha256:48c0afd214ceb59340075b4a052ea1ee91c16fbc2a9b1469cca0e54566977b02", size = 68109, upload-time = "2025-10-18T13:46:42.958Z" }, +] + +[[package]] +name = "websockets" +version = "15.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9f/32/18fcd5919c293a398db67443acd33fde142f283853076049824fc58e6f75/websockets-15.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431", size = 175423, upload-time = "2025-03-05T20:01:56.276Z" }, + { url = "https://files.pythonhosted.org/packages/76/70/ba1ad96b07869275ef42e2ce21f07a5b0148936688c2baf7e4a1f60d5058/websockets-15.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57", size = 173082, upload-time = "2025-03-05T20:01:57.563Z" }, + { url = "https://files.pythonhosted.org/packages/86/f2/10b55821dd40eb696ce4704a87d57774696f9451108cff0d2824c97e0f97/websockets-15.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905", size = 173330, upload-time = "2025-03-05T20:01:59.063Z" }, + { url = "https://files.pythonhosted.org/packages/a5/90/1c37ae8b8a113d3daf1065222b6af61cc44102da95388ac0018fcb7d93d9/websockets-15.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562", size = 182878, upload-time = "2025-03-05T20:02:00.305Z" }, + { url = "https://files.pythonhosted.org/packages/8e/8d/96e8e288b2a41dffafb78e8904ea7367ee4f891dafc2ab8d87e2124cb3d3/websockets-15.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792", size = 181883, upload-time = "2025-03-05T20:02:03.148Z" }, + { url = "https://files.pythonhosted.org/packages/93/1f/5d6dbf551766308f6f50f8baf8e9860be6182911e8106da7a7f73785f4c4/websockets-15.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413", size = 182252, upload-time = "2025-03-05T20:02:05.29Z" }, + { url = "https://files.pythonhosted.org/packages/d4/78/2d4fed9123e6620cbf1706c0de8a1632e1a28e7774d94346d7de1bba2ca3/websockets-15.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8", size = 182521, upload-time = "2025-03-05T20:02:07.458Z" }, + { url = "https://files.pythonhosted.org/packages/e7/3b/66d4c1b444dd1a9823c4a81f50231b921bab54eee2f69e70319b4e21f1ca/websockets-15.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3", size = 181958, upload-time = "2025-03-05T20:02:09.842Z" }, + { url = "https://files.pythonhosted.org/packages/08/ff/e9eed2ee5fed6f76fdd6032ca5cd38c57ca9661430bb3d5fb2872dc8703c/websockets-15.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf", size = 181918, upload-time = "2025-03-05T20:02:11.968Z" }, + { url = "https://files.pythonhosted.org/packages/d8/75/994634a49b7e12532be6a42103597b71098fd25900f7437d6055ed39930a/websockets-15.0.1-cp311-cp311-win32.whl", hash = "sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85", size = 176388, upload-time = "2025-03-05T20:02:13.32Z" }, + { url = "https://files.pythonhosted.org/packages/98/93/e36c73f78400a65f5e236cd376713c34182e6663f6889cd45a4a04d8f203/websockets-15.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065", size = 176828, upload-time = "2025-03-05T20:02:14.585Z" }, + { url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437, upload-time = "2025-03-05T20:02:16.706Z" }, + { url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096, upload-time = "2025-03-05T20:02:18.832Z" }, + { url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332, upload-time = "2025-03-05T20:02:20.187Z" }, + { url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152, upload-time = "2025-03-05T20:02:22.286Z" }, + { url = "https://files.pythonhosted.org/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096, upload-time = "2025-03-05T20:02:24.368Z" }, + { url = "https://files.pythonhosted.org/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523, upload-time = "2025-03-05T20:02:25.669Z" }, + { url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790, upload-time = "2025-03-05T20:02:26.99Z" }, + { url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165, upload-time = "2025-03-05T20:02:30.291Z" }, + { url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160, upload-time = "2025-03-05T20:02:31.634Z" }, + { url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395, upload-time = "2025-03-05T20:02:33.017Z" }, + { url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841, upload-time = "2025-03-05T20:02:34.498Z" }, + { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440, upload-time = "2025-03-05T20:02:36.695Z" }, + { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098, upload-time = "2025-03-05T20:02:37.985Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329, upload-time = "2025-03-05T20:02:39.298Z" }, + { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111, upload-time = "2025-03-05T20:02:40.595Z" }, + { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054, upload-time = "2025-03-05T20:02:41.926Z" }, + { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496, upload-time = "2025-03-05T20:02:43.304Z" }, + { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829, upload-time = "2025-03-05T20:02:48.812Z" }, + { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217, upload-time = "2025-03-05T20:02:50.14Z" }, + { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195, upload-time = "2025-03-05T20:02:51.561Z" }, + { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393, upload-time = "2025-03-05T20:02:53.814Z" }, + { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" }, + { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, +] + +[[package]] +name = "werkzeug" +version = "3.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/32/af/d4502dc713b4ccea7175d764718d5183caf8d0867a4f0190d5d4a45cea49/werkzeug-3.1.1.tar.gz", hash = "sha256:8cd39dfbdfc1e051965f156163e2974e52c210f130810e9ad36858f0fd3edad4", size = 806453, upload-time = "2024-11-01T16:40:45.462Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ee/ea/c67e1dee1ba208ed22c06d1d547ae5e293374bfc43e0eb0ef5e262b68561/werkzeug-3.1.1-py3-none-any.whl", hash = "sha256:a71124d1ef06008baafa3d266c02f56e1836a5984afd6dd6c9230669d60d9fb5", size = 224371, upload-time = "2024-11-01T16:40:43.994Z" }, +] + +[[package]] +name = "zipp" +version = "3.23.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, +] From 4eae1e1149d149924f980c0152097abbe3ffe994 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EC=A7=80=ED=98=81?= Date: Tue, 28 Oct 2025 13:14:00 +0900 Subject: [PATCH 5/9] chore : ruff manual fix --- examples/python_api_with_token.py | 1 + fastapps/__init__.py | 22 ++++++++--------- fastapps/auth/__init__.py | 4 ++-- fastapps/auth/verifier.py | 7 +++--- fastapps/builder/compiler.py | 6 ++--- fastapps/cli/commands/create.py | 33 +++++++++++++------------- fastapps/cli/commands/dev.py | 11 ++++----- fastapps/cli/commands/init.py | 20 ++++++++-------- fastapps/cli/commands/use.py | 39 ++++++++++++++++--------------- fastapps/cli/main.py | 5 ++-- fastapps/core/__init__.py | 2 +- fastapps/core/server.py | 6 +++-- fastapps/core/widget.py | 9 ++++--- fastapps/dev_server.py | 16 ++++++------- fastapps/types/__init__.py | 2 +- fastapps/types/schema.py | 2 +- 16 files changed, 97 insertions(+), 88 deletions(-) diff --git a/examples/python_api_with_token.py b/examples/python_api_with_token.py index 456eca5..a5d0400 100644 --- a/examples/python_api_with_token.py +++ b/examples/python_api_with_token.py @@ -6,6 +6,7 @@ """ import os + from fastapps import start_dev_server if __name__ == "__main__": diff --git a/fastapps/__init__.py b/fastapps/__init__.py index d63237d..5b0d1f5 100644 --- a/fastapps/__init__.py +++ b/fastapps/__init__.py @@ -18,27 +18,27 @@ async def execute(self, input_data) -> Dict[str, Any]: __version__ = "1.1.1" __author__ = "FastApps Team" -from .core.widget import BaseWidget, ClientContext, UserContext -from .core.server import WidgetMCPServer from .builder.compiler import WidgetBuilder, WidgetBuildResult -from .types.schema import Field, ConfigDict +from .core.server import WidgetMCPServer +from .core.widget import BaseWidget, ClientContext, UserContext from .dev_server import ( - start_dev_server, - start_dev_server_with_config, - get_server_info, - run_dev_server, DevServerConfig, - ServerInfo, DevServerError, - ProjectNotFoundError, NgrokError, + ProjectNotFoundError, + ServerInfo, + get_server_info, + run_dev_server, + start_dev_server, + start_dev_server_with_config, ) +from .types.schema import ConfigDict, Field # Auth exports (optional, graceful if not available) try: - from .auth.verifier import JWTVerifier - from .auth import TokenVerifier, AccessToken + from .auth import AccessToken, TokenVerifier from .auth.decorators import auth_required, no_auth, optional_auth + from .auth.verifier import JWTVerifier _auth_exports = [ "JWTVerifier", diff --git a/fastapps/auth/__init__.py b/fastapps/auth/__init__.py index 21ea925..f254840 100644 --- a/fastapps/auth/__init__.py +++ b/fastapps/auth/__init__.py @@ -5,12 +5,12 @@ and re-exports FastMCP auth components. """ -from .verifier import JWTVerifier from .decorators import auth_required, no_auth, optional_auth +from .verifier import JWTVerifier # Re-export FastMCP auth components for convenience try: - from mcp.server.auth.provider import TokenVerifier, AccessToken + from mcp.server.auth.provider import AccessToken, TokenVerifier except ImportError: # Graceful fallback if fastmcp version doesn't have auth TokenVerifier = None diff --git a/fastapps/auth/verifier.py b/fastapps/auth/verifier.py index 71ec475..51836ff 100644 --- a/fastapps/auth/verifier.py +++ b/fastapps/auth/verifier.py @@ -5,13 +5,14 @@ and other OAuth 2.1 providers. """ -from typing import Optional, List +from typing import List, Optional + import httpx try: - from mcp.server.auth.provider import TokenVerifier, AccessToken import jwt from jwt import PyJWKClient + from mcp.server.auth.provider import AccessToken, TokenVerifier MCP_AUTH_AVAILABLE = True except ImportError: @@ -87,7 +88,7 @@ def _initialize_jwks(self): self.jwks_client = PyJWKClient(jwks_uri) except Exception as e: - raise RuntimeError(f"Failed to initialize JWKS from {self.issuer_url}: {e}") + raise RuntimeError(f"Failed to initialize JWKS from {self.issuer_url}: {e}") from e async def verify_token(self, token: str) -> Optional[AccessToken]: """ diff --git a/fastapps/builder/compiler.py b/fastapps/builder/compiler.py index 990d40d..a82c526 100644 --- a/fastapps/builder/compiler.py +++ b/fastapps/builder/compiler.py @@ -1,9 +1,9 @@ -import subprocess +import platform import re import shutil -import platform -from pathlib import Path +import subprocess from dataclasses import dataclass +from pathlib import Path from typing import Dict diff --git a/fastapps/cli/commands/create.py b/fastapps/cli/commands/create.py index 40dd3f5..37c41b1 100644 --- a/fastapps/cli/commands/create.py +++ b/fastapps/cli/commands/create.py @@ -1,6 +1,7 @@ """Create widget command.""" from pathlib import Path + from rich.console import Console console = Console() @@ -57,7 +58,7 @@ def generate_tool_code( "user_id": user.subject, "scopes": user.scopes, } - + return { "message": "Welcome to FastApps" }""" @@ -94,12 +95,12 @@ class {class_name}Tool(BaseWidget): input_schema = {class_name}Input invoking = "Loading widget..." invoked = "Widget ready!" - + widget_csp = {{ "connect_domains": [], "resource_domains": [] }} - + async def execute(self, input_data: {class_name}Input, context=None, user=None) -> Dict[str, Any]: {execute_body} """ @@ -129,12 +130,12 @@ class {ClassName}Tool(BaseWidget): input_schema = {ClassName}Input invoking = "Loading widget..." invoked = "Widget ready!" - + widget_csp = {{ "connect_domains": [], "resource_domains": [] }} - + async def execute(self, input_data: {ClassName}Input, context=None, user=None) -> Dict[str, Any]: # Access authenticated user (if present) # if user and user.is_authenticated: @@ -143,7 +144,7 @@ async def execute(self, input_data: {ClassName}Input, context=None, user=None) - # "scopes": user.scopes, # "user_data": user.claims # }} - + return {{ "message": "Welcome to FastApps" }} @@ -154,7 +155,7 @@ async def execute(self, input_data: {ClassName}Input, context=None, user=None) - export default function {ClassName}() {{ const props = useWidgetProps(); - + return (
> requirements.txt") console.print(" echo 'openai' >> requirements.txt") console.print(" pip install -r requirements.txt") - + # Display setup instructions console.print("\n[bold green]✓ Metorial MCP integration added![/bold green]") console.print("\n[cyan]Setup Instructions:[/cyan]") console.print("\n[yellow]1. Install dependencies:[/yellow]") console.print(" pip install metorial openai") - + console.print("\n[yellow]2. Set environment variables:[/yellow]") console.print(" export METORIAL_API_KEY='your_metorial_api_key'") console.print(" export OPENAI_API_KEY='your_openai_api_key'") console.print(" export METORIAL_DEPLOYMENT_ID='your_deployment_id'") - + console.print("\n[yellow]3. Usage in your code:[/yellow]") console.print(" from server.api.metorial_mcp import call_metorial") console.print("") console.print(" result = await call_metorial('Search for AI news')") console.print(" print(result)") - + console.print("\n[dim]Documentation: https://metorial.ai/docs[/dim]") console.print() - + return True def use_integration(integration_name: str): """ Add an integration to the FastApps project. - + Args: integration_name: Name of the integration (e.g., 'metorial') """ - + if integration_name == "metorial": return use_metorial() else: diff --git a/fastapps/cli/main.py b/fastapps/cli/main.py index 41f6c2f..5044fba 100644 --- a/fastapps/cli/main.py +++ b/fastapps/cli/main.py @@ -2,9 +2,10 @@ import click from rich.console import Console + from .commands.create import create_widget +from .commands.dev import reset_ngrok_token, start_dev_server from .commands.init import init_project -from .commands.dev import start_dev_server, reset_ngrok_token from .commands.use import use_integration console = Console() @@ -183,7 +184,7 @@ def use(integration_name): Example: fastapps use metorial - + This will create server/api/metorial_mcp.py with environment variable support. """ use_integration(integration_name) diff --git a/fastapps/core/__init__.py b/fastapps/core/__init__.py index 75201ae..d39511f 100644 --- a/fastapps/core/__init__.py +++ b/fastapps/core/__init__.py @@ -1,6 +1,6 @@ """Core Flick framework modules.""" -from .widget import BaseWidget from .server import WidgetMCPServer +from .widget import BaseWidget __all__ = ["BaseWidget", "WidgetMCPServer"] diff --git a/fastapps/core/server.py b/fastapps/core/server.py index 6889289..f785341 100644 --- a/fastapps/core/server.py +++ b/fastapps/core/server.py @@ -1,12 +1,14 @@ -from typing import List, Any, Dict, Optional +from typing import Any, Dict, List, Optional + from fastmcp import FastMCP from mcp import types + from .widget import BaseWidget, ClientContext, UserContext # Auth imports (optional, graceful degradation if not available) try: - from mcp.server.auth.settings import AuthSettings from mcp.server.auth.provider import TokenVerifier + from mcp.server.auth.settings import AuthSettings MCP_AUTH_AVAILABLE = True except ImportError: diff --git a/fastapps/core/widget.py b/fastapps/core/widget.py index 22135f4..a011a8c 100644 --- a/fastapps/core/widget.py +++ b/fastapps/core/widget.py @@ -1,7 +1,10 @@ from abc import ABC, abstractmethod -from typing import Any, Dict, Optional, List -from pydantic import BaseModel +from typing import Any, Dict, List, Optional + import mcp.types as types +from pydantic import BaseModel + +from fastapps.builder.compiler import WidgetBuildResult class UserContext: @@ -128,7 +131,7 @@ class BaseWidget(ABC): ) default_locale: str = "en" - def __init__(self, build_result: "WidgetBuildResult"): + def __init__(self, build_result: WidgetBuildResult): self.build_result = build_result self.template_uri = f"ui://widget/{self.identifier}.html" self.resolved_locale = self.default_locale diff --git a/fastapps/dev_server.py b/fastapps/dev_server.py index 48b404c..22ae21e 100644 --- a/fastapps/dev_server.py +++ b/fastapps/dev_server.py @@ -15,9 +15,9 @@ import json import sys +from dataclasses import asdict, dataclass from pathlib import Path -from typing import Optional, Dict, Any -from dataclasses import dataclass, asdict +from typing import Any, Dict, Optional @dataclass @@ -150,9 +150,9 @@ def _setup_ngrok(token: str, port: int) -> str: except ImportError: raise NgrokError( "pyngrok is not installed. Install it with: pip install pyngrok" - ) + ) from None except Exception as e: - raise NgrokError(f"Failed to create ngrok tunnel: {e}") + raise NgrokError(f"Failed to create ngrok tunnel: {e}") from e def _validate_project(project_root: Path) -> None: @@ -245,7 +245,7 @@ def start_dev_server( mcp_endpoint=public_url, ) - print(f"✅ Tunnel created!") + print("✅ Tunnel created!") print(f"\n{server_info}\n") # Return info if requested (for testing/integration) @@ -256,7 +256,7 @@ def start_dev_server( sys.path.insert(0, str(config.project_root)) # Import and run server - print(f"🚀 Starting FastApps server...\n") + print("🚀 Starting FastApps server...\n") try: import uvicorn @@ -274,7 +274,7 @@ def start_dev_server( raise DevServerError( f"Failed to import server: {e}\n" f"Make sure you're in a FastApps project with dependencies installed." - ) + ) from e except KeyboardInterrupt: print("\n⏹️ Server stopped") @@ -282,7 +282,7 @@ def start_dev_server( except NgrokError: raise except Exception as e: - raise DevServerError(f"Server error: {e}") + raise DevServerError(f"Server error: {e}") from e def start_dev_server_with_config(config: DevServerConfig) -> Optional[ServerInfo]: diff --git a/fastapps/types/__init__.py b/fastapps/types/__init__.py index 0e3bbab..41a5517 100644 --- a/fastapps/types/__init__.py +++ b/fastapps/types/__init__.py @@ -1,5 +1,5 @@ """Type definitions for Flick framework.""" -from .schema import Field, ConfigDict +from .schema import ConfigDict, Field __all__ = ["Field", "ConfigDict"] diff --git a/fastapps/types/schema.py b/fastapps/types/schema.py index 65e2ed4..ee8e09c 100644 --- a/fastapps/types/schema.py +++ b/fastapps/types/schema.py @@ -1,5 +1,5 @@ """Type exports for Flick framework.""" -from pydantic import Field, ConfigDict +from pydantic import ConfigDict, Field __all__ = ["Field", "ConfigDict"] From 3dfb55626c8ed7e0a13947fda22a16803368ac6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EC=A7=80=ED=98=81?= Date: Tue, 28 Oct 2025 13:39:04 +0900 Subject: [PATCH 6/9] chore : unify version source, migrate project to use uv --- .github/WORKFLOWS.md | 11 +++-- .gitignore | 3 +- CODE_STYLE.md | 5 +- CONTRIBUTING.md | 66 +++++++++++++++++++++++---- NGROK_INTEGRATION.md | 4 ++ OAUTH_TESTING_GUIDE.md | 2 + README.md | 15 ++++-- TESTING_SUMMARY.md | 1 + create_auth_test.sh | 15 ++++-- docs/08-AUTH.md | 1 + docs/PROJECT_SETUP.md | 1 + docs/PYTHON_API.md | 2 + docs/QUICKSTART.md | 4 ++ examples/README.md | 4 +- examples/python_api_error_handling.py | 4 +- fastapps/__init__.py | 8 +++- fastapps/auth/verifier.py | 2 +- fastapps/cli/commands/dev.py | 4 +- fastapps/cli/commands/init.py | 2 +- fastapps/cli/commands/use.py | 2 + fastapps/cli/main.py | 9 +++- fastapps/core/server.py | 11 ++++- fastapps/dev_server.py | 2 +- 23 files changed, 145 insertions(+), 33 deletions(-) diff --git a/.github/WORKFLOWS.md b/.github/WORKFLOWS.md index d7d6766..237d228 100644 --- a/.github/WORKFLOWS.md +++ b/.github/WORKFLOWS.md @@ -101,7 +101,7 @@ Add these secrets in GitHub Settings → Secrets and variables → Actions: 4. **Automatic Publishing** - `publish.yml` triggers on release publish - Package builds and publishes to PyPI - - Users can install with `pip install fastapps` + - Users can install with `pip install fastapps` or `uv pip install fastapps` ### Testing Releases (Optional) @@ -109,7 +109,7 @@ Before publishing to production PyPI: 1. Create a **draft** release 2. `test-publish.yml` publishes to Test PyPI -3. Test installation: `pip install -i https://test.pypi.org/simple/ fastapps` +3. Test installation: `pip install -i https://test.pypi.org/simple/ fastapps` or `uv pip install --index-url https://test.pypi.org/simple/ fastapps` 4. If successful, publish the release ## PR Labels for Release Notes @@ -136,8 +136,11 @@ Configured in `.github/dependabot.yml`: ## Running Tests Locally ```bash -# Install dev dependencies -pip install -e ".[dev]" +# Install dev dependencies (recommended - matches CI) +uv sync --dev + +# Or with pip (traditional) +# pip install -e ".[dev]" # Run tests pytest diff --git a/.gitignore b/.gitignore index 0c81efd..d4ae839 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .vscode/ -*.egg-info/ \ No newline at end of file +*.egg-info/ +**/__pycache__/ \ No newline at end of file diff --git a/CODE_STYLE.md b/CODE_STYLE.md index dd7936e..7cc72ea 100644 --- a/CODE_STYLE.md +++ b/CODE_STYLE.md @@ -15,6 +15,7 @@ Black is the uncompromising Python code formatter. It reformats entire files in **Installation**: ```bash pip install black +# Or with uv: uv pip install black ``` **Usage**: @@ -59,6 +60,7 @@ Ruff is an extremely fast Python linter that replaces multiple tools (Flake8, is **Installation**: ```bash pip install ruff +# Or with uv: uv pip install ruff ``` **Usage**: @@ -370,6 +372,7 @@ Install pre-commit hooks to automatically format/lint before commits: ```bash pip install pre-commit +# Or with uv: uv pip install pre-commit pre-commit install ``` @@ -412,7 +415,7 @@ repos: ### PyCharm 1. **Black**: - - Install Black: `pip install black` + - Install Black: `pip install black` or `uv pip install black` - Settings → Tools → Black → Enable - Settings → Tools → Actions on Save → Run Black diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index eb3f98d..4ad7f31 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -35,6 +35,25 @@ By participating in this project, you agree to maintain a respectful and inclusi ### Initial Setup +**Recommended: Using uv (matches CI pipeline)** + +```bash +# Clone your fork +git clone https://github.com/YOUR_USERNAME/fastapps.git +cd fastapps + +# Install uv if not already installed +# curl -LsSf https://astral.sh/uv/install.sh | sh + +# Install development dependencies +uv sync --dev + +# Install pre-commit hooks (already installed via uv sync --dev) +pre-commit install +``` + +**Alternative: Using pip (traditional approach)** + ```bash # Clone your fork git clone https://github.com/YOUR_USERNAME/fastapps.git @@ -71,11 +90,15 @@ pytest -v ### Building the Package ```bash -# Build distribution packages -python -m build +# With uv (recommended) +uv build # Check package validity -twine check dist/* +uv run twine check dist/* + +# Or with pip/build (traditional) +# python -m build +# twine check dist/* ``` ## Code Style and Formatting @@ -146,7 +169,8 @@ We use mypy for type checking (not strictly enforced but encouraged): ```bash # Install mypy -pip install mypy +uv pip install mypy +# Or: pip install mypy # Run type checking mypy fastapps --ignore-missing-imports @@ -166,14 +190,16 @@ For React components in the `widgets/` directory: We recommend using pre-commit hooks to automatically format and lint code: ```bash -# Install pre-commit -pip install pre-commit - -# Install hooks +# If you used uv sync --dev or pip install -e ".[dev]", pre-commit is already installed. +# Just install the git hooks: pre-commit install # Run manually on all files pre-commit run --all-files + +# If pre-commit is not installed (standalone installation): +# uv pip install pre-commit +# Or: pip install pre-commit ``` Create `.pre-commit-config.yaml`: @@ -236,6 +262,30 @@ start htmlcov/index.html # Windows Before submitting a PR, run all CI checks locally: +**With uv (matches CI exactly):** + +```bash +# Install dependencies +uv sync --dev + +# Format code +black . + +# Lint code +ruff check . + +# Run tests +pytest --cov=fastapps + +# Build package +uv build + +# Check package +uv run twine check dist/* +``` + +**Or with pip (traditional):** + ```bash # Format code black . diff --git a/NGROK_INTEGRATION.md b/NGROK_INTEGRATION.md index f23d26b..5af020c 100644 --- a/NGROK_INTEGRATION.md +++ b/NGROK_INTEGRATION.md @@ -16,6 +16,7 @@ The `fastapps dev` command now: 1. **Install dependencies:** ```bash pip install -e . + # Or with uv: uv sync --dev ``` This will install pyngrok and all other required dependencies. @@ -112,11 +113,13 @@ Alternatively, you can manually edit `~/.fastapps/config.json`: Install pyngrok: ```bash pip install pyngrok +# Or with uv: uv pip install pyngrok ``` Or reinstall FastApps with all dependencies: ```bash pip install -e . +# Or with uv: uv sync --dev ``` ### "Not in a FastApps project directory" Error @@ -128,6 +131,7 @@ If you haven't initialized a project yet: fastapps init myproject cd myproject pip install -r requirements.txt +# Or with uv: uv pip install -r requirements.txt npm install fastapps create mywidget npm run build diff --git a/OAUTH_TESTING_GUIDE.md b/OAUTH_TESTING_GUIDE.md index 399447d..d295a2d 100644 --- a/OAUTH_TESTING_GUIDE.md +++ b/OAUTH_TESTING_GUIDE.md @@ -90,9 +90,11 @@ source venv/bin/activate # On Windows: venv\Scripts\activate # Install FastApps pip install -e ../FastApps +# Or with uv: uv pip install -e ../FastApps # Install dependencies pip install httpx PyJWT cryptography +# Or with uv: uv pip install httpx PyJWT cryptography # Initialize project fastapps init auth-test-project diff --git a/README.md b/README.md index 1d4cc64..e6ec992 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,8 @@ source .venv/bin/activate # Mac/Linux # 1. Install pip install fastapps +# Or with uv (faster, modern alternative): +# uv pip install fastapps # 2. Create project (includes example widget + auto npm install) fastapps init my-app @@ -149,11 +151,16 @@ We welcome contributions! Please see our contributing guidelines: git clone https://github.com/YOUR_USERNAME/FastApps.git cd FastApps -# Install development dependencies -pip install -e ".[dev]" +# Install uv (if not already installed) +# curl -LsSf https://astral.sh/uv/install.sh | sh -# Install pre-commit hooks -pip install pre-commit +# Install development dependencies (recommended - matches CI) +uv sync --dev + +# Or use pip (traditional approach) +# pip install -e ".[dev]" + +# Install pre-commit hooks (already installed via uv sync --dev) pre-commit install # Make changes and ensure they pass checks diff --git a/TESTING_SUMMARY.md b/TESTING_SUMMARY.md index 3776138..e7c3e1b 100644 --- a/TESTING_SUMMARY.md +++ b/TESTING_SUMMARY.md @@ -245,6 +245,7 @@ Widget receives UserContext: **Solution**: ```bash pip install --upgrade fastmcp +# Or: uv pip install --upgrade fastmcp ``` ### Issue: "Failed to initialize JWKS" diff --git a/create_auth_test.sh b/create_auth_test.sh index efb13de..7b77377 100755 --- a/create_auth_test.sh +++ b/create_auth_test.sh @@ -44,9 +44,18 @@ source venv/bin/activate # Install FastApps echo "📦 Installing FastApps..." -pip install -q --upgrade pip -pip install -q -e ../FastApps -pip install -q httpx PyJWT cryptography + +# Detect if uv is available +if command -v uv &> /dev/null; then + echo "Using uv for package installation..." + uv pip install -q -e ../FastApps + uv pip install -q httpx PyJWT cryptography +else + echo "Using pip for package installation..." + pip install -q --upgrade pip + pip install -q -e ../FastApps + pip install -q httpx PyJWT cryptography +fi # Initialize project echo "🏗️ Initializing FastApps project..." diff --git a/docs/08-AUTH.md b/docs/08-AUTH.md index f76c5e3..0238a26 100644 --- a/docs/08-AUTH.md +++ b/docs/08-AUTH.md @@ -342,6 +342,7 @@ class MonitoredVerifier(TokenVerifier): **Solution**: Upgrade fastmcp: ```bash pip install --upgrade fastmcp +# Or: uv pip install --upgrade fastmcp ``` ### Issue: "Failed to initialize JWKS" diff --git a/docs/PROJECT_SETUP.md b/docs/PROJECT_SETUP.md index 9c3ce98..044043a 100644 --- a/docs/PROJECT_SETUP.md +++ b/docs/PROJECT_SETUP.md @@ -129,6 +129,7 @@ httpx>=0.28.0 ```bash # Python pip install -r requirements.txt +# Or with uv: uv pip install -r requirements.txt # JavaScript npm install diff --git a/docs/PYTHON_API.md b/docs/PYTHON_API.md index 2ba0cdd..674f9f2 100644 --- a/docs/PYTHON_API.md +++ b/docs/PYTHON_API.md @@ -21,12 +21,14 @@ The Python API is included with FastApps: ```bash pip install fastapps +# Or with uv: uv pip install fastapps ``` Make sure pyngrok is also installed: ```bash pip install pyngrok +# Or with uv: uv pip install pyngrok ``` --- diff --git a/docs/QUICKSTART.md b/docs/QUICKSTART.md index 720e2dd..c20cd53 100644 --- a/docs/QUICKSTART.md +++ b/docs/QUICKSTART.md @@ -36,6 +36,7 @@ my-widgets/ ```bash pip install fastapps +# Or with uv (faster): uv pip install fastapps ``` ## Step 2: Create Project Structure @@ -138,6 +139,8 @@ httpx>=0.28.0 ```bash pip install -r requirements.txt +# Or with uv: uv pip install -r requirements.txt + npm install ``` @@ -210,6 +213,7 @@ Your server runs on `http://localhost:8001`. For ChatGPT testing, you'll need to ```bash pip install --upgrade fastapps +# Or with uv: uv pip install --upgrade fastapps ``` ### Build fails diff --git a/examples/README.md b/examples/README.md index 08c558d..9322080 100644 --- a/examples/README.md +++ b/examples/README.md @@ -100,8 +100,8 @@ See `docs/API.md` for complete API documentation. ## Requirements - Python 3.11+ -- FastApps installed (`pip install fastapps`) -- pyngrok installed (`pip install pyngrok`) +- FastApps installed (`pip install fastapps` or `uv pip install fastapps`) +- pyngrok installed (`pip install pyngrok` or `uv pip install pyngrok`) - ngrok auth token (get free at https://ngrok.com) ## Need Help? diff --git a/examples/python_api_error_handling.py b/examples/python_api_error_handling.py index 8a22378..c4c12d8 100644 --- a/examples/python_api_error_handling.py +++ b/examples/python_api_error_handling.py @@ -34,7 +34,7 @@ except DevServerError as e: print(f"\n❌ Server Error: {e}") print("\n💡 Check:") - print(" 1. All dependencies are installed (pip install -r requirements.txt)") + print(" 1. All dependencies are installed (pip install -r requirements.txt or uv pip install -r requirements.txt)") print(" 2. Port is not already in use") print(" 3. Project structure is correct") @@ -45,5 +45,5 @@ print(f"\n❌ Unexpected Error: {e}") print("\n💡 Try:") print(" 1. Check error message above") - print(" 2. Verify FastApps installation: pip install --upgrade fastapps") + print(" 2. Verify FastApps installation: pip install --upgrade fastapps or uv pip install --upgrade fastapps") print(" 3. Report issue if problem persists") diff --git a/fastapps/__init__.py b/fastapps/__init__.py index 5b0d1f5..f8d35b7 100644 --- a/fastapps/__init__.py +++ b/fastapps/__init__.py @@ -15,7 +15,13 @@ async def execute(self, input_data) -> Dict[str, Any]: return {"message": "Hello from FastApps!"} """ -__version__ = "1.1.1" +# Get version from package metadata +try: + from importlib.metadata import version + __version__ = version("fastapps") +except Exception: + __version__ = "1.1.1" # Fallback version + __author__ = "FastApps Team" from .builder.compiler import WidgetBuilder, WidgetBuildResult diff --git a/fastapps/auth/verifier.py b/fastapps/auth/verifier.py index 51836ff..ec1d2e5 100644 --- a/fastapps/auth/verifier.py +++ b/fastapps/auth/verifier.py @@ -57,7 +57,7 @@ def __init__( if not MCP_AUTH_AVAILABLE: raise ImportError( "Authentication dependencies not available. " - "Install with: pip install 'PyJWT>=2.8.0' 'cryptography>=41.0.0'" + "Install with: pip install 'PyJWT>=2.8.0' 'cryptography>=41.0.0' or uv pip install 'PyJWT>=2.8.0' 'cryptography>=41.0.0'" ) self.issuer_url = issuer_url.rstrip("/") diff --git a/fastapps/cli/commands/dev.py b/fastapps/cli/commands/dev.py index a1850d9..4b9c9b2 100644 --- a/fastapps/cli/commands/dev.py +++ b/fastapps/cli/commands/dev.py @@ -177,7 +177,9 @@ def start_dev_server(port=8001, host="0.0.0.0"): except ImportError as e: if "pyngrok" in str(e): console.print("[red]Error: pyngrok not installed[/red]") - console.print("[yellow]Install it with: pip install pyngrok[/yellow]") + console.print("[yellow]Install it with:[/yellow]") + console.print(" pip install pyngrok") + console.print(" # Or: uv pip install pyngrok") else: console.print(f"[red]Error: Could not import server: {e}[/red]") console.print( diff --git a/fastapps/cli/commands/init.py b/fastapps/cli/commands/init.py index 5fab928..203b1f3 100644 --- a/fastapps/cli/commands/init.py +++ b/fastapps/cli/commands/init.py @@ -77,7 +77,7 @@ def auto_load_tools(build_results): ''' REQUIREMENTS_TXT = """# All dependencies included in fastapps package -# pip install fastapps is all you need! +# pip install fastapps (or: uv pip install fastapps) is all you need! """ diff --git a/fastapps/cli/commands/use.py b/fastapps/cli/commands/use.py index ca3b290..b8e7fa4 100644 --- a/fastapps/cli/commands/use.py +++ b/fastapps/cli/commands/use.py @@ -88,12 +88,14 @@ def use_metorial(): console.print(" echo 'metorial' >> requirements.txt") console.print(" echo 'openai' >> requirements.txt") console.print(" pip install -r requirements.txt") + console.print(" # Or: uv pip install -r requirements.txt") # Display setup instructions console.print("\n[bold green]✓ Metorial MCP integration added![/bold green]") console.print("\n[cyan]Setup Instructions:[/cyan]") console.print("\n[yellow]1. Install dependencies:[/yellow]") console.print(" pip install metorial openai") + console.print(" # Or: uv pip install metorial openai") console.print("\n[yellow]2. Set environment variables:[/yellow]") console.print(" export METORIAL_API_KEY='your_metorial_api_key'") diff --git a/fastapps/cli/main.py b/fastapps/cli/main.py index 5044fba..ac96801 100644 --- a/fastapps/cli/main.py +++ b/fastapps/cli/main.py @@ -10,9 +10,16 @@ console = Console() +# Get version from package metadata +try: + from importlib.metadata import version + __version__ = version("fastapps") +except Exception: + __version__ = "unknown" + @click.group() -@click.version_option(version="1.0.8", prog_name="fastapps") +@click.version_option(version=__version__, prog_name="fastapps") def cli(): """FastApps - ChatGPT Widget Framework diff --git a/fastapps/core/server.py b/fastapps/core/server.py index f785341..5d6012c 100644 --- a/fastapps/core/server.py +++ b/fastapps/core/server.py @@ -5,6 +5,13 @@ from .widget import BaseWidget, ClientContext, UserContext +# Get version from package +try: + from importlib.metadata import version + __version__ = version("fastapps") +except Exception: + __version__ = "1.1.1" # Fallback version + # Auth imports (optional, graceful degradation if not available) try: from mcp.server.auth.provider import TokenVerifier @@ -83,7 +90,7 @@ def __init__( if not MCP_AUTH_AVAILABLE: raise ImportError( "FastMCP auth support not available. " - "Please upgrade fastmcp: pip install --upgrade fastmcp" + "Please upgrade fastmcp: pip install --upgrade fastmcp or uv pip install --upgrade fastmcp" ) # Use built-in JWTVerifier if no custom verifier provided @@ -149,7 +156,7 @@ async def initialize_handler( types.InitializeResult( protocolVersion=req.params.protocolVersion, capabilities=types.ServerCapabilities(), - serverInfo=types.Implementation(name="FastApps", version="1.0.5"), + serverInfo=types.Implementation(name="FastApps", version=__version__), ) ) diff --git a/fastapps/dev_server.py b/fastapps/dev_server.py index 22ae21e..e479427 100644 --- a/fastapps/dev_server.py +++ b/fastapps/dev_server.py @@ -149,7 +149,7 @@ def _setup_ngrok(token: str, port: int) -> str: except ImportError: raise NgrokError( - "pyngrok is not installed. Install it with: pip install pyngrok" + "pyngrok is not installed. Install it with: pip install pyngrok or uv pip install pyngrok" ) from None except Exception as e: raise NgrokError(f"Failed to create ngrok tunnel: {e}") from e From d26f8e0c3215e362bdb912cbcc0f583398949cb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EC=A7=80=ED=98=81?= Date: Tue, 28 Oct 2025 16:51:43 +0900 Subject: [PATCH 7/9] refactor : removed dev_server module --- examples/README.md | 111 -------- examples/python_api_advanced.py | 28 -- examples/python_api_basic.py | 14 - examples/python_api_error_handling.py | 49 ---- examples/python_api_get_info.py | 45 ---- examples/python_api_with_token.py | 23 -- fastapps/__init__.py | 11 - fastapps/dev_server.py | 352 -------------------------- 8 files changed, 633 deletions(-) delete mode 100644 examples/README.md delete mode 100644 examples/python_api_advanced.py delete mode 100644 examples/python_api_basic.py delete mode 100644 examples/python_api_error_handling.py delete mode 100644 examples/python_api_get_info.py delete mode 100644 examples/python_api_with_token.py delete mode 100644 fastapps/dev_server.py diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index 9322080..0000000 --- a/examples/README.md +++ /dev/null @@ -1,111 +0,0 @@ -# FastApps Python API Examples - -This directory contains examples of using the FastApps Python API programmatically. - -## Available Examples - -### 1. Basic Usage (`python_api_basic.py`) -The simplest way to start a dev server: -```bash -python examples/python_api_basic.py -``` - -### 2. Advanced Configuration (`python_api_advanced.py`) -Custom port, host, and other settings: -```bash -python examples/python_api_advanced.py -``` - -### 3. With Token (`python_api_with_token.py`) -Provide ngrok token via environment variable: -```bash -NGROK_TOKEN=your_token python examples/python_api_with_token.py -``` - -### 4. Get Server Info (`python_api_get_info.py`) -Get URLs without starting the server: -```bash -python examples/python_api_get_info.py -``` - -### 5. Error Handling (`python_api_error_handling.py`) -Proper error handling patterns: -```bash -python examples/python_api_error_handling.py -``` - -## Quick Reference - -### Simple Start -```python -from fastapps import start_dev_server - -start_dev_server() -``` - -### With Configuration -```python -from fastapps import start_dev_server - -start_dev_server( - port=8080, - host="0.0.0.0", - ngrok_token="your_token", - auto_reload=True, - log_level="debug" -) -``` - -### Using Config Object -```python -from fastapps import start_dev_server_with_config, DevServerConfig - -config = DevServerConfig( - port=8080, - ngrok_token="your_token" -) -start_dev_server_with_config(config) -``` - -### Get Server Info -```python -from fastapps import get_server_info - -info = get_server_info(port=8001) -print(f"Public URL: {info.public_url}") -``` - -### Error Handling -```python -from fastapps import start_dev_server, DevServerError - -try: - start_dev_server() -except DevServerError as e: - print(f"Error: {e}") -``` - -## API Reference - -See `docs/API.md` for complete API documentation. - -## Use Cases - -- **Automation**: Start servers programmatically in scripts -- **Testing**: Get URLs for integration tests -- **CI/CD**: Run dev servers in pipelines -- **Jupyter Notebooks**: Start servers in notebooks -- **Custom Workflows**: Build your own dev tools - -## Requirements - -- Python 3.11+ -- FastApps installed (`pip install fastapps` or `uv pip install fastapps`) -- pyngrok installed (`pip install pyngrok` or `uv pip install pyngrok`) -- ngrok auth token (get free at https://ngrok.com) - -## Need Help? - -- CLI Documentation: Run `fastapps --help` -- Full Docs: See `docs/` directory -- Issues: https://github.com/fastapps-framework/fastapps/issues diff --git a/examples/python_api_advanced.py b/examples/python_api_advanced.py deleted file mode 100644 index 0153c20..0000000 --- a/examples/python_api_advanced.py +++ /dev/null @@ -1,28 +0,0 @@ -""" -Advanced Python API Usage for FastApps Dev Server - -This example shows how to configure the dev server with custom settings. -""" - -from fastapps import start_dev_server - -if __name__ == "__main__": - # Option 1: Using keyword arguments - print("Starting with custom port...") - - start_dev_server( - port=8080, - host="127.0.0.1", - log_level="debug", - auto_reload=True, # Enable hot reload - ) - - # Option 2: Using config object (alternative, not run in this example) - # config = DevServerConfig( - # port=8080, - # host="0.0.0.0", - # ngrok_token="your_token_here", # Optional - # auto_reload=True, - # log_level="debug" - # ) - # start_dev_server_with_config(config) diff --git a/examples/python_api_basic.py b/examples/python_api_basic.py deleted file mode 100644 index c7f8f72..0000000 --- a/examples/python_api_basic.py +++ /dev/null @@ -1,14 +0,0 @@ -""" -Basic Python API Usage for FastApps Dev Server - -This example shows the simplest way to start a dev server programmatically. -""" - -from fastapps import start_dev_server - -if __name__ == "__main__": - # Simple usage - uses defaults (port 8001, host 0.0.0.0) - # Will use saved ngrok token from ~/.fastapps/config.json - print("Starting FastApps development server...") - - start_dev_server() diff --git a/examples/python_api_error_handling.py b/examples/python_api_error_handling.py deleted file mode 100644 index c4c12d8..0000000 --- a/examples/python_api_error_handling.py +++ /dev/null @@ -1,49 +0,0 @@ -""" -Error Handling with Python API - -This example shows how to handle errors when using the dev server API. -""" - -from fastapps import ( - DevServerError, - NgrokError, - ProjectNotFoundError, - start_dev_server, -) - -if __name__ == "__main__": - try: - print("Starting dev server with error handling...") - - start_dev_server(port=8001, host="0.0.0.0") - - except ProjectNotFoundError as e: - print(f"\n❌ Project Error: {e}") - print("\n💡 Solution:") - print(" 1. Make sure you're in a FastApps project directory") - print(" 2. Check that server/main.py exists") - print(" 3. Run 'fastapps init myproject' to create a new project") - - except NgrokError as e: - print(f"\n❌ ngrok Error: {e}") - print("\n💡 Solution:") - print(" 1. Check your ngrok token is valid") - print(" 2. Verify internet connection") - print(" 3. Try running 'fastapps reset-token' and re-enter token") - - except DevServerError as e: - print(f"\n❌ Server Error: {e}") - print("\n💡 Check:") - print(" 1. All dependencies are installed (pip install -r requirements.txt or uv pip install -r requirements.txt)") - print(" 2. Port is not already in use") - print(" 3. Project structure is correct") - - except KeyboardInterrupt: - print("\n\n👋 Server stopped by user") - - except Exception as e: - print(f"\n❌ Unexpected Error: {e}") - print("\n💡 Try:") - print(" 1. Check error message above") - print(" 2. Verify FastApps installation: pip install --upgrade fastapps or uv pip install --upgrade fastapps") - print(" 3. Report issue if problem persists") diff --git a/examples/python_api_get_info.py b/examples/python_api_get_info.py deleted file mode 100644 index d27bce0..0000000 --- a/examples/python_api_get_info.py +++ /dev/null @@ -1,45 +0,0 @@ -""" -Get Server Info Without Starting Server - -This example shows how to get server URLs without actually starting the server. -Useful for testing, automation, or displaying URLs before starting. -""" - -import time - -from fastapps import get_server_info - -if __name__ == "__main__": - try: - # Get server info (creates ngrok tunnel but doesn't start server) - print("Creating ngrok tunnel...") - - info = get_server_info(port=8001) - - print("\n" + "=" * 50) - print("Server Information") - print("=" * 50) - print(f"Local URL: {info.local_url}") - print(f"Public URL: {info.public_url}") - print(f"MCP Endpoint: {info.mcp_endpoint}") - print(f"Port: {info.port}") - print(f"Host: {info.host}") - print("=" * 50) - - # You can also convert to dict - print("\nAs dictionary:") - print(info.to_dict()) - - print("\n✅ Tunnel created! You can now start your server manually.") - print(f" Use this public URL: {info.public_url}") - - # Keep tunnel alive - print("\nPress Ctrl+C to close tunnel...") - - while True: - time.sleep(1) - - except KeyboardInterrupt: - print("\n\n👋 Tunnel closed") - except Exception as e: - print(f"\n❌ Error: {e}") diff --git a/examples/python_api_with_token.py b/examples/python_api_with_token.py deleted file mode 100644 index a5d0400..0000000 --- a/examples/python_api_with_token.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -Python API with Explicit ngrok Token - -This example shows how to provide ngrok token programmatically. -Useful for CI/CD or when you don't want to save token to config file. -""" - -import os - -from fastapps import start_dev_server - -if __name__ == "__main__": - # Get token from environment variable - ngrok_token = os.getenv("NGROK_TOKEN") - - if not ngrok_token: - print("Error: NGROK_TOKEN environment variable not set") - print("Usage: NGROK_TOKEN=your_token python python_api_with_token.py") - exit(1) - - print("Starting server with provided ngrok token...") - - start_dev_server(port=8001, ngrok_token=ngrok_token) diff --git a/fastapps/__init__.py b/fastapps/__init__.py index f8d35b7..11e6bfe 100644 --- a/fastapps/__init__.py +++ b/fastapps/__init__.py @@ -27,17 +27,6 @@ async def execute(self, input_data) -> Dict[str, Any]: from .builder.compiler import WidgetBuilder, WidgetBuildResult from .core.server import WidgetMCPServer from .core.widget import BaseWidget, ClientContext, UserContext -from .dev_server import ( - DevServerConfig, - DevServerError, - NgrokError, - ProjectNotFoundError, - ServerInfo, - get_server_info, - run_dev_server, - start_dev_server, - start_dev_server_with_config, -) from .types.schema import ConfigDict, Field # Auth exports (optional, graceful if not available) diff --git a/fastapps/dev_server.py b/fastapps/dev_server.py deleted file mode 100644 index e479427..0000000 --- a/fastapps/dev_server.py +++ /dev/null @@ -1,352 +0,0 @@ -""" -FastApps Development Server API - -Programmatic interface for starting FastApps development server with ngrok tunnel. - -Example: - from fastapps import start_dev_server - - # Simple usage - start_dev_server() - - # With configuration - start_dev_server(port=8080, ngrok_token="your_token") -""" - -import json -import sys -from dataclasses import asdict, dataclass -from pathlib import Path -from typing import Any, Dict, Optional - - -@dataclass -class DevServerConfig: - """Configuration for development server. - - Attributes: - port: Port to run server on (default: 8001) - host: Host to bind server to (default: "0.0.0.0") - ngrok_token: ngrok auth token (optional, will prompt if not provided) - project_root: Path to FastApps project (default: current directory) - auto_reload: Enable auto-reload on code changes (default: False) - log_level: Uvicorn log level (default: "info") - """ - - port: int = 8001 - host: str = "0.0.0.0" - ngrok_token: Optional[str] = None - project_root: Optional[Path] = None - auto_reload: bool = False - log_level: str = "info" - - def __post_init__(self): - if self.project_root is None: - self.project_root = Path.cwd() - elif not isinstance(self.project_root, Path): - self.project_root = Path(self.project_root) - - -@dataclass -class ServerInfo: - """Information about running server. - - Attributes: - local_url: Local server URL - public_url: Public ngrok URL - port: Server port - host: Server host - mcp_endpoint: MCP server endpoint URL - """ - - local_url: str - public_url: str - port: int - host: str - mcp_endpoint: str - - def to_dict(self) -> Dict[str, Any]: - """Convert to dictionary.""" - return asdict(self) - - def __str__(self) -> str: - return ( - f"FastApps Dev Server\n" - f" Local: {self.local_url}\n" - f" Public: {self.public_url}\n" - f" MCP: {self.mcp_endpoint}" - ) - - -class DevServerError(Exception): - """Base exception for dev server errors.""" - - pass - - -class ProjectNotFoundError(DevServerError): - """Raised when FastApps project not found.""" - - pass - - -class NgrokError(DevServerError): - """Raised when ngrok fails.""" - - pass - - -def _get_config_dir() -> Path: - """Get FastApps config directory.""" - config_dir = Path.home() / ".fastapps" - config_dir.mkdir(exist_ok=True) - return config_dir - - -def _load_saved_config() -> Dict[str, Any]: - """Load saved configuration.""" - config_file = _get_config_dir() / "config.json" - if config_file.exists(): - try: - with open(config_file, "r") as f: - return json.load(f) - except Exception: - return {} - return {} - - -def _save_config(config: Dict[str, Any]) -> bool: - """Save configuration.""" - config_file = _get_config_dir() / "config.json" - try: - with open(config_file, "w") as f: - json.dump(config, f, indent=2) - return True - except Exception: - return False - - -def _get_ngrok_token(provided_token: Optional[str] = None) -> Optional[str]: - """Get ngrok token from config or parameter.""" - if provided_token: - return provided_token - - config = _load_saved_config() - return config.get("ngrok_token") - - -def _setup_ngrok(token: str, port: int) -> str: - """Setup ngrok tunnel and return public URL.""" - try: - from pyngrok import ngrok - - # Set auth token - ngrok.set_auth_token(token) - - # Create tunnel - public_url = ngrok.connect(port, bind_tls=True) - return public_url.public_url - - except ImportError: - raise NgrokError( - "pyngrok is not installed. Install it with: pip install pyngrok or uv pip install pyngrok" - ) from None - except Exception as e: - raise NgrokError(f"Failed to create ngrok tunnel: {e}") from e - - -def _validate_project(project_root: Path) -> None: - """Validate that directory is a FastApps project.""" - main_py = project_root / "server" / "main.py" - if not main_py.exists(): - raise ProjectNotFoundError( - f"Not a FastApps project. Missing: {main_py}\n" - f"Run 'fastapps init' to create a new project." - ) - - -def start_dev_server( - port: int = 8001, - host: str = "0.0.0.0", - ngrok_token: Optional[str] = None, - project_root: Optional[Path] = None, - auto_reload: bool = False, - log_level: str = "info", - return_info: bool = False, -) -> Optional[ServerInfo]: - """Start FastApps development server with ngrok tunnel. - - This function starts a FastApps server and creates a public ngrok tunnel. - It blocks until the server is stopped (Ctrl+C). - - Args: - port: Port to run server on (default: 8001) - host: Host to bind to (default: "0.0.0.0") - ngrok_token: ngrok auth token (if None, uses saved token or prompts) - project_root: Path to FastApps project (default: current directory) - auto_reload: Enable auto-reload on code changes (default: False) - log_level: Uvicorn log level (default: "info") - return_info: If True, return ServerInfo before starting (default: False) - - Returns: - ServerInfo if return_info=True, otherwise None - - Raises: - ProjectNotFoundError: If not in a FastApps project - NgrokError: If ngrok setup fails - DevServerError: For other server errors - - Example: - >>> from fastapps import start_dev_server - >>> start_dev_server(port=8080) - - # Or with config - >>> start_dev_server( - ... port=8080, - ... ngrok_token="your_token", - ... log_level="debug" - ... ) - """ - # Create config - config = DevServerConfig( - port=port, - host=host, - ngrok_token=ngrok_token, - project_root=project_root, - auto_reload=auto_reload, - log_level=log_level, - ) - - # Validate project - _validate_project(config.project_root) - - # Get ngrok token - token = _get_ngrok_token(config.ngrok_token) - if not token: - raise DevServerError( - "ngrok token not found. Provide it via:\n" - "1. ngrok_token parameter\n" - "2. Run 'fastapps dev' once to save token\n" - "3. Set in ~/.fastapps/config.json" - ) - - # Setup ngrok - try: - print(f"🔧 Setting up ngrok tunnel on port {config.port}...") - public_url = _setup_ngrok(token, config.port) - - # Create server info - local_url = f"http://{config.host}:{config.port}" - server_info = ServerInfo( - local_url=local_url, - public_url=public_url, - port=config.port, - host=config.host, - mcp_endpoint=public_url, - ) - - print("✅ Tunnel created!") - print(f"\n{server_info}\n") - - # Return info if requested (for testing/integration) - if return_info: - return server_info - - # Add project to path - sys.path.insert(0, str(config.project_root)) - - # Import and run server - print("🚀 Starting FastApps server...\n") - - try: - import uvicorn - from server.main import app - - uvicorn.run( - app, - host=config.host, - port=config.port, - log_level=config.log_level, - reload=config.auto_reload, - ) - - except ImportError as e: - raise DevServerError( - f"Failed to import server: {e}\n" - f"Make sure you're in a FastApps project with dependencies installed." - ) from e - - except KeyboardInterrupt: - print("\n⏹️ Server stopped") - return None - except NgrokError: - raise - except Exception as e: - raise DevServerError(f"Server error: {e}") from e - - -def start_dev_server_with_config(config: DevServerConfig) -> Optional[ServerInfo]: - """Start dev server with configuration object. - - Args: - config: DevServerConfig instance - - Returns: - ServerInfo if config includes return_info, otherwise None - - Example: - >>> from fastapps import start_dev_server_with_config, DevServerConfig - >>> config = DevServerConfig(port=8080, ngrok_token="token") - >>> start_dev_server_with_config(config) - """ - return start_dev_server( - port=config.port, - host=config.host, - ngrok_token=config.ngrok_token, - project_root=config.project_root, - auto_reload=config.auto_reload, - log_level=config.log_level, - ) - - -def get_server_info( - port: int = 8001, host: str = "0.0.0.0", ngrok_token: Optional[str] = None -) -> ServerInfo: - """Get server info without starting the server. - - This creates the ngrok tunnel and returns server URLs, - but does NOT start the FastApps server. - - Args: - port: Port for server - host: Host for server - ngrok_token: ngrok auth token - - Returns: - ServerInfo with URLs - - Raises: - NgrokError: If ngrok setup fails - - Example: - >>> from fastapps import get_server_info - >>> info = get_server_info(port=8080) - >>> print(f"Public URL: {info.public_url}") - """ - token = _get_ngrok_token(ngrok_token) - if not token: - raise DevServerError("ngrok token required") - - public_url = _setup_ngrok(token, port) - - return ServerInfo( - local_url=f"http://{host}:{port}", - public_url=public_url, - port=port, - host=host, - mcp_endpoint=public_url, - ) - - -# Convenience alias -run_dev_server = start_dev_server From 9abf505da2f0e1c4a925792ae257bf11dbaa4d88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EC=A7=80=ED=98=81?= Date: Tue, 28 Oct 2025 17:57:47 +0900 Subject: [PATCH 8/9] feat : replaced ngrok to cloudflare tunnel NOTE : guide to install cloudflared might be a better option spike : 1.1.2 - deploy command chores cli chore : updated log order Update ci.yml Update ci.yml Potential fix for code scanning alert no. 1: Reflected server-side cross-site scripting --- .env.example | 10 + .github/workflows/ci.yml | 10 +- .gitignore | 8 +- create_auth_test.sh | 31 +- docs/DEPLOY.md | 330 ++++++++++++++++++ fastapps/__init__.py | 12 +- fastapps/cli/commands/deploy.py | 377 +++++++++++++++++++++ fastapps/cli/commands/dev.py | 245 ++++++++------ fastapps/cli/main.py | 59 ++-- fastapps/core/server.py | 2 +- fastapps/deployer/__init__.py | 15 + fastapps/deployer/auth.py | 216 ++++++++++++ fastapps/deployer/client.py | 131 ++++++++ fastapps/deployer/packager.py | 264 +++++++++++++++ pyproject.toml | 5 +- setup.py | 5 +- uv.lock | 575 +++++++++++++++++++++++++++++++- 17 files changed, 2120 insertions(+), 175 deletions(-) create mode 100644 .env.example create mode 100644 docs/DEPLOY.md create mode 100644 fastapps/cli/commands/deploy.py create mode 100644 fastapps/deployer/__init__.py create mode 100644 fastapps/deployer/auth.py create mode 100644 fastapps/deployer/client.py create mode 100644 fastapps/deployer/packager.py diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..d96e1ea --- /dev/null +++ b/.env.example @@ -0,0 +1,10 @@ +# FastApps Configuration +# Copy this file to .env and configure your settings + +# Deployment Server URL +# Override the default deployment server URL +# Default: https://deploy.fastapps.org +# FASTAPPS_DEPLOY_URL=https://your-custom-deploy-server.com + +# Development Settings +# Add your development-specific environment variables here diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c8e73f1..f085748 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,11 +30,11 @@ jobs: enable-cache: true - name: Install dependencies - run: uv sync --dev + run: uv sync --all-extras --dev - name: Run tests with pytest run: | - pytest --verbose --cov=fastapps --cov-report=xml --cov-report=term + uv run --with pytest --with pytest-cov pytest --verbose --cov=fastapps --cov-report=xml --cov-report=term - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 @@ -60,10 +60,10 @@ jobs: enable-cache: true - name: Install dependencies - run: uv sync --dev + run: uv sync --all-extras --dev - name: Type check with mypy - run: uv run mypy fastapps --ignore-missing-imports + run: uv run --with mypy mypy fastapps --ignore-missing-imports continue-on-error: true build: @@ -86,7 +86,7 @@ jobs: run: uv build - name: Check package - run: uv run twine check dist/* + run: uv run --with twine twine check dist/* - name: Upload artifacts uses: actions/upload-artifact@v4 diff --git a/.gitignore b/.gitignore index d4ae839..c47d004 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,9 @@ .vscode/ *.egg-info/ -**/__pycache__/ \ No newline at end of file +**/__pycache__/ + +# Environment variables +.env + +# Deployment artifacts +.fastapps-deploy-*.tar.gz \ No newline at end of file diff --git a/create_auth_test.sh b/create_auth_test.sh index 7b77377..e7350d0 100755 --- a/create_auth_test.sh +++ b/create_auth_test.sh @@ -232,14 +232,14 @@ tools = auto_load_tools(build_results) # Auth0 Configuration AUTH0_DOMAIN = "${AUTH0_DOMAIN}" AUTH0_AUDIENCE = "${AUTH0_AUDIENCE}" -NGROK_URL = "https://REPLACE-ME.ngrok-free.app" # Update after starting ngrok +PUBLIC_URL = "https://REPLACE-ME.trycloudflare.com" # Will be auto-generated by Cloudflare Tunnel # Create server with OAuth server = WidgetMCPServer( name="fastapps-auth-test", widgets=tools, auth_issuer_url=f"https://{AUTH0_DOMAIN}", - auth_resource_server_url=f"{NGROK_URL}/mcp", + auth_resource_server_url=f"{PUBLIC_URL}/mcp", auth_audience=AUTH0_AUDIENCE, auth_required_scopes=["user"], ) @@ -255,10 +255,12 @@ if __name__ == "__main__": print(f"Widgets: {len(tools)}") print(f"Port: 8001") print() - print("⚠️ IMPORTANT: Start ngrok in another terminal:") - print(" ngrok http 8001") + print("⚠️ IMPORTANT: Use 'fastapps dev' to start with Cloudflare Tunnel") + print(" OR manually start cloudflared:") + print(" cloudflared tunnel --url http://localhost:8001") print() - print("📝 After getting ngrok URL, update NGROK_URL in server/main.py") + print("📝 After getting Cloudflare URL, update PUBLIC_URL in server/main.py") + print(" (e.g., https://random-name.trycloudflare.com)") print() print("✨ Test Widgets:") for tool in tools: @@ -268,7 +270,7 @@ if __name__ == "__main__": print() print("=" * 60) print() - + uvicorn.run(app, host="0.0.0.0", port=8001) EOF @@ -285,20 +287,27 @@ echo "📂 Project created at: $(pwd)" echo "" echo "🚀 Next Steps:" echo "" +echo "Option A - Using fastapps dev (Recommended):" +echo " cd $(pwd)" +echo " source venv/bin/activate" +echo " fastapps dev" +echo " # Cloudflare Tunnel URL will be displayed automatically" +echo "" +echo "Option B - Manual setup:" echo "1. Start the server (Terminal 1):" echo " cd $(pwd)" echo " source venv/bin/activate" echo " python server/main.py" echo "" -echo "2. Start ngrok (Terminal 2):" -echo " ngrok http 8001" +echo "2. Start Cloudflare Tunnel (Terminal 2):" +echo " cloudflared tunnel --url http://localhost:8001" echo "" -echo "3. Copy ngrok URL and update server/main.py:" -echo " NGROK_URL = \"https://YOUR-URL.ngrok-free.app\"" +echo "3. Copy Cloudflare URL and update server/main.py:" +echo " PUBLIC_URL = \"https://random-name.trycloudflare.com\"" echo "" echo "4. Restart server and test in ChatGPT:" echo " Settings → Connectors → Add Connector" -echo " URL: https://YOUR-URL.ngrok-free.app/mcp" +echo " URL: https://random-name.trycloudflare.com/mcp" echo "" echo "📖 Full guide: ../FastApps/OAUTH_TESTING_GUIDE.md" echo "" diff --git a/docs/DEPLOY.md b/docs/DEPLOY.md new file mode 100644 index 0000000..7efb173 --- /dev/null +++ b/docs/DEPLOY.md @@ -0,0 +1,330 @@ +# FastApps Deployment Server API Specification + +This document specifies the API that the FastApps deployment server must implement to support the `fastapps deploy` command. + +## Overview + +The deployment server provides OAuth 2.1 authentication with PKCE and accepts deployment artifacts for publishing FastApps projects. + +**Base URL**: `https://deploy.fastapps.org` (configurable) + +## Configuration + +### Environment Variables + +You can configure the deployment server URL using environment variables: + +```bash +# Option 1: Set environment variable directly +export FASTAPPS_DEPLOY_URL=https://your-custom-server.com + +# Option 2: Create .env file in your project root +echo "FASTAPPS_DEPLOY_URL=https://your-custom-server.com" > .env +``` + +**Priority Order:** +1. `--url` command-line flag (highest priority) +2. `FASTAPPS_DEPLOY_URL` environment variable +3. Saved URL in `~/.fastapps/config.json` +4. Default: `https://deploy.fastapps.org` (lowest priority) + +**Example `.env` file:** +```bash +# Deployment Configuration +FASTAPPS_DEPLOY_URL=https://deploy.example.com +``` + +> **Note:** The `.env` file is automatically gitignored to prevent accidental exposure of sensitive configuration. + +## Authentication + +### OAuth 2.1 with PKCE + +The server uses Clerk for OAuth authentication with PKCE (Proof Key for Code Exchange) flow. + +#### 1. Authorization Endpoint + +**Endpoint**: `GET /oauth/authorize` + +**Description**: Redirects user to Clerk authentication page. + +**Query Parameters**: +``` +client_id=fastapps-cli +response_type=code +redirect_uri=http://localhost:8765/callback +code_challenge= +code_challenge_method=S256 +scope=deploy +``` + +**Response**: HTTP 302 redirect to Clerk authorization page + +**Success**: Redirects to `redirect_uri` with authorization code: +``` +http://localhost:8765/callback?code= +``` + +**Error**: Redirects to `redirect_uri` with error: +``` +http://localhost:8765/callback?error=&error_description= +``` + +#### 2. Token Endpoint + +**Endpoint**: `POST /oauth/token` + +**Description**: Exchange authorization code for access token. + +**Request Headers**: +``` +Content-Type: application/x-www-form-urlencoded +``` + +**Request Body** (form-encoded): +``` +grant_type=authorization_code +client_id=fastapps-cli +code= +redirect_uri=http://localhost:8765/callback +code_verifier= +``` + +**Success Response** (200 OK): +```json +{ + "access_token": "eyJhbGciOiJIUzI1NiIs...", + "token_type": "Bearer", + "expires_in": 3600, + "scope": "deploy" +} +``` + +**Error Response** (400 Bad Request): +```json +{ + "error": "invalid_grant", + "error_description": "Invalid authorization code" +} +``` + +## Deployment API + +### Deploy Project + +**Endpoint**: `POST /deploy` + +**Description**: Upload and deploy a FastApps project artifact. + +**Authentication**: Required (Bearer token) + +**Request Headers**: +``` +Authorization: Bearer +Content-Type: multipart/form-data +``` + +**Request Body** (multipart/form-data): +``` +artifact: +``` + +**Artifact Format**: tar.gz archive containing: +- `.fastapps-manifest.json` - Deployment manifest (see Manifest Format below) +- `assets/` - Built widget HTML files +- `server/` - Python backend code +- `package.json` - Node.js dependencies +- `requirements.txt` - Python dependencies +- Optional: `README.md`, `.env.example` + +**Success Response** (200 OK): +```json +{ + "success": true, + "deployment_id": "dep_abc123xyz", + "url": "https://myproject-abc123.fastapps.app", + "message": "Deployment successful", + "timestamp": "2025-10-28T10:30:00Z" +} +``` + +**Error Responses**: + +**401 Unauthorized** - Invalid or expired token: +```json +{ + "error": "unauthorized", + "message": "Invalid or expired access token" +} +``` + +**400 Bad Request** - Invalid artifact: +```json +{ + "error": "invalid_artifact", + "message": "Missing required file: server/main.py" +} +``` + +**413 Payload Too Large** - Artifact too large: +```json +{ + "error": "payload_too_large", + "message": "Artifact must be less than 100MB", + "max_size_mb": 100 +} +``` + +**500 Internal Server Error** - Server error: +```json +{ + "error": "internal_error", + "message": "Deployment failed due to server error" +} +``` + +## Manifest Format + +The `.fastapps-manifest.json` file in the deployment artifact contains project metadata: + +```json +{ + "fastapps_version": "1.1.1", + "timestamp": "2025-10-28T10:30:00Z", + "project_name": "my-fastapps-project", + "widgets": ["weather", "calculator", "todo"], + "dependencies": { + "python": [ + "fastapps>=1.1.0", + "httpx>=0.25.0" + ], + "node": { + "react": "^18.3.1", + "react-dom": "^18.3.1", + "fastapps": "^1.0.0" + } + } +} +``` + +**Fields**: +- `fastapps_version`: Version of FastApps framework used +- `timestamp`: ISO 8601 timestamp of deployment creation +- `project_name`: Project identifier from package.json +- `widgets`: Array of widget identifiers found in assets/ +- `dependencies.python`: Array of Python package specifications from requirements.txt +- `dependencies.node`: Object of Node.js package name to version from package.json + +## Security Considerations + +### PKCE Implementation + +The client generates a cryptographically random `code_verifier` (64 URL-safe characters) and computes the `code_challenge` using SHA256: + +```python +code_verifier = secrets.token_urlsafe(64) +code_challenge = hashlib.sha256(code_verifier.encode()).digest().hex() +``` + +The server must: +1. Store the `code_challenge` with the authorization code +2. Validate the `code_verifier` on token exchange by computing SHA256 and comparing with stored challenge +3. Reject token exchange if verification fails + +### Token Validation + +The server must validate: +- Bearer token is present in Authorization header +- Token is valid and not expired +- Token has the `deploy` scope +- Token is associated with a valid Clerk user + +### Rate Limiting + +Recommended rate limits: +- OAuth authorization: 10 requests per minute per IP +- Token exchange: 5 requests per minute per client_id +- Deployment: 10 deployments per hour per user + +### Artifact Validation + +The server must validate: +- Artifact is valid tar.gz format +- Artifact size is under limit (recommended: 100MB) +- Manifest file exists and is valid JSON +- Required files exist: `server/main.py`, `package.json`, `requirements.txt` +- No malicious file paths (e.g., `../../../etc/passwd`) + +## Example Deployment Flow + +``` +1. User runs: fastapps deploy + +2. CLI validates project structure + +3. CLI builds widgets: npm run build + +4. CLI checks for saved token in ~/.fastapps/config.json + +5. If no token: + a. CLI generates PKCE pair + b. CLI starts local server on :8765 + c. CLI opens browser to /oauth/authorize + d. User authorizes in browser + e. Server redirects to localhost:8765/callback?code=... + f. CLI exchanges code for token via /oauth/token + g. CLI saves token to config.json + +6. CLI packages artifacts into tar.gz + +7. CLI uploads to POST /deploy with Bearer token + +8. Server validates token, unpacks artifact, deploys project + +9. Server responds with deployment URL + +10. CLI displays success message with URL +``` + +## Implementation Notes for Server + +### Clerk Configuration + +The server should configure Clerk with: +- Client ID: `fastapps-cli` +- Redirect URI: `http://localhost:8765/callback` +- Grant types: `authorization_code` +- Token endpoint authentication: `none` (PKCE provides security) +- Scopes: `deploy` + +### Deployment Process + +The server should: +1. Validate and extract tar.gz artifact +2. Read and validate `.fastapps-manifest.json` +3. Provision deployment environment (container, VM, etc.) +4. Install Python dependencies from `requirements.txt` +5. Install Node.js dependencies from `package.json` +6. Copy server code and assets to deployment environment +7. Configure environment variables (if needed) +8. Start FastApps server via `uvicorn server.main:app` +9. Configure reverse proxy/load balancer +10. Generate unique deployment URL +11. Store deployment metadata +12. Return deployment URL to client + +### Deployment URL Format + +Recommended URL format: +``` +https://{project_name}-{short_id}.fastapps.app +``` + +Example: +``` +https://my-widgets-a1b2c3.fastapps.app +``` + +## Version History + +- **v1.0** (2025-10-28): Initial API specification diff --git a/fastapps/__init__.py b/fastapps/__init__.py index 11e6bfe..5d457f4 100644 --- a/fastapps/__init__.py +++ b/fastapps/__init__.py @@ -20,7 +20,7 @@ async def execute(self, input_data) -> Dict[str, Any]: from importlib.metadata import version __version__ = version("fastapps") except Exception: - __version__ = "1.1.1" # Fallback version + __version__ = "1.1.2" # Fallback version __author__ = "FastApps Team" @@ -56,14 +56,4 @@ async def execute(self, input_data) -> Dict[str, Any]: "WidgetBuildResult", "Field", "ConfigDict", - # Dev server API - "start_dev_server", - "start_dev_server_with_config", - "get_server_info", - "run_dev_server", - "DevServerConfig", - "ServerInfo", - "DevServerError", - "ProjectNotFoundError", - "NgrokError", ] + _auth_exports diff --git a/fastapps/cli/commands/deploy.py b/fastapps/cli/commands/deploy.py new file mode 100644 index 0000000..f9d26bd --- /dev/null +++ b/fastapps/cli/commands/deploy.py @@ -0,0 +1,377 @@ +"""Deploy command for publishing FastApps projects.""" + +import asyncio +import json +import os +import subprocess +from pathlib import Path + +from dotenv import load_dotenv +from rich.console import Console +from rich.panel import Panel +from rich.progress import Progress, SpinnerColumn, TextColumn +from rich.table import Table + +# Load environment variables from .env file +load_dotenv() + +console = Console() + +# Default deployment server URL (can be overridden by FASTAPPS_DEPLOY_URL env var) +DEFAULT_DEPLOY_URL = os.getenv("FASTAPPS_DEPLOY_URL", "https://deploy.fastapps.org") + + +def get_config_dir(): + """Get FastApps config directory.""" + config_dir = Path.home() / ".fastapps" + config_dir.mkdir(exist_ok=True) + return config_dir + + +def get_config_file(): + """Get config file path.""" + return get_config_dir() / "config.json" + + +def load_config(): + """Load configuration from file.""" + config_file = get_config_file() + if config_file.exists(): + try: + with open(config_file, "r") as f: + return json.load(f) + except Exception as e: + console.print(f"[yellow]Warning: Could not load config: {e}[/yellow]") + return {} + + +def save_config(config): + """Save configuration to file.""" + import os + + config_file = get_config_file() + try: + with open(config_file, "w") as f: + json.dump(config, f, indent=2) + + # Set restrictive permissions (owner read/write only) + os.chmod(config_file, 0o600) + return True + except Exception as e: + console.print(f"[red]Error: Could not save config: {e}[/red]") + return False + + +def get_deploy_url() -> str: + """ + Get deployment server URL from environment variable or default. + + Returns: + Deployment server URL + """ + return DEFAULT_DEPLOY_URL + + +def get_deploy_token() -> str: + """ + Get stored deployment token. + + Returns: + Access token or None + """ + config = load_config() + return config.get("deploy_token") + + +def save_deploy_token(token: str): + """ + Save deployment token to config. + + Args: + token: Access token to save + """ + config = load_config() + config["deploy_token"] = token + save_config(config) + + +async def async_deploy( + skip_build: bool = False, + skip_confirmation: bool = False, +): + """ + Async deployment workflow. + + Args: + skip_build: Skip widget build step + skip_confirmation: Skip confirmation prompt + + Returns: + True if successful, False otherwise + """ + deploy_url = get_deploy_url() + from ..deployer import ArtifactPackager, ClerkOAuthAuthenticator, DeployClient + + project_root = Path.cwd() + + # Step 1: Validate project structure + console.print("\n[cyan]Validating project structure...[/cyan]") + try: + packager = ArtifactPackager(project_root) + packager._validate_project() + console.print("[green]✓ Project structure valid[/green]") + except FileNotFoundError as e: + console.print(f"[red]✗ {e}[/red]") + return False + + # Step 2: Build widgets + if not skip_build: + console.print("\n[cyan]Building widgets...[/cyan]") + try: + result = subprocess.run( + ["npm", "run", "build"], + capture_output=True, + text=True, + check=True, + ) + console.print("[green]✓ Widgets built successfully[/green]") + except subprocess.CalledProcessError as e: + console.print(f"[red]✗ Build failed: {e.stderr}[/red]") + console.print( + "[yellow]Tip: Run 'npm install' if packages are not installed[/yellow]" + ) + return False + except FileNotFoundError: + console.print("[red]✗ npm not found[/red]") + return False + + # Step 3: Show deployment summary + console.print("\n[bold cyan]Deployment Summary[/bold cyan]") + + summary_table = Table(show_header=False, box=None) + summary_table.add_column("Key", style="cyan") + summary_table.add_column("Value", style="white") + + # Get project info + try: + package_json = json.loads((project_root / "package.json").read_text()) + project_name = package_json.get("name", "unknown") + except Exception: + project_name = "unknown" + + # Count widgets + assets_dir = project_root / "assets" + widget_count = len(list(assets_dir.glob("*.html"))) if assets_dir.exists() else 0 + + summary_table.add_row("Project", project_name) + summary_table.add_row("Widgets", str(widget_count)) + summary_table.add_row("Deploy URL", deploy_url) + + console.print(summary_table) + console.print() + + # Step 4: Confirmation + if not skip_confirmation: + confirm = console.input("[bold]Deploy to production? (yes/no):[/bold] ") + if confirm.lower() not in ["yes", "y"]: + console.print("[yellow]Deployment cancelled[/yellow]") + return False + + # Step 5: Authenticate + console.print("\n[cyan]Authenticating...[/cyan]") + token = get_deploy_token() + + if not token: + console.print( + "[yellow]No authentication token found. Starting OAuth flow...[/yellow]" + ) + console.print( + "[dim]Your browser will open for authentication. " + "Please authorize FastApps CLI.[/dim]\n" + ) + + try: + authenticator = ClerkOAuthAuthenticator(deploy_url) + token = await authenticator.authenticate() + save_deploy_token(token) + console.print("[green]✓ Authentication successful[/green]") + except ConnectionError as e: + console.print(f"\n[red]✗ Connection Error[/red]\n") + console.print(f"[yellow]Cannot connect to deployment server:[/yellow]") + return False + except TimeoutError as e: + console.print(f"\n[red]✗ Authentication Timeout[/red]\n") + console.print( + "[yellow]Authentication took too long (5 minutes limit)[/yellow]\n" + ) + console.print("[dim]Please try again.[/dim]") + return False + except RuntimeError as e: + error_msg = str(e) + if "OAuth error" in error_msg: + console.print(f"\n[red]✗ OAuth Error[/red]\n") + console.print(f"[yellow]{error_msg}[/yellow]\n") + console.print("[dim]Please contact your deployment server administrator.[/dim]") + elif "timed out" in error_msg.lower(): + console.print(f"\n[red]✗ Authentication Timeout[/red]\n") + console.print( + "[yellow]Authentication took too long. Please try again.[/yellow]" + ) + else: + console.print(f"\n[red]✗ Authentication Failed[/red]\n") + console.print(f"[yellow]{error_msg}[/yellow]") + return False + except Exception as e: + console.print(f"\n[red]✗ Unexpected Error[/red]\n") + console.print(f"[yellow]{type(e).__name__}: {e}[/yellow]\n") + console.print( + "[dim]If this persists, please report at: https://github.com/DooiLabs/FastApps/issues[/dim]" + ) + return False + else: + console.print("[green]✓ Using saved authentication token[/green]") + + # Step 6: Package artifacts + console.print("\n[cyan]Packaging deployment artifacts...[/cyan]") + try: + with Progress( + SpinnerColumn(), + TextColumn("[progress.description]{task.description}"), + console=console, + ) as progress: + progress.add_task("Creating tarball...", total=None) + tarball_path = packager.package() + + # Show tarball size + tarball_size_mb = tarball_path.stat().st_size / (1024 * 1024) + console.print( + f"[green]✓ Package created ({tarball_size_mb:.2f} MB)[/green]" + ) + except Exception as e: + console.print(f"[red]✗ Packaging failed: {e}[/red]") + return False + + # Step 7: Upload to server + console.print("\n[cyan]Uploading to deployment server...[/cyan]") + try: + async with DeployClient(deploy_url, token) as client: + with Progress( + SpinnerColumn(), + TextColumn("[progress.description]{task.description}"), + console=console, + ) as progress: + progress.add_task("Uploading artifacts...", total=None) + result = await client.deploy(tarball_path) + + # Clean up tarball + tarball_path.unlink(missing_ok=True) + + if result.success: + console.print("[green]✓ Deployment successful![/green]\n") + + # Display deployment URL + success_panel = Panel( + f"[bold green]Deployment URL:[/bold green]\n" + f"[link={result.deployment_url}]{result.deployment_url}[/link]\n\n" + f"[dim]Deployment ID: {result.deployment_id}[/dim]", + title="🚀 Deployment Complete", + border_style="green", + ) + console.print(success_panel) + return True + else: + # Format error message + error_msg = result.error or "Unknown error" + + if "Authentication" in error_msg or "401" in error_msg: + console.print(f"\n[red]✗ Authentication Failed[/red]\n") + console.print("[yellow]Your access token is invalid or expired.[/yellow]\n") + console.print("[dim]Clearing saved token...[/dim]") + + # Clear token + config = load_config() + if "deploy_token" in config: + del config["deploy_token"] + save_config(config) + + console.print("[green]Token cleared.[/green]") + console.print("\n[cyan]Please run 'fastapps deploy' again to re-authenticate.[/cyan]") + + elif "Network" in error_msg or "Connection" in error_msg: + console.print(f"\n[red]✗ Connection Error[/red]\n") + console.print(f"[yellow]Cannot upload to deployment server:[/yellow]") + console.print(f"[white]{deploy_url}[/white]\n") + console.print(f"[dim]Error: {error_msg}[/dim]\n") + console.print("[cyan]Possible solutions:[/cyan]") + console.print(" • Check your internet connection") + console.print(" • Verify the server URL is correct") + console.print(" • Try again in a few moments") + + elif "Invalid deployment package" in error_msg: + console.print(f"\n[red]✗ Invalid Deployment Package[/red]\n") + console.print(f"[yellow]{error_msg}[/yellow]\n") + console.print("[dim]Please ensure all required files are present and valid.[/dim]") + + elif "Missing required" in error_msg: + console.print(f"\n[red]✗ Missing Required Files[/red]\n") + console.print(f"[yellow]{error_msg}[/yellow]\n") + console.print("[dim]Check that your project structure is complete.[/dim]") + + else: + console.print(f"\n[red]✗ Deployment Failed[/red]\n") + console.print(f"[yellow]{error_msg}[/yellow]") + + return False + + except ConnectionError as e: + console.print(f"\n[red]✗ Connection Error[/red]\n") + console.print(f"[yellow]Cannot connect to deployment server:[/yellow]") + console.print(f"[white]{deploy_url}[/white]\n") + console.print(f"[dim]Error: {e}[/dim]\n") + console.print("[cyan]Please check:[/cyan]") + console.print(" • Your internet connection") + console.print(" • Server availability") + console.print(" • Server URL is correct") + return False + + except TimeoutError as e: + console.print(f"\n[red]✗ Upload Timeout[/red]\n") + console.print("[yellow]Upload took too long (5 minutes limit)[/yellow]\n") + console.print("[dim]The deployment package may be too large or connection too slow.[/dim]") + return False + + except Exception as e: + console.print(f"\n[red]✗ Upload Failed[/red]\n") + console.print(f"[yellow]{type(e).__name__}: {e}[/yellow]\n") + console.print( + "[dim]If this persists, please report at: https://github.com/DooiLabs/FastApps/issues[/dim]" + ) + return False + + +def deploy_command( + yes: bool = False, + no_build: bool = False, +): + """ + Deploy FastApps project to production. + + Args: + yes: Skip confirmation prompt + no_build: Skip widget build step + """ + # Check if in FastApps project + if not Path("server/main.py").exists(): + console.print("[red]Error: Not in a FastApps project directory[/red]") + console.print( + "[yellow]Run this command from your project root (where server/main.py exists)[/yellow]" + ) + return False + + # Run async deployment + try: + success = asyncio.run(async_deploy(no_build, yes)) + return success + except KeyboardInterrupt: + console.print("\n[yellow]Deployment cancelled by user[/yellow]") + return False diff --git a/fastapps/cli/commands/dev.py b/fastapps/cli/commands/dev.py index 4b9c9b2..bc94f7e 100644 --- a/fastapps/cli/commands/dev.py +++ b/fastapps/cli/commands/dev.py @@ -1,8 +1,11 @@ -"""Development server command with ngrok integration.""" +"""Development server command with Cloudflare Tunnel integration.""" import json +import platform +import re import subprocess import sys +import time from pathlib import Path from rich.console import Console @@ -12,84 +15,107 @@ console = Console() -def get_config_dir(): - """Get FastApps config directory.""" - config_dir = Path.home() / ".fastapps" - config_dir.mkdir(exist_ok=True) - return config_dir - - -def get_config_file(): - """Get config file path.""" - return get_config_dir() / "config.json" +def check_cloudflared_installed() -> bool: + """Check if cloudflared is installed.""" + try: + subprocess.run( + ["cloudflared", "--version"], + capture_output=True, + check=True, + ) + return True + except (FileNotFoundError, subprocess.CalledProcessError): + return False -def load_config(): - """Load configuration from file.""" - config_file = get_config_file() - if config_file.exists(): - try: - with open(config_file, "r") as f: - return json.load(f) - except Exception as e: - console.print(f"[yellow]Warning: Could not load config: {e}[/yellow]") - return {} +def install_cloudflared() -> bool: + """Install cloudflared automatically.""" + console.print("\n[cyan]Installing cloudflared...[/cyan]") + system = platform.system() -def save_config(config): - """Save configuration to file.""" - config_file = get_config_file() try: - with open(config_file, "w") as f: - json.dump(config, f, indent=2) + if system == "Darwin": # macOS + console.print("[dim]Using Homebrew...[/dim]") + subprocess.run( + ["brew", "install", "cloudflare/cloudflare/cloudflared"], + check=True, + ) + elif system == "Linux": + console.print("[dim]Downloading binary...[/dim]") + arch = platform.machine() + if arch == "x86_64": + arch = "amd64" + elif arch == "aarch64": + arch = "arm64" + + url = f"https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-{arch}" + + subprocess.run(["wget", url, "-O", "/tmp/cloudflared"], check=True) + subprocess.run(["chmod", "+x", "/tmp/cloudflared"], check=True) + subprocess.run(["sudo", "mv", "/tmp/cloudflared", "/usr/local/bin/"], check=True) + elif system == "Windows": + console.print("[dim]Downloading Windows binary...[/dim]") + subprocess.run([ + "powershell", "-Command", + "Invoke-WebRequest -Uri 'https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-windows-amd64.exe' -OutFile 'C:\\Windows\\System32\\cloudflared.exe'" + ], check=True) + else: + console.print(f"[red]Unsupported platform: {system}[/red]") + return False + + console.print("[green]✓ cloudflared installed successfully[/green]\n") return True - except Exception as e: - console.print(f"[red]Error: Could not save config: {e}[/red]") + + except subprocess.CalledProcessError as e: + console.print(f"[red]Installation failed: {e}[/red]") + console.print("\n[yellow]Manual installation:[/yellow]") + console.print(" macOS: brew install cloudflare/cloudflare/cloudflared") + console.print(" Linux: https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/") + console.print(" Windows: https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/") return False -def get_ngrok_token(): - """Get ngrok auth token from config or prompt user.""" - config = load_config() +def start_cloudflare_tunnel(port: int) -> tuple[subprocess.Popen, str]: + """ + Start Cloudflare Tunnel and return process and public URL. - # Check if token exists - if "ngrok_token" in config and config["ngrok_token"]: - return config["ngrok_token"] + Returns: + (process, public_url) + """ + console.print(f"[cyan]Starting Cloudflare Tunnel on port {port}...[/cyan]") - # Prompt for token - console.print("\n[cyan]ngrok authentication required[/cyan]") - console.print( - "Get your free auth token at: [link]https://dashboard.ngrok.com/get-started/your-authtoken[/link]\n" + # Start cloudflared tunnel + process = subprocess.Popen( + ["cloudflared", "tunnel", "--url", f"http://localhost:{port}"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + bufsize=1, ) - token = console.input("[bold]Enter your ngrok auth token:[/bold] ").strip() - - if not token: - console.print("[red]Error: Token is required[/red]") - return None - - # Save token - config["ngrok_token"] = token - if save_config(config): - console.print("[green]Token saved successfully[/green]\n") - - return token + # Wait for tunnel URL + public_url = None + for _ in range(30): # 30 seconds timeout + if process.stderr: + line = process.stderr.readline() + if line: + # Look for URL in output: https://random-name.trycloudflare.com + match = re.search(r'https://[a-zA-Z0-9-]+\.trycloudflare\.com', line) + if match: + public_url = match.group(0) + break + time.sleep(0.1) + if not public_url: + process.terminate() + raise RuntimeError("Failed to get tunnel URL from cloudflared") -def set_ngrok_auth(token): - """Set ngrok auth token.""" - try: - from pyngrok import ngrok - - ngrok.set_auth_token(token) - return True - except Exception as e: - console.print(f"[red]Error setting ngrok auth token: {e}[/red]") - return False + return process, public_url def start_dev_server(port=8001, host="0.0.0.0"): - """Start development server with ngrok tunnel.""" + """Start development server with Cloudflare Tunnel.""" # Check if we're in a FastApps project if not Path("server/main.py").exists(): @@ -103,43 +129,65 @@ def start_dev_server(port=8001, host="0.0.0.0"): console.print("[cyan]Building widgets...[/cyan]") try: subprocess.run(["npm", "run", "build"], check=True, capture_output=True) - console.print("[green]Widgets built[/green]\n") - except Exception: + console.print("[green]✓ Widgets built[/green]\n") + except subprocess.CalledProcessError: console.print("[yellow]Build failed. Make sure npm packages are installed[/yellow]") return False + except FileNotFoundError: + console.print("[red]npm not found[/red]") + return False - # Get ngrok token - console.print("[cyan]Setting up ngrok tunnel...[/cyan]") - token = get_ngrok_token() + # Check if cloudflared is installed + if not check_cloudflared_installed(): + console.print("[yellow]cloudflared not found[/yellow]") + if not install_cloudflared(): + return False - if not token: + # Start Cloudflare Tunnel + try: + tunnel_process, public_url = start_cloudflare_tunnel(port) + except RuntimeError as e: + console.print(f"[red]Failed to start tunnel: {e}[/red]") return False - # Set ngrok auth - if not set_ngrok_auth(token): - return False + console.print() + + # Import and start server (shows uvicorn boot logs first) + console.print("[cyan]Starting FastApps server...[/cyan]\n") try: import uvicorn - from pyngrok import ngrok + import asyncio # Import project server sys.path.insert(0, str(Path.cwd())) + from server.main import app - # Start ngrok tunnel - console.print(f"[cyan]Starting ngrok tunnel on port {port}...[/cyan]") - public_url = ngrok.connect(port, bind_tls=True) - ngrok_url = public_url.public_url + # Create server config + config = uvicorn.Config(app, host=host, port=port, log_level="info") + server = uvicorn.Server(config) - console.print() + # Start server in background thread to show info panel + import threading + import time + + def run_server(): + asyncio.run(server.serve()) + + server_thread = threading.Thread(target=run_server, daemon=True) + server_thread.start() - # Display connection info + # Wait a moment for server to start and show logs + time.sleep(1) + + # Now display connection info (will stay visible above ongoing logs) + console.print() table = Table(title="FastApps Development Server", title_style="bold green") table.add_column("Type", style="cyan", no_wrap=True) table.add_column("URL", style="white") table.add_row("Local", f"http://{host}:{port}") - table.add_row("Public (ngrok)", f"[bold green]{ngrok_url}[/bold green]") + table.add_row("Public", f"[bold green]{public_url}[/bold green]") console.print(table) console.print() @@ -147,7 +195,7 @@ def start_dev_server(port=8001, host="0.0.0.0"): # Display MCP endpoint info mcp_panel = Panel( f"[bold]MCP Server Endpoint:[/bold]\n" - f"[green]{ngrok_url}[/green]\n\n" + f"[green]{public_url}[/green]\n\n" f"[dim]Use this URL in your MCP client configuration[/dim]", title="Model Context Protocol", border_style="blue", @@ -157,47 +205,28 @@ def start_dev_server(port=8001, host="0.0.0.0"): console.print("[yellow]Press Ctrl+C to stop the server[/yellow]\n") - # Import and run server - console.print("[cyan]Starting FastApps server...[/cyan]\n") - - from server.main import app - - # Run uvicorn - uvicorn.run(app, host=host, port=port, log_level="info") + # Keep main thread alive + server_thread.join() except KeyboardInterrupt: console.print("\n[yellow]Shutting down server...[/yellow]") try: - ngrok.disconnect(public_url.public_url) + tunnel_process.terminate() + tunnel_process.wait(timeout=5) console.print("[green]Server stopped[/green]") except Exception: pass return True except ImportError as e: - if "pyngrok" in str(e): - console.print("[red]Error: pyngrok not installed[/red]") - console.print("[yellow]Install it with:[/yellow]") - console.print(" pip install pyngrok") - console.print(" # Or: uv pip install pyngrok") - else: - console.print(f"[red]Error: Could not import server: {e}[/red]") - console.print( - "[yellow]Make sure you're in a FastApps project and dependencies are installed[/yellow]" - ) + console.print(f"[red]Error: Could not import server: {e}[/red]") + console.print( + "[yellow]Make sure you're in a FastApps project and dependencies are installed[/yellow]" + ) + tunnel_process.terminate() return False except Exception as e: console.print(f"[red]Error starting server: {e}[/red]") + tunnel_process.terminate() return False - - -def reset_ngrok_token(): - """Reset stored ngrok token.""" - config = load_config() - if "ngrok_token" in config: - del config["ngrok_token"] - save_config(config) - console.print("[green]ngrok token cleared[/green]") - else: - console.print("[yellow]No token stored[/yellow]") diff --git a/fastapps/cli/main.py b/fastapps/cli/main.py index ac96801..7beec14 100644 --- a/fastapps/cli/main.py +++ b/fastapps/cli/main.py @@ -4,7 +4,8 @@ from rich.console import Console from .commands.create import create_widget -from .commands.dev import reset_ngrok_token, start_dev_server +from .commands.deploy import deploy_command +from .commands.dev import start_dev_server from .commands.init import init_project from .commands.use import use_integration @@ -97,17 +98,20 @@ def create(widget_name, auth, public, optional_auth, scopes): "--host", default="0.0.0.0", help="Host to bind the server to (default: 0.0.0.0)" ) def dev(port, host): - """Start development server with ngrok tunnel. + """Start development server with Cloudflare Tunnel. This command will: - 1. Prompt for ngrok auth token (first time only) - 2. Start a public ngrok tunnel - 3. Launch the FastApps development server - 4. Display public and local URLs + 1. Build widgets + 2. Install cloudflared if needed (automatic, no token required) + 3. Start a public Cloudflare Tunnel + 4. Launch the FastApps development server + 5. Display public and local URLs Example: fastapps dev fastapps dev --port 8080 + + Note: Uses Cloudflare Tunnel (free, unlimited, no sign-up required) """ start_dev_server(port=port, host=host) @@ -121,6 +125,36 @@ def build(): console.print(" npm run build") +@cli.command() +@click.option("--yes", "-y", is_flag=True, help="Skip confirmation prompt") +@click.option("--no-build", is_flag=True, help="Skip widget build step") +def deploy(yes, no_build): + """Deploy your FastApps project to production. + + This command will: + 1. Validate project structure + 2. Build widgets (unless --no-build) + 3. Authenticate with OAuth (if needed) + 4. Package artifacts (server, widgets, configs) + 5. Upload to deployment server + 6. Display deployment URL + + Examples: + fastapps deploy + fastapps deploy --yes + fastapps deploy --no-build --yes + + Configuration: + Set FASTAPPS_DEPLOY_URL in .env file to configure deployment server. + Default: https://deploy.fastapps.org + + Authentication: + First time: Opens browser for OAuth authentication + Subsequent: Uses saved token from ~/.fastapps/config.json + """ + deploy_command(yes=yes, no_build=no_build) + + @cli.command() def auth_info(): """Show authentication setup information.""" @@ -168,19 +202,6 @@ def auth_info(): console.print() -@cli.command() -def reset_token(): - """Reset stored ngrok auth token. - - Use this command if you want to change your ngrok auth token. - The next time you run 'fastapps dev', you'll be prompted for a new token. - - Example: - fastapps reset-token - """ - reset_ngrok_token() - - @cli.command() @click.argument("integration_name") def use(integration_name): diff --git a/fastapps/core/server.py b/fastapps/core/server.py index 5d6012c..e8ffd67 100644 --- a/fastapps/core/server.py +++ b/fastapps/core/server.py @@ -10,7 +10,7 @@ from importlib.metadata import version __version__ = version("fastapps") except Exception: - __version__ = "1.1.1" # Fallback version + __version__ = "1.1.2" # Fallback version # Auth imports (optional, graceful degradation if not available) try: diff --git a/fastapps/deployer/__init__.py b/fastapps/deployer/__init__.py new file mode 100644 index 0000000..38a4f50 --- /dev/null +++ b/fastapps/deployer/__init__.py @@ -0,0 +1,15 @@ +"""FastApps Deployment Module + +Handles OAuth authentication, artifact packaging, and deployment to remote servers. +""" + +from .auth import ClerkOAuthAuthenticator +from .client import DeployClient, DeploymentResult +from .packager import ArtifactPackager + +__all__ = [ + "ClerkOAuthAuthenticator", + "DeployClient", + "DeploymentResult", + "ArtifactPackager", +] diff --git a/fastapps/deployer/auth.py b/fastapps/deployer/auth.py new file mode 100644 index 0000000..e2dc064 --- /dev/null +++ b/fastapps/deployer/auth.py @@ -0,0 +1,216 @@ +"""OAuth 2.1 PKCE authentication for FastApps deployment.""" + +import asyncio +import hashlib +import secrets +import webbrowser +from typing import Optional, Tuple +from urllib.parse import parse_qs, urlencode, urlparse +import html +import httpx +from aiohttp import web + + +class ClerkOAuthAuthenticator: + """Handles OAuth 2.1 PKCE flow for Clerk authentication.""" + + def __init__(self, deploy_url: str): + """ + Initialize OAuth authenticator. + + Args: + deploy_url: Base URL of deployment server (e.g., https://deploy.fastapps.org) + """ + self.deploy_url = deploy_url.rstrip("/") + self.auth_url = f"{self.deploy_url}/oauth/authorize" + self.token_url = f"{self.deploy_url}/oauth/token" + self.redirect_uri = "http://localhost:8765/callback" + self.client_id = "fastapps-cli" + + def _generate_pkce_pair(self) -> Tuple[str, str]: + """ + Generate PKCE code verifier and challenge. + + Returns: + Tuple of (code_verifier, code_challenge) + """ + import base64 + + # Generate random code verifier (43-128 characters) + code_verifier = secrets.token_urlsafe(64) + + # Generate SHA256 code challenge (base64url-encoded per RFC 7636) + code_challenge = ( + base64.urlsafe_b64encode( + hashlib.sha256(code_verifier.encode()).digest() + ) + .decode() + .rstrip("=") + ) + + return code_verifier, code_challenge + + async def authenticate(self) -> str: + """ + Perform OAuth PKCE flow to get access token. + + Returns: + Access token string + + Raises: + RuntimeError: If authentication fails + """ + # Generate PKCE pair + code_verifier, code_challenge = self._generate_pkce_pair() + + # Start local callback server + auth_code_future = asyncio.Future() + app = self._create_callback_app(auth_code_future) + runner = web.AppRunner(app) + await runner.setup() + site = web.TCPSite(runner, "localhost", 8765) + await site.start() + + try: + # Build authorization URL + auth_params = { + "client_id": self.client_id, + "response_type": "code", + "redirect_uri": self.redirect_uri, + "code_challenge": code_challenge, + "code_challenge_method": "S256", + "scope": "deploy", + } + auth_full_url = f"{self.auth_url}?{urlencode(auth_params)}" + + # Open browser for user authorization + webbrowser.open(auth_full_url) + + # Wait for callback with timeout + try: + auth_code = await asyncio.wait_for(auth_code_future, timeout=300) + except asyncio.TimeoutError: + raise RuntimeError("Authentication timed out (5 minutes)") + + # Exchange authorization code for access token + access_token = await self._exchange_code_for_token( + auth_code, code_verifier + ) + + return access_token + + finally: + await runner.cleanup() + + def _create_callback_app(self, auth_code_future: asyncio.Future) -> web.Application: + """ + Create aiohttp app for OAuth callback. + + Args: + auth_code_future: Future to resolve with auth code + + Returns: + aiohttp Application + """ + + async def callback_handler(request: web.Request) -> web.Response: + """Handle OAuth callback.""" + # Parse query parameters + query_params = request.rel_url.query + + # Check for error + if "error" in query_params: + error = query_params.get("error", "unknown_error") + error_desc = query_params.get("error_description", "No description") + if not auth_code_future.done(): + auth_code_future.set_exception( + RuntimeError(f"OAuth error: {error} - {error_desc}") + ) + return web.Response( + text=f"

Authentication Failed

{html.escape(error_desc)}

", + content_type="text/html", + ) + + # Extract authorization code + auth_code = query_params.get("code") + if not auth_code: + if not auth_code_future.done(): + auth_code_future.set_exception( + RuntimeError("No authorization code received") + ) + return web.Response( + text="

Error

No authorization code received

", + content_type="text/html", + ) + + # Resolve future with auth code (avoid race condition) + if not auth_code_future.done(): + auth_code_future.set_result(auth_code) + + # Return success page + return web.Response( + text=""" + + FastApps - Authentication Success + +

✓ Authentication Successful

+

You can close this window and return to the terminal.

+ + + """, + content_type="text/html", + ) + + app = web.Application() + app.router.add_get("/callback", callback_handler) + return app + + async def _exchange_code_for_token( + self, auth_code: str, code_verifier: str + ) -> str: + """ + Exchange authorization code for access token. + + Args: + auth_code: Authorization code from OAuth callback + code_verifier: PKCE code verifier + + Returns: + Access token + + Raises: + RuntimeError: If token exchange fails + """ + async with httpx.AsyncClient() as client: + try: + response = await client.post( + self.token_url, + data={ + "grant_type": "authorization_code", + "client_id": self.client_id, + "code": auth_code, + "redirect_uri": self.redirect_uri, + "code_verifier": code_verifier, + }, + headers={"Content-Type": "application/x-www-form-urlencoded"}, + ) + + if response.status_code != 200: + raise RuntimeError( + f"Token exchange failed: {response.status_code} - {response.text}" + ) + + token_data = response.json() + access_token = token_data.get("access_token") + + if not access_token: + raise RuntimeError("No access token in response") + + return access_token + + except httpx.ConnectError as e: + raise ConnectionError(f"Cannot connect to server: {e}") + except httpx.TimeoutException as e: + raise TimeoutError(f"Request timed out: {e}") + except httpx.RequestError as e: + raise RuntimeError(f"Token exchange request failed: {e}") diff --git a/fastapps/deployer/client.py b/fastapps/deployer/client.py new file mode 100644 index 0000000..b50b6c5 --- /dev/null +++ b/fastapps/deployer/client.py @@ -0,0 +1,131 @@ +"""HTTP client for FastApps deployment API.""" + +from dataclasses import dataclass +from pathlib import Path +from typing import Optional + +import httpx + + +@dataclass +class DeploymentResult: + """Result of a deployment operation.""" + + success: bool + deployment_url: Optional[str] = None + deployment_id: Optional[str] = None + message: Optional[str] = None + error: Optional[str] = None + + +class DeployClient: + """Client for interacting with FastApps deployment server.""" + + def __init__(self, base_url: str, access_token: str): + """ + Initialize deployment client. + + Args: + base_url: Base URL of deployment server + access_token: OAuth access token + """ + self.base_url = base_url.rstrip("/") + self.access_token = access_token + self._client = None + + @property + def client(self) -> httpx.AsyncClient: + """Lazy-initialized HTTP client.""" + if self._client is None: + self._client = httpx.AsyncClient(timeout=300.0) # 5 minute timeout + return self._client + + async def deploy(self, tarball_path: Path) -> DeploymentResult: + """ + Deploy artifact to server. + + Args: + tarball_path: Path to deployment tarball + + Returns: + DeploymentResult with deployment information + + Raises: + RuntimeError: If deployment fails + """ + try: + # Read tarball content to avoid file handle issues + with open(tarball_path, "rb") as f: + file_content = f.read() + + files = {"artifact": ("deployment.tar.gz", file_content, "application/gzip")} + + # Send deployment request + response = await self.client.post( + f"{self.base_url}/deploy", + files=files, + headers={"Authorization": f"Bearer {self.access_token}"}, + ) + + # Handle response + if response.status_code == 200: + data = response.json() + return DeploymentResult( + success=True, + deployment_url=data.get("url"), + deployment_id=data.get("deployment_id"), + message=data.get("message", "Deployment successful"), + ) + elif response.status_code == 401: + return DeploymentResult( + success=False, + error="Authentication failed. Please run 'fastapps deploy' again to re-authenticate.", + ) + elif response.status_code == 400: + # Fix: Check if content-type contains json (handles charset) + content_type = response.headers.get("content-type", "") + data = response.json() if "application/json" in content_type else {} + error_msg = data.get("error", response.text) + return DeploymentResult( + success=False, + error=f"Invalid deployment package: {error_msg}", + ) + else: + return DeploymentResult( + success=False, + error=f"Deployment failed: {response.status_code} - {response.text}", + ) + + except httpx.ConnectError as e: + return DeploymentResult( + success=False, + error=f"Connection error: Cannot reach deployment server", + ) + except httpx.TimeoutException as e: + return DeploymentResult( + success=False, + error=f"Network timeout: Request took too long", + ) + except httpx.RequestError as e: + return DeploymentResult( + success=False, + error=f"Network error during deployment: {e}", + ) + except Exception as e: + return DeploymentResult( + success=False, + error=f"Unexpected error: {e}", + ) + + async def close(self): + """Close HTTP client.""" + if self._client is not None: + await self._client.aclose() + + async def __aenter__(self): + """Context manager entry.""" + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + """Context manager exit.""" + await self.close() diff --git a/fastapps/deployer/packager.py b/fastapps/deployer/packager.py new file mode 100644 index 0000000..6100344 --- /dev/null +++ b/fastapps/deployer/packager.py @@ -0,0 +1,264 @@ +"""Artifact packaging for FastApps deployment.""" + +import json +import tarfile +import tempfile +from datetime import datetime +from pathlib import Path +from typing import Dict, List + +# Get version from package metadata +try: + from importlib.metadata import version + + __version__ = version("fastapps") +except Exception: + __version__ = "1.1.2" # Fallback version + + +class ArtifactPackager: + """Packages FastApps project for deployment.""" + + def __init__(self, project_root: Path): + """ + Initialize artifact packager. + + Args: + project_root: Root directory of FastApps project + """ + self.project_root = project_root + + def package(self) -> Path: + """ + Create deployment package as tar.gz. + + Returns: + Path to created tarball + + Raises: + FileNotFoundError: If required files/directories are missing + RuntimeError: If packaging fails + """ + import shutil + + # Validate project structure + self._validate_project() + + # Use TemporaryDirectory context manager for automatic cleanup + with tempfile.TemporaryDirectory(prefix="fastapps-deploy-") as temp_dir_str: + temp_dir = Path(temp_dir_str) + + try: + # Create manifest + manifest = self._create_manifest() + manifest_path = temp_dir / ".fastapps-manifest.json" + manifest_path.write_text(json.dumps(manifest, indent=2)) + + # Create tarball in temp directory + tarball_temp_path = temp_dir / "deployment.tar.gz" + + with tarfile.open(tarball_temp_path, "w:gz") as tar: + # Add manifest + tar.add( + manifest_path, + arcname=".fastapps-manifest.json", + ) + + # Add required directories and files + self._add_directory(tar, "assets") + self._add_directory(tar, "server") + + # Add configuration files + self._add_file(tar, "package.json") + self._add_file(tar, "requirements.txt") + + # Add optional files if they exist + for optional_file in ["README.md", ".env.example"]: + optional_path = self.project_root / optional_file + if optional_path.exists(): + tar.add(optional_path, arcname=optional_file) + + # Move tarball to project root with timestamp + from datetime import datetime + + timestamp = datetime.utcnow().strftime("%Y%m%d-%H%M%S") + final_path = self.project_root / f".fastapps-deploy-{timestamp}.tar.gz" + shutil.move(str(tarball_temp_path), str(final_path)) + + return final_path + + except Exception as e: + # TemporaryDirectory cleanup is automatic via context manager + raise RuntimeError(f"Failed to create deployment package: {e}") + + def _validate_project(self): + """ + Validate that project has required structure. + + Raises: + FileNotFoundError: If required files are missing + """ + required_dirs = ["assets", "server"] + required_files = ["package.json", "requirements.txt", "server/main.py"] + + for dir_name in required_dirs: + dir_path = self.project_root / dir_name + if not dir_path.exists(): + raise FileNotFoundError( + f"Required directory '{dir_name}' not found. " + f"Make sure you're in a FastApps project root." + ) + + for file_name in required_files: + file_path = self.project_root / file_name + if not file_path.exists(): + raise FileNotFoundError( + f"Required file '{file_name}' not found. " + f"Make sure you're in a FastApps project root." + ) + + def _create_manifest(self) -> Dict: + """ + Create deployment manifest with project metadata. + + Returns: + Manifest dictionary + """ + return { + "fastapps_version": __version__, + "timestamp": datetime.utcnow().isoformat() + "Z", + "project_name": self._get_project_name(), + "widgets": self._list_widgets(), + "dependencies": { + "python": self._parse_python_dependencies(), + "node": self._parse_node_dependencies(), + }, + } + + def _get_project_name(self) -> str: + """ + Extract project name from package.json. + + Returns: + Project name or 'unknown' + """ + try: + package_json_path = self.project_root / "package.json" + package_data = json.loads(package_json_path.read_text()) + return package_data.get("name", "unknown") + except Exception: + return "unknown" + + def _list_widgets(self) -> List[str]: + """ + List all built widget identifiers. + + Returns: + List of widget identifiers + """ + widgets = [] + assets_dir = self.project_root / "assets" + + if assets_dir.exists(): + for html_file in assets_dir.glob("*.html"): + # Extract widget identifier from filename + # Format: widgetid-hash.html + filename = html_file.stem + if "-" in filename: + widget_id = filename.rsplit("-", 1)[0] + widgets.append(widget_id) + + return sorted(set(widgets)) + + def _parse_python_dependencies(self) -> List[str]: + """ + Parse Python dependencies from requirements.txt. + + Returns: + List of package specifications + """ + try: + requirements_path = self.project_root / "requirements.txt" + requirements_text = requirements_path.read_text() + + dependencies = [] + for line in requirements_text.splitlines(): + line = line.strip() + # Skip comments and empty lines + if line and not line.startswith("#"): + dependencies.append(line) + + return dependencies + except Exception: + return [] + + def _parse_node_dependencies(self) -> Dict[str, str]: + """ + Parse Node.js dependencies from package.json. + + Returns: + Dictionary of package name to version + """ + try: + package_json_path = self.project_root / "package.json" + package_data = json.loads(package_json_path.read_text()) + return package_data.get("dependencies", {}) + except Exception: + return {} + + def _add_directory(self, tar: tarfile.TarFile, dir_name: str): + """ + Add directory to tarball with exclusions. + + Args: + tar: TarFile object + dir_name: Directory name relative to project root + """ + dir_path = self.project_root / dir_name + + if not dir_path.exists(): + return + + # Exclusion patterns + exclude_patterns = [ + "__pycache__", + "*.pyc", + ".DS_Store", + "*.swp", + ".venv", + "venv", + "env", + "node_modules", + ".git", + ] + + def should_exclude(path: Path) -> bool: + """Check if path matches exclusion patterns.""" + path_str = str(path) + for pattern in exclude_patterns: + if pattern in path_str: + return True + return False + + # Add directory recursively with exclusions + for item in dir_path.rglob("*"): + if should_exclude(item): + continue + + # Calculate relative path for archive + rel_path = item.relative_to(self.project_root) + + tar.add(item, arcname=str(rel_path)) + + def _add_file(self, tar: tarfile.TarFile, file_name: str): + """ + Add single file to tarball. + + Args: + tar: TarFile object + file_name: File name relative to project root + """ + file_path = self.project_root / file_name + + if file_path.exists(): + tar.add(file_path, arcname=file_name) diff --git a/pyproject.toml b/pyproject.toml index 3d29f2f..c0f1aea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "fastapps" -version = "1.1.1" +version = "1.1.2" description = "A zero-boilerplate framework for building interactive ChatGPT widgets" readme = "README.md" requires-python = ">=3.11" @@ -32,7 +32,8 @@ dependencies = [ "httpx>=0.28.0", "PyJWT>=2.8.0", "cryptography>=41.0.0", - "pyngrok>=7.4.0", + "aiohttp>=3.8.0", + "python-dotenv>=1.0.0", ] [project.optional-dependencies] diff --git a/setup.py b/setup.py index fa2d1cb..899b262 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( name="fastapps", - version="1.1.1", + version="1.1.2", author="FastApps Team", author_email="david@dooi.ai", description="A zero-boilerplate framework for building interactive ChatGPT widgets", @@ -32,7 +32,8 @@ "httpx>=0.28.0", "PyJWT>=2.8.0", "cryptography>=41.0.0", - "pyngrok>=7.4.0", + "aiohttp>=3.8.0", + "python-dotenv>=1.0.0", ], entry_points={ "console_scripts": [ diff --git a/uv.lock b/uv.lock index ec7ca1a..6295a41 100644 --- a/uv.lock +++ b/uv.lock @@ -2,6 +2,130 @@ version = 1 revision = 3 requires-python = ">=3.11" +[[package]] +name = "aiohappyeyeballs" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, +] + +[[package]] +name = "aiohttp" +version = "3.13.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ba/fa/3ae643cd525cf6844d3dc810481e5748107368eb49563c15a5fb9f680750/aiohttp-3.13.1.tar.gz", hash = "sha256:4b7ee9c355015813a6aa085170b96ec22315dabc3d866fd77d147927000e9464", size = 7835344, upload-time = "2025-10-17T14:03:29.337Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/2c/739d03730ffce57d2093e2e611e1541ac9a4b3bb88288c33275058b9ffc2/aiohttp-3.13.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9eefa0a891e85dca56e2d00760945a6325bd76341ec386d3ad4ff72eb97b7e64", size = 742004, upload-time = "2025-10-17T13:59:29.73Z" }, + { url = "https://files.pythonhosted.org/packages/fc/f8/7f5b7f7184d7c80e421dbaecbd13e0b2a0bb8663fd0406864f9a167a438c/aiohttp-3.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6c20eb646371a5a57a97de67e52aac6c47badb1564e719b3601bbb557a2e8fd0", size = 495601, upload-time = "2025-10-17T13:59:31.312Z" }, + { url = "https://files.pythonhosted.org/packages/3e/af/fb78d028b9642dd33ff127d9a6a151586f33daff631b05250fecd0ab23f8/aiohttp-3.13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bfc28038cd86fb1deed5cc75c8fda45c6b0f5c51dfd76f8c63d3d22dc1ab3d1b", size = 491790, upload-time = "2025-10-17T13:59:33.304Z" }, + { url = "https://files.pythonhosted.org/packages/1e/ae/e40e422ee995e4f91f7f087b86304e3dd622d3a5b9ca902a1e94ebf9a117/aiohttp-3.13.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b22eeffca2e522451990c31a36fe0e71079e6112159f39a4391f1c1e259a795", size = 1746350, upload-time = "2025-10-17T13:59:35.158Z" }, + { url = "https://files.pythonhosted.org/packages/28/a5/fe6022bb869bf2d2633b155ed8348d76358c22d5ff9692a15016b2d1019f/aiohttp-3.13.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:65782b2977c05ebd78787e3c834abe499313bf69d6b8be4ff9c340901ee7541f", size = 1703046, upload-time = "2025-10-17T13:59:37.077Z" }, + { url = "https://files.pythonhosted.org/packages/5a/a5/c4ef3617d7cdc49f2d5af077f19794946f0f2d94b93c631ace79047361a2/aiohttp-3.13.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dacba54f9be3702eb866b0b9966754b475e1e39996e29e442c3cd7f1117b43a9", size = 1806161, upload-time = "2025-10-17T13:59:38.837Z" }, + { url = "https://files.pythonhosted.org/packages/ad/45/b87d2430aee7e7d00b24e3dff2c5bd69f21017f6edb19cfd91e514664fc8/aiohttp-3.13.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:aa878da718e8235302c365e376b768035add36b55177706d784a122cb822a6a4", size = 1894546, upload-time = "2025-10-17T13:59:40.741Z" }, + { url = "https://files.pythonhosted.org/packages/e8/a2/79eb466786a7f11a0292c353a8a9b95e88268c48c389239d7531d66dbb48/aiohttp-3.13.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e4b4e607fbd4964d65945a7b9d1e7f98b0d5545736ea613f77d5a2a37ff1e46", size = 1745683, upload-time = "2025-10-17T13:59:42.59Z" }, + { url = "https://files.pythonhosted.org/packages/93/1a/153b0ad694f377e94eacc85338efe03ed4776a396c8bb47bd9227135792a/aiohttp-3.13.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0c3db2d0e5477ad561bf7ba978c3ae5f8f78afda70daa05020179f759578754f", size = 1605418, upload-time = "2025-10-17T13:59:45.229Z" }, + { url = "https://files.pythonhosted.org/packages/3f/4e/18605b1bfeb4b00d3396d833647cdb213118e2a96862e5aebee62ad065b4/aiohttp-3.13.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9739d34506fdf59bf2c092560d502aa728b8cdb33f34ba15fb5e2852c35dd829", size = 1722379, upload-time = "2025-10-17T13:59:46.969Z" }, + { url = "https://files.pythonhosted.org/packages/72/13/0a38ad385d547fb283e0e1fe1ff1dff8899bd4ed0aaceeb13ec14abbf136/aiohttp-3.13.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:b902e30a268a85d50197b4997edc6e78842c14c0703450f632c2d82f17577845", size = 1716693, upload-time = "2025-10-17T13:59:49.217Z" }, + { url = "https://files.pythonhosted.org/packages/55/65/7029d7573ab9009adde380052c6130d02c8db52195fda112db35e914fe7b/aiohttp-3.13.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1bbfc04c8de7def6504cce0a97f9885a5c805fd2395a0634bc10f9d6ecb42524", size = 1784174, upload-time = "2025-10-17T13:59:51.439Z" }, + { url = "https://files.pythonhosted.org/packages/2d/36/fd46e39cb85418e45b0e4a8bfc39651ee0b8f08ea006adf217a221cdb269/aiohttp-3.13.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:6941853405a38a5eeb7d9776db77698df373ff7fa8c765cb81ea14a344fccbeb", size = 1593716, upload-time = "2025-10-17T13:59:53.367Z" }, + { url = "https://files.pythonhosted.org/packages/85/b8/188e0cb1be37b4408373171070fda17c3bf9c67c0d3d4fd5ee5b1fa108e1/aiohttp-3.13.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:7764adcd2dc8bd21c8228a53dda2005428498dc4d165f41b6086f0ac1c65b1c9", size = 1799254, upload-time = "2025-10-17T13:59:55.352Z" }, + { url = "https://files.pythonhosted.org/packages/67/ff/fdf768764eb427b0cc9ebb2cebddf990f94d98b430679f8383c35aa114be/aiohttp-3.13.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c09e08d38586fa59e5a2f9626505a0326fadb8e9c45550f029feeb92097a0afc", size = 1738122, upload-time = "2025-10-17T13:59:57.263Z" }, + { url = "https://files.pythonhosted.org/packages/94/84/fce7a4d575943394d7c0e632273838eb6f39de8edf25386017bf5f0de23b/aiohttp-3.13.1-cp311-cp311-win32.whl", hash = "sha256:ce1371675e74f6cf271d0b5530defb44cce713fd0ab733713562b3a2b870815c", size = 430491, upload-time = "2025-10-17T13:59:59.466Z" }, + { url = "https://files.pythonhosted.org/packages/ac/d2/d21b8ab6315a5d588c550ab285b4f02ae363edf012920e597904c5a56608/aiohttp-3.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:77a2f5cc28cf4704cc157be135c6a6cfb38c9dea478004f1c0fd7449cf445c28", size = 454808, upload-time = "2025-10-17T14:00:01.247Z" }, + { url = "https://files.pythonhosted.org/packages/1a/72/d463a10bf29871f6e3f63bcf3c91362dc4d72ed5917a8271f96672c415ad/aiohttp-3.13.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0760bd9a28efe188d77b7c3fe666e6ef74320d0f5b105f2e931c7a7e884c8230", size = 736218, upload-time = "2025-10-17T14:00:03.51Z" }, + { url = "https://files.pythonhosted.org/packages/26/13/f7bccedbe52ea5a6eef1e4ebb686a8d7765319dfd0a5939f4238cb6e79e6/aiohttp-3.13.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7129a424b441c3fe018a414401bf1b9e1d49492445f5676a3aecf4f74f67fcdb", size = 491251, upload-time = "2025-10-17T14:00:05.756Z" }, + { url = "https://files.pythonhosted.org/packages/0c/7c/7ea51b5aed6cc69c873f62548da8345032aa3416336f2d26869d4d37b4a2/aiohttp-3.13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e1cb04ae64a594f6ddf5cbb024aba6b4773895ab6ecbc579d60414f8115e9e26", size = 490394, upload-time = "2025-10-17T14:00:07.504Z" }, + { url = "https://files.pythonhosted.org/packages/31/05/1172cc4af4557f6522efdee6eb2b9f900e1e320a97e25dffd3c5a6af651b/aiohttp-3.13.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:782d656a641e755decd6bd98d61d2a8ea062fd45fd3ff8d4173605dd0d2b56a1", size = 1737455, upload-time = "2025-10-17T14:00:09.403Z" }, + { url = "https://files.pythonhosted.org/packages/24/3d/ce6e4eca42f797d6b1cd3053cf3b0a22032eef3e4d1e71b9e93c92a3f201/aiohttp-3.13.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f92ad8169767429a6d2237331726c03ccc5f245222f9373aa045510976af2b35", size = 1699176, upload-time = "2025-10-17T14:00:11.314Z" }, + { url = "https://files.pythonhosted.org/packages/25/04/7127ba55653e04da51477372566b16ae786ef854e06222a1c96b4ba6c8ef/aiohttp-3.13.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0e778f634ca50ec005eefa2253856921c429581422d887be050f2c1c92e5ce12", size = 1767216, upload-time = "2025-10-17T14:00:13.668Z" }, + { url = "https://files.pythonhosted.org/packages/b8/3b/43bca1e75847e600f40df829a6b2f0f4e1d4c70fb6c4818fdc09a462afd5/aiohttp-3.13.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9bc36b41cf4aab5d3b34d22934a696ab83516603d1bc1f3e4ff9930fe7d245e5", size = 1865870, upload-time = "2025-10-17T14:00:15.852Z" }, + { url = "https://files.pythonhosted.org/packages/9e/69/b204e5d43384197a614c88c1717c324319f5b4e7d0a1b5118da583028d40/aiohttp-3.13.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3fd4570ea696aee27204dd524f287127ed0966d14d309dc8cc440f474e3e7dbd", size = 1751021, upload-time = "2025-10-17T14:00:18.297Z" }, + { url = "https://files.pythonhosted.org/packages/1c/af/845dc6b6fdf378791d720364bf5150f80d22c990f7e3a42331d93b337cc7/aiohttp-3.13.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7bda795f08b8a620836ebfb0926f7973972a4bf8c74fdf9145e489f88c416811", size = 1561448, upload-time = "2025-10-17T14:00:20.152Z" }, + { url = "https://files.pythonhosted.org/packages/7a/91/d2ab08cd77ed76a49e4106b1cfb60bce2768242dd0c4f9ec0cb01e2cbf94/aiohttp-3.13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:055a51d90e351aae53dcf324d0eafb2abe5b576d3ea1ec03827d920cf81a1c15", size = 1698196, upload-time = "2025-10-17T14:00:22.131Z" }, + { url = "https://files.pythonhosted.org/packages/5e/d1/082f0620dc428ecb8f21c08a191a4694915cd50f14791c74a24d9161cc50/aiohttp-3.13.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:d4131df864cbcc09bb16d3612a682af0db52f10736e71312574d90f16406a867", size = 1719252, upload-time = "2025-10-17T14:00:24.453Z" }, + { url = "https://files.pythonhosted.org/packages/fc/78/2af2f44491be7b08e43945b72d2b4fd76f0a14ba850ba9e41d28a7ce716a/aiohttp-3.13.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:163d3226e043f79bf47c87f8dfc89c496cc7bc9128cb7055ce026e435d551720", size = 1736529, upload-time = "2025-10-17T14:00:26.567Z" }, + { url = "https://files.pythonhosted.org/packages/b0/34/3e919ecdc93edaea8d140138049a0d9126141072e519535e2efa38eb7a02/aiohttp-3.13.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:a2370986a3b75c1a5f3d6f6d763fc6be4b430226577b0ed16a7c13a75bf43d8f", size = 1553723, upload-time = "2025-10-17T14:00:28.592Z" }, + { url = "https://files.pythonhosted.org/packages/21/4b/d8003aeda2f67f359b37e70a5a4b53fee336d8e89511ac307ff62aeefcdb/aiohttp-3.13.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:d7c14de0c7c9f1e6e785ce6cbe0ed817282c2af0012e674f45b4e58c6d4ea030", size = 1763394, upload-time = "2025-10-17T14:00:31.051Z" }, + { url = "https://files.pythonhosted.org/packages/4c/7b/1dbe6a39e33af9baaafc3fc016a280663684af47ba9f0e5d44249c1f72ec/aiohttp-3.13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb611489cf0db10b99beeb7280bd39e0ef72bc3eb6d8c0f0a16d8a56075d1eb7", size = 1718104, upload-time = "2025-10-17T14:00:33.407Z" }, + { url = "https://files.pythonhosted.org/packages/5c/88/bd1b38687257cce67681b9b0fa0b16437be03383fa1be4d1a45b168bef25/aiohttp-3.13.1-cp312-cp312-win32.whl", hash = "sha256:f90fe0ee75590f7428f7c8b5479389d985d83c949ea10f662ab928a5ed5cf5e6", size = 425303, upload-time = "2025-10-17T14:00:35.829Z" }, + { url = "https://files.pythonhosted.org/packages/0e/e3/4481f50dd6f27e9e58c19a60cff44029641640237e35d32b04aaee8cf95f/aiohttp-3.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:3461919a9dca272c183055f2aab8e6af0adc810a1b386cce28da11eb00c859d9", size = 452071, upload-time = "2025-10-17T14:00:37.764Z" }, + { url = "https://files.pythonhosted.org/packages/16/6d/d267b132342e1080f4c1bb7e1b4e96b168b3cbce931ec45780bff693ff95/aiohttp-3.13.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:55785a7f8f13df0c9ca30b5243d9909bd59f48b274262a8fe78cee0828306e5d", size = 730727, upload-time = "2025-10-17T14:00:39.681Z" }, + { url = "https://files.pythonhosted.org/packages/92/c8/1cf495bac85cf71b80fad5f6d7693e84894f11b9fe876b64b0a1e7cbf32f/aiohttp-3.13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4bef5b83296cebb8167707b4f8d06c1805db0af632f7a72d7c5288a84667e7c3", size = 488678, upload-time = "2025-10-17T14:00:41.541Z" }, + { url = "https://files.pythonhosted.org/packages/a8/19/23c6b81cca587ec96943d977a58d11d05a82837022e65cd5502d665a7d11/aiohttp-3.13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:27af0619c33f9ca52f06069ec05de1a357033449ab101836f431768ecfa63ff5", size = 487637, upload-time = "2025-10-17T14:00:43.527Z" }, + { url = "https://files.pythonhosted.org/packages/48/58/8f9464afb88b3eed145ad7c665293739b3a6f91589694a2bb7e5778cbc72/aiohttp-3.13.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a47fe43229a8efd3764ef7728a5c1158f31cdf2a12151fe99fde81c9ac87019c", size = 1718975, upload-time = "2025-10-17T14:00:45.496Z" }, + { url = "https://files.pythonhosted.org/packages/e1/8b/c3da064ca392b2702f53949fd7c403afa38d9ee10bf52c6ad59a42537103/aiohttp-3.13.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6e68e126de5b46e8b2bee73cab086b5d791e7dc192056916077aa1e2e2b04437", size = 1686905, upload-time = "2025-10-17T14:00:47.707Z" }, + { url = "https://files.pythonhosted.org/packages/0a/a4/9c8a3843ecf526daee6010af1a66eb62579be1531d2d5af48ea6f405ad3c/aiohttp-3.13.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e65ef49dd22514329c55970d39079618a8abf856bae7147913bb774a3ab3c02f", size = 1754907, upload-time = "2025-10-17T14:00:49.702Z" }, + { url = "https://files.pythonhosted.org/packages/a4/80/1f470ed93e06436e3fc2659a9fc329c192fa893fb7ed4e884d399dbfb2a8/aiohttp-3.13.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0e425a7e0511648b3376839dcc9190098671a47f21a36e815b97762eb7d556b0", size = 1857129, upload-time = "2025-10-17T14:00:51.822Z" }, + { url = "https://files.pythonhosted.org/packages/cc/e6/33d305e6cce0a8daeb79c7d8d6547d6e5f27f4e35fa4883fc9c9eb638596/aiohttp-3.13.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:010dc9b7110f055006acd3648d5d5955bb6473b37c3663ec42a1b4cba7413e6b", size = 1738189, upload-time = "2025-10-17T14:00:53.976Z" }, + { url = "https://files.pythonhosted.org/packages/ac/42/8df03367e5a64327fe0c39291080697795430c438fc1139c7cc1831aa1df/aiohttp-3.13.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1b5c722d0ca5f57d61066b5dfa96cdb87111e2519156b35c1f8dd17c703bee7a", size = 1553608, upload-time = "2025-10-17T14:00:56.144Z" }, + { url = "https://files.pythonhosted.org/packages/96/17/6d5c73cd862f1cf29fddcbb54aac147037ff70a043a2829d03a379e95742/aiohttp-3.13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:93029f0e9b77b714904a281b5aa578cdc8aa8ba018d78c04e51e1c3d8471b8ec", size = 1681809, upload-time = "2025-10-17T14:00:58.603Z" }, + { url = "https://files.pythonhosted.org/packages/be/31/8926c8ab18533f6076ce28d2c329a203b58c6861681906e2d73b9c397588/aiohttp-3.13.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:d1824c7d08d8ddfc8cb10c847f696942e5aadbd16fd974dfde8bd2c3c08a9fa1", size = 1711161, upload-time = "2025-10-17T14:01:01.744Z" }, + { url = "https://files.pythonhosted.org/packages/f2/36/2f83e1ca730b1e0a8cf1c8ab9559834c5eec9f5da86e77ac71f0d16b521d/aiohttp-3.13.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:8f47d0ff5b3eb9c1278a2f56ea48fda667da8ebf28bd2cb378b7c453936ce003", size = 1731999, upload-time = "2025-10-17T14:01:04.626Z" }, + { url = "https://files.pythonhosted.org/packages/b9/ec/1f818cc368dfd4d5ab4e9efc8f2f6f283bfc31e1c06d3e848bcc862d4591/aiohttp-3.13.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8a396b1da9b51ded79806ac3b57a598f84e0769eaa1ba300655d8b5e17b70c7b", size = 1548684, upload-time = "2025-10-17T14:01:06.828Z" }, + { url = "https://files.pythonhosted.org/packages/d3/ad/33d36efd16e4fefee91b09a22a3a0e1b830f65471c3567ac5a8041fac812/aiohttp-3.13.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d9c52a65f54796e066b5d674e33b53178014752d28bca555c479c2c25ffcec5b", size = 1756676, upload-time = "2025-10-17T14:01:09.517Z" }, + { url = "https://files.pythonhosted.org/packages/3c/c4/4a526d84e77d464437713ca909364988ed2e0cd0cdad2c06cb065ece9e08/aiohttp-3.13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a89da72d18d6c95a653470b78d8ee5aa3c4b37212004c103403d0776cbea6ff0", size = 1715577, upload-time = "2025-10-17T14:01:11.958Z" }, + { url = "https://files.pythonhosted.org/packages/a2/21/e39638b7d9c7f1362c4113a91870f89287e60a7ea2d037e258b81e8b37d5/aiohttp-3.13.1-cp313-cp313-win32.whl", hash = "sha256:02e0258b7585ddf5d01c79c716ddd674386bfbf3041fbbfe7bdf9c7c32eb4a9b", size = 424468, upload-time = "2025-10-17T14:01:14.344Z" }, + { url = "https://files.pythonhosted.org/packages/cc/00/f3a92c592a845ebb2f47d102a67f35f0925cb854c5e7386f1a3a1fdff2ab/aiohttp-3.13.1-cp313-cp313-win_amd64.whl", hash = "sha256:ef56ffe60e8d97baac123272bde1ab889ee07d3419606fae823c80c2b86c403e", size = 450806, upload-time = "2025-10-17T14:01:16.437Z" }, + { url = "https://files.pythonhosted.org/packages/97/be/0f6c41d2fd0aab0af133c509cabaf5b1d78eab882cb0ceb872e87ceeabf7/aiohttp-3.13.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:77f83b3dc5870a2ea79a0fcfdcc3fc398187ec1675ff61ec2ceccad27ecbd303", size = 733828, upload-time = "2025-10-17T14:01:18.58Z" }, + { url = "https://files.pythonhosted.org/packages/75/14/24e2ac5efa76ae30e05813e0f50737005fd52da8ddffee474d4a5e7f38a6/aiohttp-3.13.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:9cafd2609ebb755e47323306c7666283fbba6cf82b5f19982ea627db907df23a", size = 489320, upload-time = "2025-10-17T14:01:20.644Z" }, + { url = "https://files.pythonhosted.org/packages/da/5a/4cbe599358d05ea7db4869aff44707b57d13f01724d48123dc68b3288d5a/aiohttp-3.13.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9c489309a2ca548d5f11131cfb4092f61d67954f930bba7e413bcdbbb82d7fae", size = 489899, upload-time = "2025-10-17T14:01:22.638Z" }, + { url = "https://files.pythonhosted.org/packages/67/96/3aec9d9cfc723273d4386328a1e2562cf23629d2f57d137047c49adb2afb/aiohttp-3.13.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79ac15fe5fdbf3c186aa74b656cd436d9a1e492ba036db8901c75717055a5b1c", size = 1716556, upload-time = "2025-10-17T14:01:25.406Z" }, + { url = "https://files.pythonhosted.org/packages/b9/99/39a3d250595b5c8172843831221fa5662884f63f8005b00b4034f2a7a836/aiohttp-3.13.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:095414be94fce3bc080684b4cd50fb70d439bc4662b2a1984f45f3bf9ede08aa", size = 1665814, upload-time = "2025-10-17T14:01:27.683Z" }, + { url = "https://files.pythonhosted.org/packages/3b/96/8319e7060a85db14a9c178bc7b3cf17fad458db32ba6d2910de3ca71452d/aiohttp-3.13.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c68172e1a2dca65fa1272c85ca72e802d78b67812b22827df01017a15c5089fa", size = 1755767, upload-time = "2025-10-17T14:01:29.914Z" }, + { url = "https://files.pythonhosted.org/packages/1c/c6/0a2b3d886b40aa740fa2294cd34ed46d2e8108696748492be722e23082a7/aiohttp-3.13.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3751f9212bcd119944d4ea9de6a3f0fee288c177b8ca55442a2cdff0c8201eb3", size = 1836591, upload-time = "2025-10-17T14:01:32.28Z" }, + { url = "https://files.pythonhosted.org/packages/fb/34/8ab5904b3331c91a58507234a1e2f662f837e193741609ee5832eb436251/aiohttp-3.13.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8619dca57d98a8353abdc7a1eeb415548952b39d6676def70d9ce76d41a046a9", size = 1714915, upload-time = "2025-10-17T14:01:35.138Z" }, + { url = "https://files.pythonhosted.org/packages/b5/d3/d36077ca5f447649112189074ac6c192a666bf68165b693e48c23b0d008c/aiohttp-3.13.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:97795a0cb0a5f8a843759620e9cbd8889f8079551f5dcf1ccd99ed2f056d9632", size = 1546579, upload-time = "2025-10-17T14:01:38.237Z" }, + { url = "https://files.pythonhosted.org/packages/a8/14/dbc426a1bb1305c4fc78ce69323498c9e7c699983366ef676aa5d3f949fa/aiohttp-3.13.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1060e058da8f9f28a7026cdfca9fc886e45e551a658f6a5c631188f72a3736d2", size = 1680633, upload-time = "2025-10-17T14:01:40.902Z" }, + { url = "https://files.pythonhosted.org/packages/29/83/1e68e519aff9f3ef6d4acb6cdda7b5f592ef5c67c8f095dc0d8e06ce1c3e/aiohttp-3.13.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:f48a2c26333659101ef214907d29a76fe22ad7e912aa1e40aeffdff5e8180977", size = 1678675, upload-time = "2025-10-17T14:01:43.779Z" }, + { url = "https://files.pythonhosted.org/packages/38/b9/7f3e32a81c08b6d29ea15060c377e1f038ad96cd9923a85f30e817afff22/aiohttp-3.13.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f1dfad638b9c91ff225162b2824db0e99ae2d1abe0dc7272b5919701f0a1e685", size = 1726829, upload-time = "2025-10-17T14:01:46.546Z" }, + { url = "https://files.pythonhosted.org/packages/23/ce/610b1f77525a0a46639aea91377b12348e9f9412cc5ddcb17502aa4681c7/aiohttp-3.13.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:8fa09ab6dd567cb105db4e8ac4d60f377a7a94f67cf669cac79982f626360f32", size = 1542985, upload-time = "2025-10-17T14:01:49.082Z" }, + { url = "https://files.pythonhosted.org/packages/53/39/3ac8dfdad5de38c401846fa071fcd24cb3b88ccfb024854df6cbd9b4a07e/aiohttp-3.13.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4159fae827f9b5f655538a4f99b7cbc3a2187e5ca2eee82f876ef1da802ccfa9", size = 1741556, upload-time = "2025-10-17T14:01:51.846Z" }, + { url = "https://files.pythonhosted.org/packages/2a/48/b1948b74fea7930b0f29595d1956842324336de200593d49a51a40607fdc/aiohttp-3.13.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ad671118c19e9cfafe81a7a05c294449fe0ebb0d0c6d5bb445cd2190023f5cef", size = 1696175, upload-time = "2025-10-17T14:01:54.232Z" }, + { url = "https://files.pythonhosted.org/packages/96/26/063bba38e4b27b640f56cc89fe83cc3546a7ae162c2e30ca345f0ccdc3d1/aiohttp-3.13.1-cp314-cp314-win32.whl", hash = "sha256:c5c970c148c48cf6acb65224ca3c87a47f74436362dde75c27bc44155ccf7dfc", size = 430254, upload-time = "2025-10-17T14:01:56.451Z" }, + { url = "https://files.pythonhosted.org/packages/88/aa/25fd764384dc4eab714023112d3548a8dd69a058840d61d816ea736097a2/aiohttp-3.13.1-cp314-cp314-win_amd64.whl", hash = "sha256:748a00167b7a88385756fa615417d24081cba7e58c8727d2e28817068b97c18c", size = 456256, upload-time = "2025-10-17T14:01:58.752Z" }, + { url = "https://files.pythonhosted.org/packages/d4/9f/9ba6059de4bad25c71cd88e3da53f93e9618ea369cf875c9f924b1c167e2/aiohttp-3.13.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:390b73e99d7a1f0f658b3f626ba345b76382f3edc65f49d6385e326e777ed00e", size = 765956, upload-time = "2025-10-17T14:02:01.515Z" }, + { url = "https://files.pythonhosted.org/packages/1f/30/b86da68b494447d3060f45c7ebb461347535dab4af9162a9267d9d86ca31/aiohttp-3.13.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:27e83abb330e687e019173d8fc1fd6a1cf471769624cf89b1bb49131198a810a", size = 503206, upload-time = "2025-10-17T14:02:03.818Z" }, + { url = "https://files.pythonhosted.org/packages/c1/21/d27a506552843ff9eeb9fcc2d45f943b09eefdfdf205aab044f4f1f39f6a/aiohttp-3.13.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2b20eed07131adbf3e873e009c2869b16a579b236e9d4b2f211bf174d8bef44a", size = 507719, upload-time = "2025-10-17T14:02:05.947Z" }, + { url = "https://files.pythonhosted.org/packages/58/23/4042230ec7e4edc7ba43d0342b5a3d2fe0222ca046933c4251a35aaf17f5/aiohttp-3.13.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:58fee9ef8477fd69e823b92cfd1f590ee388521b5ff8f97f3497e62ee0656212", size = 1862758, upload-time = "2025-10-17T14:02:08.469Z" }, + { url = "https://files.pythonhosted.org/packages/df/88/525c45bea7cbb9f65df42cadb4ff69f6a0dbf95931b0ff7d1fdc40a1cb5f/aiohttp-3.13.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1f62608fcb7b3d034d5e9496bea52d94064b7b62b06edba82cd38191336bbeda", size = 1717790, upload-time = "2025-10-17T14:02:11.37Z" }, + { url = "https://files.pythonhosted.org/packages/1d/80/21e9b5eb77df352a5788713f37359b570a793f0473f3a72db2e46df379b9/aiohttp-3.13.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fdc4d81c3dfc999437f23e36d197e8b557a3f779625cd13efe563a9cfc2ce712", size = 1842088, upload-time = "2025-10-17T14:02:13.872Z" }, + { url = "https://files.pythonhosted.org/packages/d2/bf/d1738f6d63fe8b2a0ad49533911b3347f4953cd001bf3223cb7b61f18dff/aiohttp-3.13.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:601d7ec812f746fd80ff8af38eeb3f196e1bab4a4d39816ccbc94c222d23f1d0", size = 1934292, upload-time = "2025-10-17T14:02:16.624Z" }, + { url = "https://files.pythonhosted.org/packages/04/e6/26cab509b42610ca49573f2fc2867810f72bd6a2070182256c31b14f2e98/aiohttp-3.13.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47c3f21c469b840d9609089435c0d9918ae89f41289bf7cc4afe5ff7af5458db", size = 1791328, upload-time = "2025-10-17T14:02:19.051Z" }, + { url = "https://files.pythonhosted.org/packages/8a/6d/baf7b462852475c9d045bee8418d9cdf280efb687752b553e82d0c58bcc2/aiohttp-3.13.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d6c6cdc0750db88520332d4aaa352221732b0cafe89fd0e42feec7cb1b5dc236", size = 1622663, upload-time = "2025-10-17T14:02:21.397Z" }, + { url = "https://files.pythonhosted.org/packages/c8/48/396a97318af9b5f4ca8b3dc14a67976f71c6400a9609c622f96da341453f/aiohttp-3.13.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:58a12299eeb1fca2414ee2bc345ac69b0f765c20b82c3ab2a75d91310d95a9f6", size = 1787791, upload-time = "2025-10-17T14:02:24.212Z" }, + { url = "https://files.pythonhosted.org/packages/a8/e2/6925f6784134ce3ff3ce1a8502ab366432a3b5605387618c1a939ce778d9/aiohttp-3.13.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:0989cbfc195a4de1bb48f08454ef1cb47424b937e53ed069d08404b9d3c7aea1", size = 1775459, upload-time = "2025-10-17T14:02:26.971Z" }, + { url = "https://files.pythonhosted.org/packages/c3/e3/b372047ba739fc39f199b99290c4cc5578ce5fd125f69168c967dac44021/aiohttp-3.13.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:feb5ee664300e2435e0d1bc3443a98925013dfaf2cae9699c1f3606b88544898", size = 1789250, upload-time = "2025-10-17T14:02:29.686Z" }, + { url = "https://files.pythonhosted.org/packages/02/8c/9f48b93d7d57fc9ef2ad4adace62e4663ea1ce1753806c4872fb36b54c39/aiohttp-3.13.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:58a6f8702da0c3606fb5cf2e669cce0ca681d072fe830968673bb4c69eb89e88", size = 1616139, upload-time = "2025-10-17T14:02:32.151Z" }, + { url = "https://files.pythonhosted.org/packages/5c/c6/c64e39d61aaa33d7de1be5206c0af3ead4b369bf975dac9fdf907a4291c1/aiohttp-3.13.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a417ceb433b9d280e2368ffea22d4bc6e3e0d894c4bc7768915124d57d0964b6", size = 1815829, upload-time = "2025-10-17T14:02:34.635Z" }, + { url = "https://files.pythonhosted.org/packages/22/75/e19e93965ea675f1151753b409af97a14f1d888588a555e53af1e62b83eb/aiohttp-3.13.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8ac8854f7b0466c5d6a9ea49249b3f6176013859ac8f4bb2522ad8ed6b94ded2", size = 1760923, upload-time = "2025-10-17T14:02:37.364Z" }, + { url = "https://files.pythonhosted.org/packages/6c/a4/06ed38f1dabd98ea136fd116cba1d02c9b51af5a37d513b6850a9a567d86/aiohttp-3.13.1-cp314-cp314t-win32.whl", hash = "sha256:be697a5aeff42179ed13b332a411e674994bcd406c81642d014ace90bf4bb968", size = 463318, upload-time = "2025-10-17T14:02:39.924Z" }, + { url = "https://files.pythonhosted.org/packages/04/0f/27e4fdde899e1e90e35eeff56b54ed63826435ad6cdb06b09ed312d1b3fa/aiohttp-3.13.1-cp314-cp314t-win_amd64.whl", hash = "sha256:f1d6aa90546a4e8f20c3500cb68ab14679cd91f927fa52970035fd3207dfb3da", size = 496721, upload-time = "2025-10-17T14:02:42.199Z" }, +] + +[[package]] +name = "aiosignal" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, +] + [[package]] name = "annotated-types" version = "0.7.0" @@ -507,16 +631,17 @@ wheels = [ [[package]] name = "fastapps" -version = "1.1.1" +version = "1.1.2" source = { editable = "." } dependencies = [ + { name = "aiohttp" }, { name = "click" }, { name = "cryptography" }, { name = "fastmcp" }, { name = "httpx" }, { name = "pydantic" }, { name = "pyjwt" }, - { name = "pyngrok" }, + { name = "python-dotenv" }, { name = "rich" }, { name = "uvicorn" }, ] @@ -532,6 +657,7 @@ dev = [ [package.metadata] requires-dist = [ + { name = "aiohttp", specifier = ">=3.8.0" }, { name = "black", marker = "extra == 'dev'", specifier = ">=23.0.0" }, { name = "click", specifier = ">=8.0.0" }, { name = "cryptography", specifier = ">=41.0.0" }, @@ -539,10 +665,10 @@ requires-dist = [ { name = "httpx", specifier = ">=0.28.0" }, { name = "pydantic", specifier = ">=2.0.0" }, { name = "pyjwt", specifier = ">=2.8.0" }, - { name = "pyngrok", specifier = ">=7.4.0" }, { name = "pytest", marker = "extra == 'dev'", specifier = ">=7.0.0" }, { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.21.0" }, { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=4.0.0" }, + { name = "python-dotenv", specifier = ">=1.0.0" }, { name = "rich", specifier = ">=13.0.0" }, { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.1.0" }, { name = "uvicorn", specifier = ">=0.20.0" }, @@ -574,6 +700,111 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/90/0c/7e966e01240f7a9347d22d09e9d60cb46eaad68ca2ba24830755dab2c55c/fastmcp-2.13.0.1-py3-none-any.whl", hash = "sha256:60a8313b48803a2ddfad2d0fe8b9dd1f231aba7564c0551cad8447527ffe46e2", size = 367504, upload-time = "2025-10-26T15:42:58.98Z" }, ] +[[package]] +name = "frozenlist" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/03/077f869d540370db12165c0aa51640a873fb661d8b315d1d4d67b284d7ac/frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84", size = 86912, upload-time = "2025-10-06T05:35:45.98Z" }, + { url = "https://files.pythonhosted.org/packages/df/b5/7610b6bd13e4ae77b96ba85abea1c8cb249683217ef09ac9e0ae93f25a91/frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9", size = 50046, upload-time = "2025-10-06T05:35:47.009Z" }, + { url = "https://files.pythonhosted.org/packages/6e/ef/0e8f1fe32f8a53dd26bdd1f9347efe0778b0fddf62789ea683f4cc7d787d/frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93", size = 50119, upload-time = "2025-10-06T05:35:48.38Z" }, + { url = "https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f", size = 231067, upload-time = "2025-10-06T05:35:49.97Z" }, + { url = "https://files.pythonhosted.org/packages/45/7e/afe40eca3a2dc19b9904c0f5d7edfe82b5304cb831391edec0ac04af94c2/frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695", size = 233160, upload-time = "2025-10-06T05:35:51.729Z" }, + { url = "https://files.pythonhosted.org/packages/a6/aa/7416eac95603ce428679d273255ffc7c998d4132cfae200103f164b108aa/frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52", size = 228544, upload-time = "2025-10-06T05:35:53.246Z" }, + { url = "https://files.pythonhosted.org/packages/8b/3d/2a2d1f683d55ac7e3875e4263d28410063e738384d3adc294f5ff3d7105e/frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581", size = 243797, upload-time = "2025-10-06T05:35:54.497Z" }, + { url = "https://files.pythonhosted.org/packages/78/1e/2d5565b589e580c296d3bb54da08d206e797d941a83a6fdea42af23be79c/frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567", size = 247923, upload-time = "2025-10-06T05:35:55.861Z" }, + { url = "https://files.pythonhosted.org/packages/aa/c3/65872fcf1d326a7f101ad4d86285c403c87be7d832b7470b77f6d2ed5ddc/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b", size = 230886, upload-time = "2025-10-06T05:35:57.399Z" }, + { url = "https://files.pythonhosted.org/packages/a0/76/ac9ced601d62f6956f03cc794f9e04c81719509f85255abf96e2510f4265/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92", size = 245731, upload-time = "2025-10-06T05:35:58.563Z" }, + { url = "https://files.pythonhosted.org/packages/b9/49/ecccb5f2598daf0b4a1415497eba4c33c1e8ce07495eb07d2860c731b8d5/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d", size = 241544, upload-time = "2025-10-06T05:35:59.719Z" }, + { url = "https://files.pythonhosted.org/packages/53/4b/ddf24113323c0bbcc54cb38c8b8916f1da7165e07b8e24a717b4a12cbf10/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd", size = 241806, upload-time = "2025-10-06T05:36:00.959Z" }, + { url = "https://files.pythonhosted.org/packages/a7/fb/9b9a084d73c67175484ba2789a59f8eebebd0827d186a8102005ce41e1ba/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967", size = 229382, upload-time = "2025-10-06T05:36:02.22Z" }, + { url = "https://files.pythonhosted.org/packages/95/a3/c8fb25aac55bf5e12dae5c5aa6a98f85d436c1dc658f21c3ac73f9fa95e5/frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25", size = 39647, upload-time = "2025-10-06T05:36:03.409Z" }, + { url = "https://files.pythonhosted.org/packages/0a/f5/603d0d6a02cfd4c8f2a095a54672b3cf967ad688a60fb9faf04fc4887f65/frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b", size = 44064, upload-time = "2025-10-06T05:36:04.368Z" }, + { url = "https://files.pythonhosted.org/packages/5d/16/c2c9ab44e181f043a86f9a8f84d5124b62dbcb3a02c0977ec72b9ac1d3e0/frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a", size = 39937, upload-time = "2025-10-06T05:36:05.669Z" }, + { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" }, + { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" }, + { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" }, + { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" }, + { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" }, + { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" }, + { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" }, + { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" }, + { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" }, + { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" }, + { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" }, + { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" }, + { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" }, + { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" }, + { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717, upload-time = "2025-10-06T05:36:27.341Z" }, + { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651, upload-time = "2025-10-06T05:36:28.855Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417, upload-time = "2025-10-06T05:36:29.877Z" }, + { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391, upload-time = "2025-10-06T05:36:31.301Z" }, + { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048, upload-time = "2025-10-06T05:36:32.531Z" }, + { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549, upload-time = "2025-10-06T05:36:33.706Z" }, + { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833, upload-time = "2025-10-06T05:36:34.947Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363, upload-time = "2025-10-06T05:36:36.534Z" }, + { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314, upload-time = "2025-10-06T05:36:38.582Z" }, + { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365, upload-time = "2025-10-06T05:36:40.152Z" }, + { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763, upload-time = "2025-10-06T05:36:41.355Z" }, + { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110, upload-time = "2025-10-06T05:36:42.716Z" }, + { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717, upload-time = "2025-10-06T05:36:44.251Z" }, + { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628, upload-time = "2025-10-06T05:36:45.423Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882, upload-time = "2025-10-06T05:36:46.796Z" }, + { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676, upload-time = "2025-10-06T05:36:47.8Z" }, + { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235, upload-time = "2025-10-06T05:36:48.78Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742, upload-time = "2025-10-06T05:36:49.837Z" }, + { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725, upload-time = "2025-10-06T05:36:50.851Z" }, + { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533, upload-time = "2025-10-06T05:36:51.898Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506, upload-time = "2025-10-06T05:36:53.101Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161, upload-time = "2025-10-06T05:36:54.309Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676, upload-time = "2025-10-06T05:36:55.566Z" }, + { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638, upload-time = "2025-10-06T05:36:56.758Z" }, + { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067, upload-time = "2025-10-06T05:36:57.965Z" }, + { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101, upload-time = "2025-10-06T05:36:59.237Z" }, + { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901, upload-time = "2025-10-06T05:37:00.811Z" }, + { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395, upload-time = "2025-10-06T05:37:02.115Z" }, + { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659, upload-time = "2025-10-06T05:37:03.711Z" }, + { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492, upload-time = "2025-10-06T05:37:04.915Z" }, + { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034, upload-time = "2025-10-06T05:37:06.343Z" }, + { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749, upload-time = "2025-10-06T05:37:07.431Z" }, + { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" }, + { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" }, + { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" }, + { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" }, + { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" }, + { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" }, + { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" }, + { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" }, + { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" }, + { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" }, + { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" }, + { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" }, + { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" }, + { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" }, + { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" }, + { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" }, + { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" }, + { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" }, + { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" }, + { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" }, + { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" }, + { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" }, + { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" }, + { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" }, + { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" }, + { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" }, + { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" }, + { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" }, + { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, +] + [[package]] name = "h11" version = "0.16.0" @@ -929,6 +1160,123 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", size = 69667, upload-time = "2025-09-02T15:23:09.635Z" }, ] +[[package]] +name = "multidict" +version = "6.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/80/1e/5492c365f222f907de1039b91f922b93fa4f764c713ee858d235495d8f50/multidict-6.7.0.tar.gz", hash = "sha256:c6e99d9a65ca282e578dfea819cfa9c0a62b2499d8677392e09feaf305e9e6f5", size = 101834, upload-time = "2025-10-06T14:52:30.657Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/34/9e/5c727587644d67b2ed479041e4b1c58e30afc011e3d45d25bbe35781217c/multidict-6.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4d409aa42a94c0b3fa617708ef5276dfe81012ba6753a0370fcc9d0195d0a1fc", size = 76604, upload-time = "2025-10-06T14:48:54.277Z" }, + { url = "https://files.pythonhosted.org/packages/17/e4/67b5c27bd17c085a5ea8f1ec05b8a3e5cba0ca734bfcad5560fb129e70ca/multidict-6.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:14c9e076eede3b54c636f8ce1c9c252b5f057c62131211f0ceeec273810c9721", size = 44715, upload-time = "2025-10-06T14:48:55.445Z" }, + { url = "https://files.pythonhosted.org/packages/4d/e1/866a5d77be6ea435711bef2a4291eed11032679b6b28b56b4776ab06ba3e/multidict-6.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c09703000a9d0fa3c3404b27041e574cc7f4df4c6563873246d0e11812a94b6", size = 44332, upload-time = "2025-10-06T14:48:56.706Z" }, + { url = "https://files.pythonhosted.org/packages/31/61/0c2d50241ada71ff61a79518db85ada85fdabfcf395d5968dae1cbda04e5/multidict-6.7.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a265acbb7bb33a3a2d626afbe756371dce0279e7b17f4f4eda406459c2b5ff1c", size = 245212, upload-time = "2025-10-06T14:48:58.042Z" }, + { url = "https://files.pythonhosted.org/packages/ac/e0/919666a4e4b57fff1b57f279be1c9316e6cdc5de8a8b525d76f6598fefc7/multidict-6.7.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51cb455de290ae462593e5b1cb1118c5c22ea7f0d3620d9940bf695cea5a4bd7", size = 246671, upload-time = "2025-10-06T14:49:00.004Z" }, + { url = "https://files.pythonhosted.org/packages/a1/cc/d027d9c5a520f3321b65adea289b965e7bcbd2c34402663f482648c716ce/multidict-6.7.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:db99677b4457c7a5c5a949353e125ba72d62b35f74e26da141530fbb012218a7", size = 225491, upload-time = "2025-10-06T14:49:01.393Z" }, + { url = "https://files.pythonhosted.org/packages/75/c4/bbd633980ce6155a28ff04e6a6492dd3335858394d7bb752d8b108708558/multidict-6.7.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f470f68adc395e0183b92a2f4689264d1ea4b40504a24d9882c27375e6662bb9", size = 257322, upload-time = "2025-10-06T14:49:02.745Z" }, + { url = "https://files.pythonhosted.org/packages/4c/6d/d622322d344f1f053eae47e033b0b3f965af01212de21b10bcf91be991fb/multidict-6.7.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0db4956f82723cc1c270de9c6e799b4c341d327762ec78ef82bb962f79cc07d8", size = 254694, upload-time = "2025-10-06T14:49:04.15Z" }, + { url = "https://files.pythonhosted.org/packages/a8/9f/78f8761c2705d4c6d7516faed63c0ebdac569f6db1bef95e0d5218fdc146/multidict-6.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e56d780c238f9e1ae66a22d2adf8d16f485381878250db8d496623cd38b22bd", size = 246715, upload-time = "2025-10-06T14:49:05.967Z" }, + { url = "https://files.pythonhosted.org/packages/78/59/950818e04f91b9c2b95aab3d923d9eabd01689d0dcd889563988e9ea0fd8/multidict-6.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9d14baca2ee12c1a64740d4531356ba50b82543017f3ad6de0deb943c5979abb", size = 243189, upload-time = "2025-10-06T14:49:07.37Z" }, + { url = "https://files.pythonhosted.org/packages/7a/3d/77c79e1934cad2ee74991840f8a0110966d9599b3af95964c0cd79bb905b/multidict-6.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:295a92a76188917c7f99cda95858c822f9e4aae5824246bba9b6b44004ddd0a6", size = 237845, upload-time = "2025-10-06T14:49:08.759Z" }, + { url = "https://files.pythonhosted.org/packages/63/1b/834ce32a0a97a3b70f86437f685f880136677ac00d8bce0027e9fd9c2db7/multidict-6.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39f1719f57adbb767ef592a50ae5ebb794220d1188f9ca93de471336401c34d2", size = 246374, upload-time = "2025-10-06T14:49:10.574Z" }, + { url = "https://files.pythonhosted.org/packages/23/ef/43d1c3ba205b5dec93dc97f3fba179dfa47910fc73aaaea4f7ceb41cec2a/multidict-6.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0a13fb8e748dfc94749f622de065dd5c1def7e0d2216dba72b1d8069a389c6ff", size = 253345, upload-time = "2025-10-06T14:49:12.331Z" }, + { url = "https://files.pythonhosted.org/packages/6b/03/eaf95bcc2d19ead522001f6a650ef32811aa9e3624ff0ad37c445c7a588c/multidict-6.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e3aa16de190d29a0ea1b48253c57d99a68492c8dd8948638073ab9e74dc9410b", size = 246940, upload-time = "2025-10-06T14:49:13.821Z" }, + { url = "https://files.pythonhosted.org/packages/e8/df/ec8a5fd66ea6cd6f525b1fcbb23511b033c3e9bc42b81384834ffa484a62/multidict-6.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a048ce45dcdaaf1defb76b2e684f997fb5abf74437b6cb7b22ddad934a964e34", size = 242229, upload-time = "2025-10-06T14:49:15.603Z" }, + { url = "https://files.pythonhosted.org/packages/8a/a2/59b405d59fd39ec86d1142630e9049243015a5f5291ba49cadf3c090c541/multidict-6.7.0-cp311-cp311-win32.whl", hash = "sha256:a90af66facec4cebe4181b9e62a68be65e45ac9b52b67de9eec118701856e7ff", size = 41308, upload-time = "2025-10-06T14:49:16.871Z" }, + { url = "https://files.pythonhosted.org/packages/32/0f/13228f26f8b882c34da36efa776c3b7348455ec383bab4a66390e42963ae/multidict-6.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:95b5ffa4349df2887518bb839409bcf22caa72d82beec453216802f475b23c81", size = 46037, upload-time = "2025-10-06T14:49:18.457Z" }, + { url = "https://files.pythonhosted.org/packages/84/1f/68588e31b000535a3207fd3c909ebeec4fb36b52c442107499c18a896a2a/multidict-6.7.0-cp311-cp311-win_arm64.whl", hash = "sha256:329aa225b085b6f004a4955271a7ba9f1087e39dcb7e65f6284a988264a63912", size = 43023, upload-time = "2025-10-06T14:49:19.648Z" }, + { url = "https://files.pythonhosted.org/packages/c2/9e/9f61ac18d9c8b475889f32ccfa91c9f59363480613fc807b6e3023d6f60b/multidict-6.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8a3862568a36d26e650a19bb5cbbba14b71789032aebc0423f8cc5f150730184", size = 76877, upload-time = "2025-10-06T14:49:20.884Z" }, + { url = "https://files.pythonhosted.org/packages/38/6f/614f09a04e6184f8824268fce4bc925e9849edfa654ddd59f0b64508c595/multidict-6.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:960c60b5849b9b4f9dcc9bea6e3626143c252c74113df2c1540aebce70209b45", size = 45467, upload-time = "2025-10-06T14:49:22.054Z" }, + { url = "https://files.pythonhosted.org/packages/b3/93/c4f67a436dd026f2e780c433277fff72be79152894d9fc36f44569cab1a6/multidict-6.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2049be98fb57a31b4ccf870bf377af2504d4ae35646a19037ec271e4c07998aa", size = 43834, upload-time = "2025-10-06T14:49:23.566Z" }, + { url = "https://files.pythonhosted.org/packages/7f/f5/013798161ca665e4a422afbc5e2d9e4070142a9ff8905e482139cd09e4d0/multidict-6.7.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0934f3843a1860dd465d38895c17fce1f1cb37295149ab05cd1b9a03afacb2a7", size = 250545, upload-time = "2025-10-06T14:49:24.882Z" }, + { url = "https://files.pythonhosted.org/packages/71/2f/91dbac13e0ba94669ea5119ba267c9a832f0cb65419aca75549fcf09a3dc/multidict-6.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3e34f3a1b8131ba06f1a73adab24f30934d148afcd5f5de9a73565a4404384e", size = 258305, upload-time = "2025-10-06T14:49:26.778Z" }, + { url = "https://files.pythonhosted.org/packages/ef/b0/754038b26f6e04488b48ac621f779c341338d78503fb45403755af2df477/multidict-6.7.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:efbb54e98446892590dc2458c19c10344ee9a883a79b5cec4bc34d6656e8d546", size = 242363, upload-time = "2025-10-06T14:49:28.562Z" }, + { url = "https://files.pythonhosted.org/packages/87/15/9da40b9336a7c9fa606c4cf2ed80a649dffeb42b905d4f63a1d7eb17d746/multidict-6.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a35c5fc61d4f51eb045061e7967cfe3123d622cd500e8868e7c0c592a09fedc4", size = 268375, upload-time = "2025-10-06T14:49:29.96Z" }, + { url = "https://files.pythonhosted.org/packages/82/72/c53fcade0cc94dfaad583105fd92b3a783af2091eddcb41a6d5a52474000/multidict-6.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29fe6740ebccba4175af1b9b87bf553e9c15cd5868ee967e010efcf94e4fd0f1", size = 269346, upload-time = "2025-10-06T14:49:31.404Z" }, + { url = "https://files.pythonhosted.org/packages/0d/e2/9baffdae21a76f77ef8447f1a05a96ec4bc0a24dae08767abc0a2fe680b8/multidict-6.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:123e2a72e20537add2f33a79e605f6191fba2afda4cbb876e35c1a7074298a7d", size = 256107, upload-time = "2025-10-06T14:49:32.974Z" }, + { url = "https://files.pythonhosted.org/packages/3c/06/3f06f611087dc60d65ef775f1fb5aca7c6d61c6db4990e7cda0cef9b1651/multidict-6.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b284e319754366c1aee2267a2036248b24eeb17ecd5dc16022095e747f2f4304", size = 253592, upload-time = "2025-10-06T14:49:34.52Z" }, + { url = "https://files.pythonhosted.org/packages/20/24/54e804ec7945b6023b340c412ce9c3f81e91b3bf5fa5ce65558740141bee/multidict-6.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:803d685de7be4303b5a657b76e2f6d1240e7e0a8aa2968ad5811fa2285553a12", size = 251024, upload-time = "2025-10-06T14:49:35.956Z" }, + { url = "https://files.pythonhosted.org/packages/14/48/011cba467ea0b17ceb938315d219391d3e421dfd35928e5dbdc3f4ae76ef/multidict-6.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c04a328260dfd5db8c39538f999f02779012268f54614902d0afc775d44e0a62", size = 251484, upload-time = "2025-10-06T14:49:37.631Z" }, + { url = "https://files.pythonhosted.org/packages/0d/2f/919258b43bb35b99fa127435cfb2d91798eb3a943396631ef43e3720dcf4/multidict-6.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8a19cdb57cd3df4cd865849d93ee14920fb97224300c88501f16ecfa2604b4e0", size = 263579, upload-time = "2025-10-06T14:49:39.502Z" }, + { url = "https://files.pythonhosted.org/packages/31/22/a0e884d86b5242b5a74cf08e876bdf299e413016b66e55511f7a804a366e/multidict-6.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b2fd74c52accced7e75de26023b7dccee62511a600e62311b918ec5c168fc2a", size = 259654, upload-time = "2025-10-06T14:49:41.32Z" }, + { url = "https://files.pythonhosted.org/packages/b2/e5/17e10e1b5c5f5a40f2fcbb45953c9b215f8a4098003915e46a93f5fcaa8f/multidict-6.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3e8bfdd0e487acf992407a140d2589fe598238eaeffa3da8448d63a63cd363f8", size = 251511, upload-time = "2025-10-06T14:49:46.021Z" }, + { url = "https://files.pythonhosted.org/packages/e3/9a/201bb1e17e7af53139597069c375e7b0dcbd47594604f65c2d5359508566/multidict-6.7.0-cp312-cp312-win32.whl", hash = "sha256:dd32a49400a2c3d52088e120ee00c1e3576cbff7e10b98467962c74fdb762ed4", size = 41895, upload-time = "2025-10-06T14:49:48.718Z" }, + { url = "https://files.pythonhosted.org/packages/46/e2/348cd32faad84eaf1d20cce80e2bb0ef8d312c55bca1f7fa9865e7770aaf/multidict-6.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:92abb658ef2d7ef22ac9f8bb88e8b6c3e571671534e029359b6d9e845923eb1b", size = 46073, upload-time = "2025-10-06T14:49:50.28Z" }, + { url = "https://files.pythonhosted.org/packages/25/ec/aad2613c1910dce907480e0c3aa306905830f25df2e54ccc9dea450cb5aa/multidict-6.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:490dab541a6a642ce1a9d61a4781656b346a55c13038f0b1244653828e3a83ec", size = 43226, upload-time = "2025-10-06T14:49:52.304Z" }, + { url = "https://files.pythonhosted.org/packages/d2/86/33272a544eeb36d66e4d9a920602d1a2f57d4ebea4ef3cdfe5a912574c95/multidict-6.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bee7c0588aa0076ce77c0ea5d19a68d76ad81fcd9fe8501003b9a24f9d4000f6", size = 76135, upload-time = "2025-10-06T14:49:54.26Z" }, + { url = "https://files.pythonhosted.org/packages/91/1c/eb97db117a1ebe46d457a3d235a7b9d2e6dcab174f42d1b67663dd9e5371/multidict-6.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7ef6b61cad77091056ce0e7ce69814ef72afacb150b7ac6a3e9470def2198159", size = 45117, upload-time = "2025-10-06T14:49:55.82Z" }, + { url = "https://files.pythonhosted.org/packages/f1/d8/6c3442322e41fb1dd4de8bd67bfd11cd72352ac131f6368315617de752f1/multidict-6.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c0359b1ec12b1d6849c59f9d319610b7f20ef990a6d454ab151aa0e3b9f78ca", size = 43472, upload-time = "2025-10-06T14:49:57.048Z" }, + { url = "https://files.pythonhosted.org/packages/75/3f/e2639e80325af0b6c6febdf8e57cc07043ff15f57fa1ef808f4ccb5ac4cd/multidict-6.7.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cd240939f71c64bd658f186330603aac1a9a81bf6273f523fca63673cb7378a8", size = 249342, upload-time = "2025-10-06T14:49:58.368Z" }, + { url = "https://files.pythonhosted.org/packages/5d/cc/84e0585f805cbeaa9cbdaa95f9a3d6aed745b9d25700623ac89a6ecff400/multidict-6.7.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60a4d75718a5efa473ebd5ab685786ba0c67b8381f781d1be14da49f1a2dc60", size = 257082, upload-time = "2025-10-06T14:49:59.89Z" }, + { url = "https://files.pythonhosted.org/packages/b0/9c/ac851c107c92289acbbf5cfb485694084690c1b17e555f44952c26ddc5bd/multidict-6.7.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53a42d364f323275126aff81fb67c5ca1b7a04fda0546245730a55c8c5f24bc4", size = 240704, upload-time = "2025-10-06T14:50:01.485Z" }, + { url = "https://files.pythonhosted.org/packages/50/cc/5f93e99427248c09da95b62d64b25748a5f5c98c7c2ab09825a1d6af0e15/multidict-6.7.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3b29b980d0ddbecb736735ee5bef69bb2ddca56eff603c86f3f29a1128299b4f", size = 266355, upload-time = "2025-10-06T14:50:02.955Z" }, + { url = "https://files.pythonhosted.org/packages/ec/0c/2ec1d883ceb79c6f7f6d7ad90c919c898f5d1c6ea96d322751420211e072/multidict-6.7.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f8a93b1c0ed2d04b97a5e9336fd2d33371b9a6e29ab7dd6503d63407c20ffbaf", size = 267259, upload-time = "2025-10-06T14:50:04.446Z" }, + { url = "https://files.pythonhosted.org/packages/c6/2d/f0b184fa88d6630aa267680bdb8623fb69cb0d024b8c6f0d23f9a0f406d3/multidict-6.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ff96e8815eecacc6645da76c413eb3b3d34cfca256c70b16b286a687d013c32", size = 254903, upload-time = "2025-10-06T14:50:05.98Z" }, + { url = "https://files.pythonhosted.org/packages/06/c9/11ea263ad0df7dfabcad404feb3c0dd40b131bc7f232d5537f2fb1356951/multidict-6.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7516c579652f6a6be0e266aec0acd0db80829ca305c3d771ed898538804c2036", size = 252365, upload-time = "2025-10-06T14:50:07.511Z" }, + { url = "https://files.pythonhosted.org/packages/41/88/d714b86ee2c17d6e09850c70c9d310abac3d808ab49dfa16b43aba9d53fd/multidict-6.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:040f393368e63fb0f3330e70c26bfd336656bed925e5cbe17c9da839a6ab13ec", size = 250062, upload-time = "2025-10-06T14:50:09.074Z" }, + { url = "https://files.pythonhosted.org/packages/15/fe/ad407bb9e818c2b31383f6131ca19ea7e35ce93cf1310fce69f12e89de75/multidict-6.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b3bc26a951007b1057a1c543af845f1c7e3e71cc240ed1ace7bf4484aa99196e", size = 249683, upload-time = "2025-10-06T14:50:10.714Z" }, + { url = "https://files.pythonhosted.org/packages/8c/a4/a89abdb0229e533fb925e7c6e5c40201c2873efebc9abaf14046a4536ee6/multidict-6.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7b022717c748dd1992a83e219587aabe45980d88969f01b316e78683e6285f64", size = 261254, upload-time = "2025-10-06T14:50:12.28Z" }, + { url = "https://files.pythonhosted.org/packages/8d/aa/0e2b27bd88b40a4fb8dc53dd74eecac70edaa4c1dd0707eb2164da3675b3/multidict-6.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:9600082733859f00d79dee64effc7aef1beb26adb297416a4ad2116fd61374bd", size = 257967, upload-time = "2025-10-06T14:50:14.16Z" }, + { url = "https://files.pythonhosted.org/packages/d0/8e/0c67b7120d5d5f6d874ed85a085f9dc770a7f9d8813e80f44a9fec820bb7/multidict-6.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:94218fcec4d72bc61df51c198d098ce2b378e0ccbac41ddbed5ef44092913288", size = 250085, upload-time = "2025-10-06T14:50:15.639Z" }, + { url = "https://files.pythonhosted.org/packages/ba/55/b73e1d624ea4b8fd4dd07a3bb70f6e4c7c6c5d9d640a41c6ffe5cdbd2a55/multidict-6.7.0-cp313-cp313-win32.whl", hash = "sha256:a37bd74c3fa9d00be2d7b8eca074dc56bd8077ddd2917a839bd989612671ed17", size = 41713, upload-time = "2025-10-06T14:50:17.066Z" }, + { url = "https://files.pythonhosted.org/packages/32/31/75c59e7d3b4205075b4c183fa4ca398a2daf2303ddf616b04ae6ef55cffe/multidict-6.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:30d193c6cc6d559db42b6bcec8a5d395d34d60c9877a0b71ecd7c204fcf15390", size = 45915, upload-time = "2025-10-06T14:50:18.264Z" }, + { url = "https://files.pythonhosted.org/packages/31/2a/8987831e811f1184c22bc2e45844934385363ee61c0a2dcfa8f71b87e608/multidict-6.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:ea3334cabe4d41b7ccd01e4d349828678794edbc2d3ae97fc162a3312095092e", size = 43077, upload-time = "2025-10-06T14:50:19.853Z" }, + { url = "https://files.pythonhosted.org/packages/e8/68/7b3a5170a382a340147337b300b9eb25a9ddb573bcdfff19c0fa3f31ffba/multidict-6.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:ad9ce259f50abd98a1ca0aa6e490b58c316a0fce0617f609723e40804add2c00", size = 83114, upload-time = "2025-10-06T14:50:21.223Z" }, + { url = "https://files.pythonhosted.org/packages/55/5c/3fa2d07c84df4e302060f555bbf539310980362236ad49f50eeb0a1c1eb9/multidict-6.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07f5594ac6d084cbb5de2df218d78baf55ef150b91f0ff8a21cc7a2e3a5a58eb", size = 48442, upload-time = "2025-10-06T14:50:22.871Z" }, + { url = "https://files.pythonhosted.org/packages/fc/56/67212d33239797f9bd91962bb899d72bb0f4c35a8652dcdb8ed049bef878/multidict-6.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0591b48acf279821a579282444814a2d8d0af624ae0bc600aa4d1b920b6e924b", size = 46885, upload-time = "2025-10-06T14:50:24.258Z" }, + { url = "https://files.pythonhosted.org/packages/46/d1/908f896224290350721597a61a69cd19b89ad8ee0ae1f38b3f5cd12ea2ac/multidict-6.7.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:749a72584761531d2b9467cfbdfd29487ee21124c304c4b6cb760d8777b27f9c", size = 242588, upload-time = "2025-10-06T14:50:25.716Z" }, + { url = "https://files.pythonhosted.org/packages/ab/67/8604288bbd68680eee0ab568fdcb56171d8b23a01bcd5cb0c8fedf6e5d99/multidict-6.7.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b4c3d199f953acd5b446bf7c0de1fe25d94e09e79086f8dc2f48a11a129cdf1", size = 249966, upload-time = "2025-10-06T14:50:28.192Z" }, + { url = "https://files.pythonhosted.org/packages/20/33/9228d76339f1ba51e3efef7da3ebd91964d3006217aae13211653193c3ff/multidict-6.7.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9fb0211dfc3b51efea2f349ec92c114d7754dd62c01f81c3e32b765b70c45c9b", size = 228618, upload-time = "2025-10-06T14:50:29.82Z" }, + { url = "https://files.pythonhosted.org/packages/f8/2d/25d9b566d10cab1c42b3b9e5b11ef79c9111eaf4463b8c257a3bd89e0ead/multidict-6.7.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a027ec240fe73a8d6281872690b988eed307cd7d91b23998ff35ff577ca688b5", size = 257539, upload-time = "2025-10-06T14:50:31.731Z" }, + { url = "https://files.pythonhosted.org/packages/b6/b1/8d1a965e6637fc33de3c0d8f414485c2b7e4af00f42cab3d84e7b955c222/multidict-6.7.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1d964afecdf3a8288789df2f5751dc0a8261138c3768d9af117ed384e538fad", size = 256345, upload-time = "2025-10-06T14:50:33.26Z" }, + { url = "https://files.pythonhosted.org/packages/ba/0c/06b5a8adbdeedada6f4fb8d8f193d44a347223b11939b42953eeb6530b6b/multidict-6.7.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:caf53b15b1b7df9fbd0709aa01409000a2b4dd03a5f6f5cc548183c7c8f8b63c", size = 247934, upload-time = "2025-10-06T14:50:34.808Z" }, + { url = "https://files.pythonhosted.org/packages/8f/31/b2491b5fe167ca044c6eb4b8f2c9f3b8a00b24c432c365358eadac5d7625/multidict-6.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:654030da3197d927f05a536a66186070e98765aa5142794c9904555d3a9d8fb5", size = 245243, upload-time = "2025-10-06T14:50:36.436Z" }, + { url = "https://files.pythonhosted.org/packages/61/1a/982913957cb90406c8c94f53001abd9eafc271cb3e70ff6371590bec478e/multidict-6.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:2090d3718829d1e484706a2f525e50c892237b2bf9b17a79b059cb98cddc2f10", size = 235878, upload-time = "2025-10-06T14:50:37.953Z" }, + { url = "https://files.pythonhosted.org/packages/be/c0/21435d804c1a1cf7a2608593f4d19bca5bcbd7a81a70b253fdd1c12af9c0/multidict-6.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2d2cfeec3f6f45651b3d408c4acec0ebf3daa9bc8a112a084206f5db5d05b754", size = 243452, upload-time = "2025-10-06T14:50:39.574Z" }, + { url = "https://files.pythonhosted.org/packages/54/0a/4349d540d4a883863191be6eb9a928846d4ec0ea007d3dcd36323bb058ac/multidict-6.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:4ef089f985b8c194d341eb2c24ae6e7408c9a0e2e5658699c92f497437d88c3c", size = 252312, upload-time = "2025-10-06T14:50:41.612Z" }, + { url = "https://files.pythonhosted.org/packages/26/64/d5416038dbda1488daf16b676e4dbfd9674dde10a0cc8f4fc2b502d8125d/multidict-6.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e93a0617cd16998784bf4414c7e40f17a35d2350e5c6f0bd900d3a8e02bd3762", size = 246935, upload-time = "2025-10-06T14:50:43.972Z" }, + { url = "https://files.pythonhosted.org/packages/9f/8c/8290c50d14e49f35e0bd4abc25e1bc7711149ca9588ab7d04f886cdf03d9/multidict-6.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f0feece2ef8ebc42ed9e2e8c78fc4aa3cf455733b507c09ef7406364c94376c6", size = 243385, upload-time = "2025-10-06T14:50:45.648Z" }, + { url = "https://files.pythonhosted.org/packages/ef/a0/f83ae75e42d694b3fbad3e047670e511c138be747bc713cf1b10d5096416/multidict-6.7.0-cp313-cp313t-win32.whl", hash = "sha256:19a1d55338ec1be74ef62440ca9e04a2f001a04d0cc49a4983dc320ff0f3212d", size = 47777, upload-time = "2025-10-06T14:50:47.154Z" }, + { url = "https://files.pythonhosted.org/packages/dc/80/9b174a92814a3830b7357307a792300f42c9e94664b01dee8e457551fa66/multidict-6.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3da4fb467498df97e986af166b12d01f05d2e04f978a9c1c680ea1988e0bc4b6", size = 53104, upload-time = "2025-10-06T14:50:48.851Z" }, + { url = "https://files.pythonhosted.org/packages/cc/28/04baeaf0428d95bb7a7bea0e691ba2f31394338ba424fb0679a9ed0f4c09/multidict-6.7.0-cp313-cp313t-win_arm64.whl", hash = "sha256:b4121773c49a0776461f4a904cdf6264c88e42218aaa8407e803ca8025872792", size = 45503, upload-time = "2025-10-06T14:50:50.16Z" }, + { url = "https://files.pythonhosted.org/packages/e2/b1/3da6934455dd4b261d4c72f897e3a5728eba81db59959f3a639245891baa/multidict-6.7.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3bab1e4aff7adaa34410f93b1f8e57c4b36b9af0426a76003f441ee1d3c7e842", size = 75128, upload-time = "2025-10-06T14:50:51.92Z" }, + { url = "https://files.pythonhosted.org/packages/14/2c/f069cab5b51d175a1a2cb4ccdf7a2c2dabd58aa5bd933fa036a8d15e2404/multidict-6.7.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b8512bac933afc3e45fb2b18da8e59b78d4f408399a960339598374d4ae3b56b", size = 44410, upload-time = "2025-10-06T14:50:53.275Z" }, + { url = "https://files.pythonhosted.org/packages/42/e2/64bb41266427af6642b6b128e8774ed84c11b80a90702c13ac0a86bb10cc/multidict-6.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:79dcf9e477bc65414ebfea98ffd013cb39552b5ecd62908752e0e413d6d06e38", size = 43205, upload-time = "2025-10-06T14:50:54.911Z" }, + { url = "https://files.pythonhosted.org/packages/02/68/6b086fef8a3f1a8541b9236c594f0c9245617c29841f2e0395d979485cde/multidict-6.7.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:31bae522710064b5cbeddaf2e9f32b1abab70ac6ac91d42572502299e9953128", size = 245084, upload-time = "2025-10-06T14:50:56.369Z" }, + { url = "https://files.pythonhosted.org/packages/15/ee/f524093232007cd7a75c1d132df70f235cfd590a7c9eaccd7ff422ef4ae8/multidict-6.7.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a0df7ff02397bb63e2fd22af2c87dfa39e8c7f12947bc524dbdc528282c7e34", size = 252667, upload-time = "2025-10-06T14:50:57.991Z" }, + { url = "https://files.pythonhosted.org/packages/02/a5/eeb3f43ab45878f1895118c3ef157a480db58ede3f248e29b5354139c2c9/multidict-6.7.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7a0222514e8e4c514660e182d5156a415c13ef0aabbd71682fc714e327b95e99", size = 233590, upload-time = "2025-10-06T14:50:59.589Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1e/76d02f8270b97269d7e3dbd45644b1785bda457b474315f8cf999525a193/multidict-6.7.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2397ab4daaf2698eb51a76721e98db21ce4f52339e535725de03ea962b5a3202", size = 264112, upload-time = "2025-10-06T14:51:01.183Z" }, + { url = "https://files.pythonhosted.org/packages/76/0b/c28a70ecb58963847c2a8efe334904cd254812b10e535aefb3bcce513918/multidict-6.7.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8891681594162635948a636c9fe0ff21746aeb3dd5463f6e25d9bea3a8a39ca1", size = 261194, upload-time = "2025-10-06T14:51:02.794Z" }, + { url = "https://files.pythonhosted.org/packages/b4/63/2ab26e4209773223159b83aa32721b4021ffb08102f8ac7d689c943fded1/multidict-6.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18706cc31dbf402a7945916dd5cddf160251b6dab8a2c5f3d6d5a55949f676b3", size = 248510, upload-time = "2025-10-06T14:51:04.724Z" }, + { url = "https://files.pythonhosted.org/packages/93/cd/06c1fa8282af1d1c46fd55c10a7930af652afdce43999501d4d68664170c/multidict-6.7.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f844a1bbf1d207dd311a56f383f7eda2d0e134921d45751842d8235e7778965d", size = 248395, upload-time = "2025-10-06T14:51:06.306Z" }, + { url = "https://files.pythonhosted.org/packages/99/ac/82cb419dd6b04ccf9e7e61befc00c77614fc8134362488b553402ecd55ce/multidict-6.7.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d4393e3581e84e5645506923816b9cc81f5609a778c7e7534054091acc64d1c6", size = 239520, upload-time = "2025-10-06T14:51:08.091Z" }, + { url = "https://files.pythonhosted.org/packages/fa/f3/a0f9bf09493421bd8716a362e0cd1d244f5a6550f5beffdd6b47e885b331/multidict-6.7.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:fbd18dc82d7bf274b37aa48d664534330af744e03bccf696d6f4c6042e7d19e7", size = 245479, upload-time = "2025-10-06T14:51:10.365Z" }, + { url = "https://files.pythonhosted.org/packages/8d/01/476d38fc73a212843f43c852b0eee266b6971f0e28329c2184a8df90c376/multidict-6.7.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:b6234e14f9314731ec45c42fc4554b88133ad53a09092cc48a88e771c125dadb", size = 258903, upload-time = "2025-10-06T14:51:12.466Z" }, + { url = "https://files.pythonhosted.org/packages/49/6d/23faeb0868adba613b817d0e69c5f15531b24d462af8012c4f6de4fa8dc3/multidict-6.7.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:08d4379f9744d8f78d98c8673c06e202ffa88296f009c71bbafe8a6bf847d01f", size = 252333, upload-time = "2025-10-06T14:51:14.48Z" }, + { url = "https://files.pythonhosted.org/packages/1e/cc/48d02ac22b30fa247f7dad82866e4b1015431092f4ba6ebc7e77596e0b18/multidict-6.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9fe04da3f79387f450fd0061d4dd2e45a72749d31bf634aecc9e27f24fdc4b3f", size = 243411, upload-time = "2025-10-06T14:51:16.072Z" }, + { url = "https://files.pythonhosted.org/packages/4a/03/29a8bf5a18abf1fe34535c88adbdfa88c9fb869b5a3b120692c64abe8284/multidict-6.7.0-cp314-cp314-win32.whl", hash = "sha256:fbafe31d191dfa7c4c51f7a6149c9fb7e914dcf9ffead27dcfd9f1ae382b3885", size = 40940, upload-time = "2025-10-06T14:51:17.544Z" }, + { url = "https://files.pythonhosted.org/packages/82/16/7ed27b680791b939de138f906d5cf2b4657b0d45ca6f5dd6236fdddafb1a/multidict-6.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:2f67396ec0310764b9222a1728ced1ab638f61aadc6226f17a71dd9324f9a99c", size = 45087, upload-time = "2025-10-06T14:51:18.875Z" }, + { url = "https://files.pythonhosted.org/packages/cd/3c/e3e62eb35a1950292fe39315d3c89941e30a9d07d5d2df42965ab041da43/multidict-6.7.0-cp314-cp314-win_arm64.whl", hash = "sha256:ba672b26069957ee369cfa7fc180dde1fc6f176eaf1e6beaf61fbebbd3d9c000", size = 42368, upload-time = "2025-10-06T14:51:20.225Z" }, + { url = "https://files.pythonhosted.org/packages/8b/40/cd499bd0dbc5f1136726db3153042a735fffd0d77268e2ee20d5f33c010f/multidict-6.7.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:c1dcc7524066fa918c6a27d61444d4ee7900ec635779058571f70d042d86ed63", size = 82326, upload-time = "2025-10-06T14:51:21.588Z" }, + { url = "https://files.pythonhosted.org/packages/13/8a/18e031eca251c8df76daf0288e6790561806e439f5ce99a170b4af30676b/multidict-6.7.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:27e0b36c2d388dc7b6ced3406671b401e84ad7eb0656b8f3a2f46ed0ce483718", size = 48065, upload-time = "2025-10-06T14:51:22.93Z" }, + { url = "https://files.pythonhosted.org/packages/40/71/5e6701277470a87d234e433fb0a3a7deaf3bcd92566e421e7ae9776319de/multidict-6.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2a7baa46a22e77f0988e3b23d4ede5513ebec1929e34ee9495be535662c0dfe2", size = 46475, upload-time = "2025-10-06T14:51:24.352Z" }, + { url = "https://files.pythonhosted.org/packages/fe/6a/bab00cbab6d9cfb57afe1663318f72ec28289ea03fd4e8236bb78429893a/multidict-6.7.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7bf77f54997a9166a2f5675d1201520586439424c2511723a7312bdb4bcc034e", size = 239324, upload-time = "2025-10-06T14:51:25.822Z" }, + { url = "https://files.pythonhosted.org/packages/2a/5f/8de95f629fc22a7769ade8b41028e3e5a822c1f8904f618d175945a81ad3/multidict-6.7.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e011555abada53f1578d63389610ac8a5400fc70ce71156b0aa30d326f1a5064", size = 246877, upload-time = "2025-10-06T14:51:27.604Z" }, + { url = "https://files.pythonhosted.org/packages/23/b4/38881a960458f25b89e9f4a4fdcb02ac101cfa710190db6e5528841e67de/multidict-6.7.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:28b37063541b897fd6a318007373930a75ca6d6ac7c940dbe14731ffdd8d498e", size = 225824, upload-time = "2025-10-06T14:51:29.664Z" }, + { url = "https://files.pythonhosted.org/packages/1e/39/6566210c83f8a261575f18e7144736059f0c460b362e96e9cf797a24b8e7/multidict-6.7.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05047ada7a2fde2631a0ed706f1fd68b169a681dfe5e4cf0f8e4cb6618bbc2cd", size = 253558, upload-time = "2025-10-06T14:51:31.684Z" }, + { url = "https://files.pythonhosted.org/packages/00/a3/67f18315100f64c269f46e6c0319fa87ba68f0f64f2b8e7fd7c72b913a0b/multidict-6.7.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:716133f7d1d946a4e1b91b1756b23c088881e70ff180c24e864c26192ad7534a", size = 252339, upload-time = "2025-10-06T14:51:33.699Z" }, + { url = "https://files.pythonhosted.org/packages/c8/2a/1cb77266afee2458d82f50da41beba02159b1d6b1f7973afc9a1cad1499b/multidict-6.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d1bed1b467ef657f2a0ae62844a607909ef1c6889562de5e1d505f74457d0b96", size = 244895, upload-time = "2025-10-06T14:51:36.189Z" }, + { url = "https://files.pythonhosted.org/packages/dd/72/09fa7dd487f119b2eb9524946ddd36e2067c08510576d43ff68469563b3b/multidict-6.7.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ca43bdfa5d37bd6aee89d85e1d0831fb86e25541be7e9d376ead1b28974f8e5e", size = 241862, upload-time = "2025-10-06T14:51:41.291Z" }, + { url = "https://files.pythonhosted.org/packages/65/92/bc1f8bd0853d8669300f732c801974dfc3702c3eeadae2f60cef54dc69d7/multidict-6.7.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:44b546bd3eb645fd26fb949e43c02a25a2e632e2ca21a35e2e132c8105dc8599", size = 232376, upload-time = "2025-10-06T14:51:43.55Z" }, + { url = "https://files.pythonhosted.org/packages/09/86/ac39399e5cb9d0c2ac8ef6e10a768e4d3bc933ac808d49c41f9dc23337eb/multidict-6.7.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a6ef16328011d3f468e7ebc326f24c1445f001ca1dec335b2f8e66bed3006394", size = 240272, upload-time = "2025-10-06T14:51:45.265Z" }, + { url = "https://files.pythonhosted.org/packages/3d/b6/fed5ac6b8563ec72df6cb1ea8dac6d17f0a4a1f65045f66b6d3bf1497c02/multidict-6.7.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:5aa873cbc8e593d361ae65c68f85faadd755c3295ea2c12040ee146802f23b38", size = 248774, upload-time = "2025-10-06T14:51:46.836Z" }, + { url = "https://files.pythonhosted.org/packages/6b/8d/b954d8c0dc132b68f760aefd45870978deec6818897389dace00fcde32ff/multidict-6.7.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:3d7b6ccce016e29df4b7ca819659f516f0bc7a4b3efa3bb2012ba06431b044f9", size = 242731, upload-time = "2025-10-06T14:51:48.541Z" }, + { url = "https://files.pythonhosted.org/packages/16/9d/a2dac7009125d3540c2f54e194829ea18ac53716c61b655d8ed300120b0f/multidict-6.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:171b73bd4ee683d307599b66793ac80981b06f069b62eea1c9e29c9241aa66b0", size = 240193, upload-time = "2025-10-06T14:51:50.355Z" }, + { url = "https://files.pythonhosted.org/packages/39/ca/c05f144128ea232ae2178b008d5011d4e2cea86e4ee8c85c2631b1b94802/multidict-6.7.0-cp314-cp314t-win32.whl", hash = "sha256:b2d7f80c4e1fd010b07cb26820aae86b7e73b681ee4889684fb8d2d4537aab13", size = 48023, upload-time = "2025-10-06T14:51:51.883Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8f/0a60e501584145588be1af5cc829265701ba3c35a64aec8e07cbb71d39bb/multidict-6.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:09929cab6fcb68122776d575e03c6cc64ee0b8fca48d17e135474b042ce515cd", size = 53507, upload-time = "2025-10-06T14:51:53.672Z" }, + { url = "https://files.pythonhosted.org/packages/7f/ae/3148b988a9c6239903e786eac19c889fab607c31d6efa7fb2147e5680f23/multidict-6.7.0-cp314-cp314t-win_arm64.whl", hash = "sha256:cc41db090ed742f32bd2d2c721861725e6109681eddf835d0a82bd3a5c382827", size = 44804, upload-time = "2025-10-06T14:51:55.415Z" }, + { url = "https://files.pythonhosted.org/packages/b7/da/7d22601b625e241d4f23ef1ebff8acfc60da633c9e7e7922e24d10f592b3/multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3", size = 12317, upload-time = "2025-10-06T14:52:29.272Z" }, +] + [[package]] name = "mypy-extensions" version = "1.1.0" @@ -1062,6 +1410,105 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, ] +[[package]] +name = "propcache" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/d4/4e2c9aaf7ac2242b9358f98dccd8f90f2605402f5afeff6c578682c2c491/propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf", size = 80208, upload-time = "2025-10-08T19:46:24.597Z" }, + { url = "https://files.pythonhosted.org/packages/c2/21/d7b68e911f9c8e18e4ae43bdbc1e1e9bbd971f8866eb81608947b6f585ff/propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5", size = 45777, upload-time = "2025-10-08T19:46:25.733Z" }, + { url = "https://files.pythonhosted.org/packages/d3/1d/11605e99ac8ea9435651ee71ab4cb4bf03f0949586246476a25aadfec54a/propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e", size = 47647, upload-time = "2025-10-08T19:46:27.304Z" }, + { url = "https://files.pythonhosted.org/packages/58/1a/3c62c127a8466c9c843bccb503d40a273e5cc69838805f322e2826509e0d/propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566", size = 214929, upload-time = "2025-10-08T19:46:28.62Z" }, + { url = "https://files.pythonhosted.org/packages/56/b9/8fa98f850960b367c4b8fe0592e7fc341daa7a9462e925228f10a60cf74f/propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165", size = 221778, upload-time = "2025-10-08T19:46:30.358Z" }, + { url = "https://files.pythonhosted.org/packages/46/a6/0ab4f660eb59649d14b3d3d65c439421cf2f87fe5dd68591cbe3c1e78a89/propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc", size = 228144, upload-time = "2025-10-08T19:46:32.607Z" }, + { url = "https://files.pythonhosted.org/packages/52/6a/57f43e054fb3d3a56ac9fc532bc684fc6169a26c75c353e65425b3e56eef/propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48", size = 210030, upload-time = "2025-10-08T19:46:33.969Z" }, + { url = "https://files.pythonhosted.org/packages/40/e2/27e6feebb5f6b8408fa29f5efbb765cd54c153ac77314d27e457a3e993b7/propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570", size = 208252, upload-time = "2025-10-08T19:46:35.309Z" }, + { url = "https://files.pythonhosted.org/packages/9e/f8/91c27b22ccda1dbc7967f921c42825564fa5336a01ecd72eb78a9f4f53c2/propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85", size = 202064, upload-time = "2025-10-08T19:46:36.993Z" }, + { url = "https://files.pythonhosted.org/packages/f2/26/7f00bd6bd1adba5aafe5f4a66390f243acab58eab24ff1a08bebb2ef9d40/propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e", size = 212429, upload-time = "2025-10-08T19:46:38.398Z" }, + { url = "https://files.pythonhosted.org/packages/84/89/fd108ba7815c1117ddca79c228f3f8a15fc82a73bca8b142eb5de13b2785/propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757", size = 216727, upload-time = "2025-10-08T19:46:39.732Z" }, + { url = "https://files.pythonhosted.org/packages/79/37/3ec3f7e3173e73f1d600495d8b545b53802cbf35506e5732dd8578db3724/propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f", size = 205097, upload-time = "2025-10-08T19:46:41.025Z" }, + { url = "https://files.pythonhosted.org/packages/61/b0/b2631c19793f869d35f47d5a3a56fb19e9160d3c119f15ac7344fc3ccae7/propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1", size = 38084, upload-time = "2025-10-08T19:46:42.693Z" }, + { url = "https://files.pythonhosted.org/packages/f4/78/6cce448e2098e9f3bfc91bb877f06aa24b6ccace872e39c53b2f707c4648/propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6", size = 41637, upload-time = "2025-10-08T19:46:43.778Z" }, + { url = "https://files.pythonhosted.org/packages/9c/e9/754f180cccd7f51a39913782c74717c581b9cc8177ad0e949f4d51812383/propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239", size = 38064, upload-time = "2025-10-08T19:46:44.872Z" }, + { url = "https://files.pythonhosted.org/packages/a2/0f/f17b1b2b221d5ca28b4b876e8bb046ac40466513960646bda8e1853cdfa2/propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2", size = 80061, upload-time = "2025-10-08T19:46:46.075Z" }, + { url = "https://files.pythonhosted.org/packages/76/47/8ccf75935f51448ba9a16a71b783eb7ef6b9ee60f5d14c7f8a8a79fbeed7/propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403", size = 46037, upload-time = "2025-10-08T19:46:47.23Z" }, + { url = "https://files.pythonhosted.org/packages/0a/b6/5c9a0e42df4d00bfb4a3cbbe5cf9f54260300c88a0e9af1f47ca5ce17ac0/propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207", size = 47324, upload-time = "2025-10-08T19:46:48.384Z" }, + { url = "https://files.pythonhosted.org/packages/9e/d3/6c7ee328b39a81ee877c962469f1e795f9db87f925251efeb0545e0020d0/propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72", size = 225505, upload-time = "2025-10-08T19:46:50.055Z" }, + { url = "https://files.pythonhosted.org/packages/01/5d/1c53f4563490b1d06a684742cc6076ef944bc6457df6051b7d1a877c057b/propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367", size = 230242, upload-time = "2025-10-08T19:46:51.815Z" }, + { url = "https://files.pythonhosted.org/packages/20/e1/ce4620633b0e2422207c3cb774a0ee61cac13abc6217763a7b9e2e3f4a12/propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4", size = 238474, upload-time = "2025-10-08T19:46:53.208Z" }, + { url = "https://files.pythonhosted.org/packages/46/4b/3aae6835b8e5f44ea6a68348ad90f78134047b503765087be2f9912140ea/propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf", size = 221575, upload-time = "2025-10-08T19:46:54.511Z" }, + { url = "https://files.pythonhosted.org/packages/6e/a5/8a5e8678bcc9d3a1a15b9a29165640d64762d424a16af543f00629c87338/propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3", size = 216736, upload-time = "2025-10-08T19:46:56.212Z" }, + { url = "https://files.pythonhosted.org/packages/f1/63/b7b215eddeac83ca1c6b934f89d09a625aa9ee4ba158338854c87210cc36/propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778", size = 213019, upload-time = "2025-10-08T19:46:57.595Z" }, + { url = "https://files.pythonhosted.org/packages/57/74/f580099a58c8af587cac7ba19ee7cb418506342fbbe2d4a4401661cca886/propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6", size = 220376, upload-time = "2025-10-08T19:46:59.067Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ee/542f1313aff7eaf19c2bb758c5d0560d2683dac001a1c96d0774af799843/propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9", size = 226988, upload-time = "2025-10-08T19:47:00.544Z" }, + { url = "https://files.pythonhosted.org/packages/8f/18/9c6b015dd9c6930f6ce2229e1f02fb35298b847f2087ea2b436a5bfa7287/propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75", size = 215615, upload-time = "2025-10-08T19:47:01.968Z" }, + { url = "https://files.pythonhosted.org/packages/80/9e/e7b85720b98c45a45e1fca6a177024934dc9bc5f4d5dd04207f216fc33ed/propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8", size = 38066, upload-time = "2025-10-08T19:47:03.503Z" }, + { url = "https://files.pythonhosted.org/packages/54/09/d19cff2a5aaac632ec8fc03737b223597b1e347416934c1b3a7df079784c/propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db", size = 41655, upload-time = "2025-10-08T19:47:04.973Z" }, + { url = "https://files.pythonhosted.org/packages/68/ab/6b5c191bb5de08036a8c697b265d4ca76148efb10fa162f14af14fb5f076/propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1", size = 37789, upload-time = "2025-10-08T19:47:06.077Z" }, + { url = "https://files.pythonhosted.org/packages/bf/df/6d9c1b6ac12b003837dde8a10231a7344512186e87b36e855bef32241942/propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf", size = 77750, upload-time = "2025-10-08T19:47:07.648Z" }, + { url = "https://files.pythonhosted.org/packages/8b/e8/677a0025e8a2acf07d3418a2e7ba529c9c33caf09d3c1f25513023c1db56/propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311", size = 44780, upload-time = "2025-10-08T19:47:08.851Z" }, + { url = "https://files.pythonhosted.org/packages/89/a4/92380f7ca60f99ebae761936bc48a72a639e8a47b29050615eef757cb2a7/propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74", size = 46308, upload-time = "2025-10-08T19:47:09.982Z" }, + { url = "https://files.pythonhosted.org/packages/2d/48/c5ac64dee5262044348d1d78a5f85dd1a57464a60d30daee946699963eb3/propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe", size = 208182, upload-time = "2025-10-08T19:47:11.319Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0c/cd762dd011a9287389a6a3eb43aa30207bde253610cca06824aeabfe9653/propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af", size = 211215, upload-time = "2025-10-08T19:47:13.146Z" }, + { url = "https://files.pythonhosted.org/packages/30/3e/49861e90233ba36890ae0ca4c660e95df565b2cd15d4a68556ab5865974e/propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c", size = 218112, upload-time = "2025-10-08T19:47:14.913Z" }, + { url = "https://files.pythonhosted.org/packages/f1/8b/544bc867e24e1bd48f3118cecd3b05c694e160a168478fa28770f22fd094/propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f", size = 204442, upload-time = "2025-10-08T19:47:16.277Z" }, + { url = "https://files.pythonhosted.org/packages/50/a6/4282772fd016a76d3e5c0df58380a5ea64900afd836cec2c2f662d1b9bb3/propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1", size = 199398, upload-time = "2025-10-08T19:47:17.962Z" }, + { url = "https://files.pythonhosted.org/packages/3e/ec/d8a7cd406ee1ddb705db2139f8a10a8a427100347bd698e7014351c7af09/propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24", size = 196920, upload-time = "2025-10-08T19:47:19.355Z" }, + { url = "https://files.pythonhosted.org/packages/f6/6c/f38ab64af3764f431e359f8baf9e0a21013e24329e8b85d2da32e8ed07ca/propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa", size = 203748, upload-time = "2025-10-08T19:47:21.338Z" }, + { url = "https://files.pythonhosted.org/packages/d6/e3/fa846bd70f6534d647886621388f0a265254d30e3ce47e5c8e6e27dbf153/propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61", size = 205877, upload-time = "2025-10-08T19:47:23.059Z" }, + { url = "https://files.pythonhosted.org/packages/e2/39/8163fc6f3133fea7b5f2827e8eba2029a0277ab2c5beee6c1db7b10fc23d/propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66", size = 199437, upload-time = "2025-10-08T19:47:24.445Z" }, + { url = "https://files.pythonhosted.org/packages/93/89/caa9089970ca49c7c01662bd0eeedfe85494e863e8043565aeb6472ce8fe/propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81", size = 37586, upload-time = "2025-10-08T19:47:25.736Z" }, + { url = "https://files.pythonhosted.org/packages/f5/ab/f76ec3c3627c883215b5c8080debb4394ef5a7a29be811f786415fc1e6fd/propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e", size = 40790, upload-time = "2025-10-08T19:47:26.847Z" }, + { url = "https://files.pythonhosted.org/packages/59/1b/e71ae98235f8e2ba5004d8cb19765a74877abf189bc53fc0c80d799e56c3/propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1", size = 37158, upload-time = "2025-10-08T19:47:27.961Z" }, + { url = "https://files.pythonhosted.org/packages/83/ce/a31bbdfc24ee0dcbba458c8175ed26089cf109a55bbe7b7640ed2470cfe9/propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b", size = 81451, upload-time = "2025-10-08T19:47:29.445Z" }, + { url = "https://files.pythonhosted.org/packages/25/9c/442a45a470a68456e710d96cacd3573ef26a1d0a60067e6a7d5e655621ed/propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566", size = 46374, upload-time = "2025-10-08T19:47:30.579Z" }, + { url = "https://files.pythonhosted.org/packages/f4/bf/b1d5e21dbc3b2e889ea4327044fb16312a736d97640fb8b6aa3f9c7b3b65/propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835", size = 48396, upload-time = "2025-10-08T19:47:31.79Z" }, + { url = "https://files.pythonhosted.org/packages/f4/04/5b4c54a103d480e978d3c8a76073502b18db0c4bc17ab91b3cb5092ad949/propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e", size = 275950, upload-time = "2025-10-08T19:47:33.481Z" }, + { url = "https://files.pythonhosted.org/packages/b4/c1/86f846827fb969c4b78b0af79bba1d1ea2156492e1b83dea8b8a6ae27395/propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859", size = 273856, upload-time = "2025-10-08T19:47:34.906Z" }, + { url = "https://files.pythonhosted.org/packages/36/1d/fc272a63c8d3bbad6878c336c7a7dea15e8f2d23a544bda43205dfa83ada/propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b", size = 280420, upload-time = "2025-10-08T19:47:36.338Z" }, + { url = "https://files.pythonhosted.org/packages/07/0c/01f2219d39f7e53d52e5173bcb09c976609ba30209912a0680adfb8c593a/propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0", size = 263254, upload-time = "2025-10-08T19:47:37.692Z" }, + { url = "https://files.pythonhosted.org/packages/2d/18/cd28081658ce597898f0c4d174d4d0f3c5b6d4dc27ffafeef835c95eb359/propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af", size = 261205, upload-time = "2025-10-08T19:47:39.659Z" }, + { url = "https://files.pythonhosted.org/packages/7a/71/1f9e22eb8b8316701c2a19fa1f388c8a3185082607da8e406a803c9b954e/propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393", size = 247873, upload-time = "2025-10-08T19:47:41.084Z" }, + { url = "https://files.pythonhosted.org/packages/4a/65/3d4b61f36af2b4eddba9def857959f1016a51066b4f1ce348e0cf7881f58/propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874", size = 262739, upload-time = "2025-10-08T19:47:42.51Z" }, + { url = "https://files.pythonhosted.org/packages/2a/42/26746ab087faa77c1c68079b228810436ccd9a5ce9ac85e2b7307195fd06/propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7", size = 263514, upload-time = "2025-10-08T19:47:43.927Z" }, + { url = "https://files.pythonhosted.org/packages/94/13/630690fe201f5502d2403dd3cfd451ed8858fe3c738ee88d095ad2ff407b/propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1", size = 257781, upload-time = "2025-10-08T19:47:45.448Z" }, + { url = "https://files.pythonhosted.org/packages/92/f7/1d4ec5841505f423469efbfc381d64b7b467438cd5a4bbcbb063f3b73d27/propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717", size = 41396, upload-time = "2025-10-08T19:47:47.202Z" }, + { url = "https://files.pythonhosted.org/packages/48/f0/615c30622316496d2cbbc29f5985f7777d3ada70f23370608c1d3e081c1f/propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37", size = 44897, upload-time = "2025-10-08T19:47:48.336Z" }, + { url = "https://files.pythonhosted.org/packages/fd/ca/6002e46eccbe0e33dcd4069ef32f7f1c9e243736e07adca37ae8c4830ec3/propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a", size = 39789, upload-time = "2025-10-08T19:47:49.876Z" }, + { url = "https://files.pythonhosted.org/packages/8e/5c/bca52d654a896f831b8256683457ceddd490ec18d9ec50e97dfd8fc726a8/propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12", size = 78152, upload-time = "2025-10-08T19:47:51.051Z" }, + { url = "https://files.pythonhosted.org/packages/65/9b/03b04e7d82a5f54fb16113d839f5ea1ede58a61e90edf515f6577c66fa8f/propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c", size = 44869, upload-time = "2025-10-08T19:47:52.594Z" }, + { url = "https://files.pythonhosted.org/packages/b2/fa/89a8ef0468d5833a23fff277b143d0573897cf75bd56670a6d28126c7d68/propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded", size = 46596, upload-time = "2025-10-08T19:47:54.073Z" }, + { url = "https://files.pythonhosted.org/packages/86/bd/47816020d337f4a746edc42fe8d53669965138f39ee117414c7d7a340cfe/propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641", size = 206981, upload-time = "2025-10-08T19:47:55.715Z" }, + { url = "https://files.pythonhosted.org/packages/df/f6/c5fa1357cc9748510ee55f37173eb31bfde6d94e98ccd9e6f033f2fc06e1/propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4", size = 211490, upload-time = "2025-10-08T19:47:57.499Z" }, + { url = "https://files.pythonhosted.org/packages/80/1e/e5889652a7c4a3846683401a48f0f2e5083ce0ec1a8a5221d8058fbd1adf/propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44", size = 215371, upload-time = "2025-10-08T19:47:59.317Z" }, + { url = "https://files.pythonhosted.org/packages/b2/f2/889ad4b2408f72fe1a4f6a19491177b30ea7bf1a0fd5f17050ca08cfc882/propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d", size = 201424, upload-time = "2025-10-08T19:48:00.67Z" }, + { url = "https://files.pythonhosted.org/packages/27/73/033d63069b57b0812c8bd19f311faebeceb6ba31b8f32b73432d12a0b826/propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b", size = 197566, upload-time = "2025-10-08T19:48:02.604Z" }, + { url = "https://files.pythonhosted.org/packages/dc/89/ce24f3dc182630b4e07aa6d15f0ff4b14ed4b9955fae95a0b54c58d66c05/propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e", size = 193130, upload-time = "2025-10-08T19:48:04.499Z" }, + { url = "https://files.pythonhosted.org/packages/a9/24/ef0d5fd1a811fb5c609278d0209c9f10c35f20581fcc16f818da959fc5b4/propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f", size = 202625, upload-time = "2025-10-08T19:48:06.213Z" }, + { url = "https://files.pythonhosted.org/packages/f5/02/98ec20ff5546f68d673df2f7a69e8c0d076b5abd05ca882dc7ee3a83653d/propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49", size = 204209, upload-time = "2025-10-08T19:48:08.432Z" }, + { url = "https://files.pythonhosted.org/packages/a0/87/492694f76759b15f0467a2a93ab68d32859672b646aa8a04ce4864e7932d/propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144", size = 197797, upload-time = "2025-10-08T19:48:09.968Z" }, + { url = "https://files.pythonhosted.org/packages/ee/36/66367de3575db1d2d3f3d177432bd14ee577a39d3f5d1b3d5df8afe3b6e2/propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f", size = 38140, upload-time = "2025-10-08T19:48:11.232Z" }, + { url = "https://files.pythonhosted.org/packages/0c/2a/a758b47de253636e1b8aef181c0b4f4f204bf0dd964914fb2af90a95b49b/propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153", size = 41257, upload-time = "2025-10-08T19:48:12.707Z" }, + { url = "https://files.pythonhosted.org/packages/34/5e/63bd5896c3fec12edcbd6f12508d4890d23c265df28c74b175e1ef9f4f3b/propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992", size = 38097, upload-time = "2025-10-08T19:48:13.923Z" }, + { url = "https://files.pythonhosted.org/packages/99/85/9ff785d787ccf9bbb3f3106f79884a130951436f58392000231b4c737c80/propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f", size = 81455, upload-time = "2025-10-08T19:48:15.16Z" }, + { url = "https://files.pythonhosted.org/packages/90/85/2431c10c8e7ddb1445c1f7c4b54d886e8ad20e3c6307e7218f05922cad67/propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393", size = 46372, upload-time = "2025-10-08T19:48:16.424Z" }, + { url = "https://files.pythonhosted.org/packages/01/20/b0972d902472da9bcb683fa595099911f4d2e86e5683bcc45de60dd05dc3/propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0", size = 48411, upload-time = "2025-10-08T19:48:17.577Z" }, + { url = "https://files.pythonhosted.org/packages/e2/e3/7dc89f4f21e8f99bad3d5ddb3a3389afcf9da4ac69e3deb2dcdc96e74169/propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a", size = 275712, upload-time = "2025-10-08T19:48:18.901Z" }, + { url = "https://files.pythonhosted.org/packages/20/67/89800c8352489b21a8047c773067644e3897f02ecbbd610f4d46b7f08612/propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be", size = 273557, upload-time = "2025-10-08T19:48:20.762Z" }, + { url = "https://files.pythonhosted.org/packages/e2/a1/b52b055c766a54ce6d9c16d9aca0cad8059acd9637cdf8aa0222f4a026ef/propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc", size = 280015, upload-time = "2025-10-08T19:48:22.592Z" }, + { url = "https://files.pythonhosted.org/packages/48/c8/33cee30bd890672c63743049f3c9e4be087e6780906bfc3ec58528be59c1/propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a", size = 262880, upload-time = "2025-10-08T19:48:23.947Z" }, + { url = "https://files.pythonhosted.org/packages/0c/b1/8f08a143b204b418285c88b83d00edbd61afbc2c6415ffafc8905da7038b/propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89", size = 260938, upload-time = "2025-10-08T19:48:25.656Z" }, + { url = "https://files.pythonhosted.org/packages/cf/12/96e4664c82ca2f31e1c8dff86afb867348979eb78d3cb8546a680287a1e9/propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726", size = 247641, upload-time = "2025-10-08T19:48:27.207Z" }, + { url = "https://files.pythonhosted.org/packages/18/ed/e7a9cfca28133386ba52278136d42209d3125db08d0a6395f0cba0c0285c/propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367", size = 262510, upload-time = "2025-10-08T19:48:28.65Z" }, + { url = "https://files.pythonhosted.org/packages/f5/76/16d8bf65e8845dd62b4e2b57444ab81f07f40caa5652b8969b87ddcf2ef6/propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36", size = 263161, upload-time = "2025-10-08T19:48:30.133Z" }, + { url = "https://files.pythonhosted.org/packages/e7/70/c99e9edb5d91d5ad8a49fa3c1e8285ba64f1476782fed10ab251ff413ba1/propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455", size = 257393, upload-time = "2025-10-08T19:48:31.567Z" }, + { url = "https://files.pythonhosted.org/packages/08/02/87b25304249a35c0915d236575bc3574a323f60b47939a2262b77632a3ee/propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", size = 42546, upload-time = "2025-10-08T19:48:32.872Z" }, + { url = "https://files.pythonhosted.org/packages/cb/ef/3c6ecf8b317aa982f309835e8f96987466123c6e596646d4e6a1dfcd080f/propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", size = 46259, upload-time = "2025-10-08T19:48:34.226Z" }, + { url = "https://files.pythonhosted.org/packages/c4/2d/346e946d4951f37eca1e4f55be0f0174c52cd70720f84029b02f296f4a38/propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", size = 40428, upload-time = "2025-10-08T19:48:35.441Z" }, + { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, +] + [[package]] name = "py-key-value-aio" version = "0.2.8" @@ -1254,18 +1701,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997, upload-time = "2024-11-28T03:43:27.893Z" }, ] -[[package]] -name = "pyngrok" -version = "7.4.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3f/64/5ab436dd78db3bcfdbae5965c48e21a6ee3fa6ec87859e44442e2fb361c3/pyngrok-7.4.1.tar.gz", hash = "sha256:ad8637738ced5bdb88c28b087fea39ca552860c2d30004ac01033c0f8eb4f36e", size = 44612, upload-time = "2025-10-23T14:32:50.524Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/5d/231c395723ee72bae6c272831642304067fda24a4d4f008e18b1088348f4/pyngrok-7.4.1-py3-none-any.whl", hash = "sha256:0325e34f26f7a5a9324df414eebbfaec5e5388f77e7439ea45a3358f645bc840", size = 25464, upload-time = "2025-10-23T14:32:49.147Z" }, -] - [[package]] name = "pyperclip" version = "1.11.0" @@ -1831,6 +2266,116 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ee/ea/c67e1dee1ba208ed22c06d1d547ae5e293374bfc43e0eb0ef5e262b68561/werkzeug-3.1.1-py3-none-any.whl", hash = "sha256:a71124d1ef06008baafa3d266c02f56e1836a5984afd6dd6c9230669d60d9fb5", size = 224371, upload-time = "2024-11-01T16:40:43.994Z" }, ] +[[package]] +name = "yarl" +version = "1.22.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169, upload-time = "2025-10-06T14:12:55.963Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/27/5ab13fc84c76a0250afd3d26d5936349a35be56ce5785447d6c423b26d92/yarl-1.22.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ab72135b1f2db3fed3997d7e7dc1b80573c67138023852b6efb336a5eae6511", size = 141607, upload-time = "2025-10-06T14:09:16.298Z" }, + { url = "https://files.pythonhosted.org/packages/6a/a1/d065d51d02dc02ce81501d476b9ed2229d9a990818332242a882d5d60340/yarl-1.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:669930400e375570189492dc8d8341301578e8493aec04aebc20d4717f899dd6", size = 94027, upload-time = "2025-10-06T14:09:17.786Z" }, + { url = "https://files.pythonhosted.org/packages/c1/da/8da9f6a53f67b5106ffe902c6fa0164e10398d4e150d85838b82f424072a/yarl-1.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:792a2af6d58177ef7c19cbf0097aba92ca1b9cb3ffdd9c7470e156c8f9b5e028", size = 94963, upload-time = "2025-10-06T14:09:19.662Z" }, + { url = "https://files.pythonhosted.org/packages/68/fe/2c1f674960c376e29cb0bec1249b117d11738db92a6ccc4a530b972648db/yarl-1.22.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ea66b1c11c9150f1372f69afb6b8116f2dd7286f38e14ea71a44eee9ec51b9d", size = 368406, upload-time = "2025-10-06T14:09:21.402Z" }, + { url = "https://files.pythonhosted.org/packages/95/26/812a540e1c3c6418fec60e9bbd38e871eaba9545e94fa5eff8f4a8e28e1e/yarl-1.22.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3e2daa88dc91870215961e96a039ec73e4937da13cf77ce17f9cad0c18df3503", size = 336581, upload-time = "2025-10-06T14:09:22.98Z" }, + { url = "https://files.pythonhosted.org/packages/0b/f5/5777b19e26fdf98563985e481f8be3d8a39f8734147a6ebf459d0dab5a6b/yarl-1.22.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba440ae430c00eee41509353628600212112cd5018d5def7e9b05ea7ac34eb65", size = 388924, upload-time = "2025-10-06T14:09:24.655Z" }, + { url = "https://files.pythonhosted.org/packages/86/08/24bd2477bd59c0bbd994fe1d93b126e0472e4e3df5a96a277b0a55309e89/yarl-1.22.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e6438cc8f23a9c1478633d216b16104a586b9761db62bfacb6425bac0a36679e", size = 392890, upload-time = "2025-10-06T14:09:26.617Z" }, + { url = "https://files.pythonhosted.org/packages/46/00/71b90ed48e895667ecfb1eaab27c1523ee2fa217433ed77a73b13205ca4b/yarl-1.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c52a6e78aef5cf47a98ef8e934755abf53953379b7d53e68b15ff4420e6683d", size = 365819, upload-time = "2025-10-06T14:09:28.544Z" }, + { url = "https://files.pythonhosted.org/packages/30/2d/f715501cae832651d3282387c6a9236cd26bd00d0ff1e404b3dc52447884/yarl-1.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3b06bcadaac49c70f4c88af4ffcfbe3dc155aab3163e75777818092478bcbbe7", size = 363601, upload-time = "2025-10-06T14:09:30.568Z" }, + { url = "https://files.pythonhosted.org/packages/f8/f9/a678c992d78e394e7126ee0b0e4e71bd2775e4334d00a9278c06a6cce96a/yarl-1.22.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6944b2dc72c4d7f7052683487e3677456050ff77fcf5e6204e98caf785ad1967", size = 358072, upload-time = "2025-10-06T14:09:32.528Z" }, + { url = "https://files.pythonhosted.org/packages/2c/d1/b49454411a60edb6fefdcad4f8e6dbba7d8019e3a508a1c5836cba6d0781/yarl-1.22.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5372ca1df0f91a86b047d1277c2aaf1edb32d78bbcefffc81b40ffd18f027ed", size = 385311, upload-time = "2025-10-06T14:09:34.634Z" }, + { url = "https://files.pythonhosted.org/packages/87/e5/40d7a94debb8448c7771a916d1861d6609dddf7958dc381117e7ba36d9e8/yarl-1.22.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:51af598701f5299012b8416486b40fceef8c26fc87dc6d7d1f6fc30609ea0aa6", size = 381094, upload-time = "2025-10-06T14:09:36.268Z" }, + { url = "https://files.pythonhosted.org/packages/35/d8/611cc282502381ad855448643e1ad0538957fc82ae83dfe7762c14069e14/yarl-1.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b266bd01fedeffeeac01a79ae181719ff848a5a13ce10075adbefc8f1daee70e", size = 370944, upload-time = "2025-10-06T14:09:37.872Z" }, + { url = "https://files.pythonhosted.org/packages/2d/df/fadd00fb1c90e1a5a8bd731fa3d3de2e165e5a3666a095b04e31b04d9cb6/yarl-1.22.0-cp311-cp311-win32.whl", hash = "sha256:a9b1ba5610a4e20f655258d5a1fdc7ebe3d837bb0e45b581398b99eb98b1f5ca", size = 81804, upload-time = "2025-10-06T14:09:39.359Z" }, + { url = "https://files.pythonhosted.org/packages/b5/f7/149bb6f45f267cb5c074ac40c01c6b3ea6d8a620d34b337f6321928a1b4d/yarl-1.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:078278b9b0b11568937d9509b589ee83ef98ed6d561dfe2020e24a9fd08eaa2b", size = 86858, upload-time = "2025-10-06T14:09:41.068Z" }, + { url = "https://files.pythonhosted.org/packages/2b/13/88b78b93ad3f2f0b78e13bfaaa24d11cbc746e93fe76d8c06bf139615646/yarl-1.22.0-cp311-cp311-win_arm64.whl", hash = "sha256:b6a6f620cfe13ccec221fa312139135166e47ae169f8253f72a0abc0dae94376", size = 81637, upload-time = "2025-10-06T14:09:42.712Z" }, + { url = "https://files.pythonhosted.org/packages/75/ff/46736024fee3429b80a165a732e38e5d5a238721e634ab41b040d49f8738/yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f", size = 142000, upload-time = "2025-10-06T14:09:44.631Z" }, + { url = "https://files.pythonhosted.org/packages/5a/9a/b312ed670df903145598914770eb12de1bac44599549b3360acc96878df8/yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2", size = 94338, upload-time = "2025-10-06T14:09:46.372Z" }, + { url = "https://files.pythonhosted.org/packages/ba/f5/0601483296f09c3c65e303d60c070a5c19fcdbc72daa061e96170785bc7d/yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74", size = 94909, upload-time = "2025-10-06T14:09:48.648Z" }, + { url = "https://files.pythonhosted.org/packages/60/41/9a1fe0b73dbcefce72e46cf149b0e0a67612d60bfc90fb59c2b2efdfbd86/yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df", size = 372940, upload-time = "2025-10-06T14:09:50.089Z" }, + { url = "https://files.pythonhosted.org/packages/17/7a/795cb6dfee561961c30b800f0ed616b923a2ec6258b5def2a00bf8231334/yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb", size = 345825, upload-time = "2025-10-06T14:09:52.142Z" }, + { url = "https://files.pythonhosted.org/packages/d7/93/a58f4d596d2be2ae7bab1a5846c4d270b894958845753b2c606d666744d3/yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2", size = 386705, upload-time = "2025-10-06T14:09:54.128Z" }, + { url = "https://files.pythonhosted.org/packages/61/92/682279d0e099d0e14d7fd2e176bd04f48de1484f56546a3e1313cd6c8e7c/yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82", size = 396518, upload-time = "2025-10-06T14:09:55.762Z" }, + { url = "https://files.pythonhosted.org/packages/db/0f/0d52c98b8a885aeda831224b78f3be7ec2e1aa4a62091f9f9188c3c65b56/yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a", size = 377267, upload-time = "2025-10-06T14:09:57.958Z" }, + { url = "https://files.pythonhosted.org/packages/22/42/d2685e35908cbeaa6532c1fc73e89e7f2efb5d8a7df3959ea8e37177c5a3/yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124", size = 365797, upload-time = "2025-10-06T14:09:59.527Z" }, + { url = "https://files.pythonhosted.org/packages/a2/83/cf8c7bcc6355631762f7d8bdab920ad09b82efa6b722999dfb05afa6cfac/yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa", size = 365535, upload-time = "2025-10-06T14:10:01.139Z" }, + { url = "https://files.pythonhosted.org/packages/25/e1/5302ff9b28f0c59cac913b91fe3f16c59a033887e57ce9ca5d41a3a94737/yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7", size = 382324, upload-time = "2025-10-06T14:10:02.756Z" }, + { url = "https://files.pythonhosted.org/packages/bf/cd/4617eb60f032f19ae3a688dc990d8f0d89ee0ea378b61cac81ede3e52fae/yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d", size = 383803, upload-time = "2025-10-06T14:10:04.552Z" }, + { url = "https://files.pythonhosted.org/packages/59/65/afc6e62bb506a319ea67b694551dab4a7e6fb7bf604e9bd9f3e11d575fec/yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520", size = 374220, upload-time = "2025-10-06T14:10:06.489Z" }, + { url = "https://files.pythonhosted.org/packages/e7/3d/68bf18d50dc674b942daec86a9ba922d3113d8399b0e52b9897530442da2/yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8", size = 81589, upload-time = "2025-10-06T14:10:09.254Z" }, + { url = "https://files.pythonhosted.org/packages/c8/9a/6ad1a9b37c2f72874f93e691b2e7ecb6137fb2b899983125db4204e47575/yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c", size = 87213, upload-time = "2025-10-06T14:10:11.369Z" }, + { url = "https://files.pythonhosted.org/packages/44/c5/c21b562d1680a77634d748e30c653c3ca918beb35555cff24986fff54598/yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74", size = 81330, upload-time = "2025-10-06T14:10:13.112Z" }, + { url = "https://files.pythonhosted.org/packages/ea/f3/d67de7260456ee105dc1d162d43a019ecad6b91e2f51809d6cddaa56690e/yarl-1.22.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8dee9c25c74997f6a750cd317b8ca63545169c098faee42c84aa5e506c819b53", size = 139980, upload-time = "2025-10-06T14:10:14.601Z" }, + { url = "https://files.pythonhosted.org/packages/01/88/04d98af0b47e0ef42597b9b28863b9060bb515524da0a65d5f4db160b2d5/yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a", size = 93424, upload-time = "2025-10-06T14:10:16.115Z" }, + { url = "https://files.pythonhosted.org/packages/18/91/3274b215fd8442a03975ce6bee5fe6aa57a8326b29b9d3d56234a1dca244/yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c", size = 93821, upload-time = "2025-10-06T14:10:17.993Z" }, + { url = "https://files.pythonhosted.org/packages/61/3a/caf4e25036db0f2da4ca22a353dfeb3c9d3c95d2761ebe9b14df8fc16eb0/yarl-1.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4f15793aa49793ec8d1c708ab7f9eded1aa72edc5174cae703651555ed1b601", size = 373243, upload-time = "2025-10-06T14:10:19.44Z" }, + { url = "https://files.pythonhosted.org/packages/6e/9e/51a77ac7516e8e7803b06e01f74e78649c24ee1021eca3d6a739cb6ea49c/yarl-1.22.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5542339dcf2747135c5c85f68680353d5cb9ffd741c0f2e8d832d054d41f35a", size = 342361, upload-time = "2025-10-06T14:10:21.124Z" }, + { url = "https://files.pythonhosted.org/packages/d4/f8/33b92454789dde8407f156c00303e9a891f1f51a0330b0fad7c909f87692/yarl-1.22.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c401e05ad47a75869c3ab3e35137f8468b846770587e70d71e11de797d113df", size = 387036, upload-time = "2025-10-06T14:10:22.902Z" }, + { url = "https://files.pythonhosted.org/packages/d9/9a/c5db84ea024f76838220280f732970aa4ee154015d7f5c1bfb60a267af6f/yarl-1.22.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:243dda95d901c733f5b59214d28b0120893d91777cb8aa043e6ef059d3cddfe2", size = 397671, upload-time = "2025-10-06T14:10:24.523Z" }, + { url = "https://files.pythonhosted.org/packages/11/c9/cd8538dc2e7727095e0c1d867bad1e40c98f37763e6d995c1939f5fdc7b1/yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b", size = 377059, upload-time = "2025-10-06T14:10:26.406Z" }, + { url = "https://files.pythonhosted.org/packages/a1/b9/ab437b261702ced75122ed78a876a6dec0a1b0f5e17a4ac7a9a2482d8abe/yarl-1.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0748275abb8c1e1e09301ee3cf90c8a99678a4e92e4373705f2a2570d581273", size = 365356, upload-time = "2025-10-06T14:10:28.461Z" }, + { url = "https://files.pythonhosted.org/packages/b2/9d/8e1ae6d1d008a9567877b08f0ce4077a29974c04c062dabdb923ed98e6fe/yarl-1.22.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:47fdb18187e2a4e18fda2c25c05d8251a9e4a521edaed757fef033e7d8498d9a", size = 361331, upload-time = "2025-10-06T14:10:30.541Z" }, + { url = "https://files.pythonhosted.org/packages/ca/5a/09b7be3905962f145b73beb468cdd53db8aa171cf18c80400a54c5b82846/yarl-1.22.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c7044802eec4524fde550afc28edda0dd5784c4c45f0be151a2d3ba017daca7d", size = 382590, upload-time = "2025-10-06T14:10:33.352Z" }, + { url = "https://files.pythonhosted.org/packages/aa/7f/59ec509abf90eda5048b0bc3e2d7b5099dffdb3e6b127019895ab9d5ef44/yarl-1.22.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:139718f35149ff544caba20fce6e8a2f71f1e39b92c700d8438a0b1d2a631a02", size = 385316, upload-time = "2025-10-06T14:10:35.034Z" }, + { url = "https://files.pythonhosted.org/packages/e5/84/891158426bc8036bfdfd862fabd0e0fa25df4176ec793e447f4b85cf1be4/yarl-1.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1b51bebd221006d3d2f95fbe124b22b247136647ae5dcc8c7acafba66e5ee67", size = 374431, upload-time = "2025-10-06T14:10:37.76Z" }, + { url = "https://files.pythonhosted.org/packages/bb/49/03da1580665baa8bef5e8ed34c6df2c2aca0a2f28bf397ed238cc1bbc6f2/yarl-1.22.0-cp313-cp313-win32.whl", hash = "sha256:d3e32536234a95f513bd374e93d717cf6b2231a791758de6c509e3653f234c95", size = 81555, upload-time = "2025-10-06T14:10:39.649Z" }, + { url = "https://files.pythonhosted.org/packages/9a/ee/450914ae11b419eadd067c6183ae08381cfdfcb9798b90b2b713bbebddda/yarl-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d", size = 86965, upload-time = "2025-10-06T14:10:41.313Z" }, + { url = "https://files.pythonhosted.org/packages/98/4d/264a01eae03b6cf629ad69bae94e3b0e5344741e929073678e84bf7a3e3b/yarl-1.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d0fcda9608875f7d052eff120c7a5da474a6796fe4d83e152e0e4d42f6d1a9b", size = 81205, upload-time = "2025-10-06T14:10:43.167Z" }, + { url = "https://files.pythonhosted.org/packages/88/fc/6908f062a2f77b5f9f6d69cecb1747260831ff206adcbc5b510aff88df91/yarl-1.22.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:719ae08b6972befcba4310e49edb1161a88cdd331e3a694b84466bd938a6ab10", size = 146209, upload-time = "2025-10-06T14:10:44.643Z" }, + { url = "https://files.pythonhosted.org/packages/65/47/76594ae8eab26210b4867be6f49129861ad33da1f1ebdf7051e98492bf62/yarl-1.22.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47d8a5c446df1c4db9d21b49619ffdba90e77c89ec6e283f453856c74b50b9e3", size = 95966, upload-time = "2025-10-06T14:10:46.554Z" }, + { url = "https://files.pythonhosted.org/packages/ab/ce/05e9828a49271ba6b5b038b15b3934e996980dd78abdfeb52a04cfb9467e/yarl-1.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cfebc0ac8333520d2d0423cbbe43ae43c8838862ddb898f5ca68565e395516e9", size = 97312, upload-time = "2025-10-06T14:10:48.007Z" }, + { url = "https://files.pythonhosted.org/packages/d1/c5/7dffad5e4f2265b29c9d7ec869c369e4223166e4f9206fc2243ee9eea727/yarl-1.22.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4398557cbf484207df000309235979c79c4356518fd5c99158c7d38203c4da4f", size = 361967, upload-time = "2025-10-06T14:10:49.997Z" }, + { url = "https://files.pythonhosted.org/packages/50/b2/375b933c93a54bff7fc041e1a6ad2c0f6f733ffb0c6e642ce56ee3b39970/yarl-1.22.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2ca6fd72a8cd803be290d42f2dec5cdcd5299eeb93c2d929bf060ad9efaf5de0", size = 323949, upload-time = "2025-10-06T14:10:52.004Z" }, + { url = "https://files.pythonhosted.org/packages/66/50/bfc2a29a1d78644c5a7220ce2f304f38248dc94124a326794e677634b6cf/yarl-1.22.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca1f59c4e1ab6e72f0a23c13fca5430f889634166be85dbf1013683e49e3278e", size = 361818, upload-time = "2025-10-06T14:10:54.078Z" }, + { url = "https://files.pythonhosted.org/packages/46/96/f3941a46af7d5d0f0498f86d71275696800ddcdd20426298e572b19b91ff/yarl-1.22.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5010a52015e7c70f86eb967db0f37f3c8bd503a695a49f8d45700144667708", size = 372626, upload-time = "2025-10-06T14:10:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/c1/42/8b27c83bb875cd89448e42cd627e0fb971fa1675c9ec546393d18826cb50/yarl-1.22.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d7672ecf7557476642c88497c2f8d8542f8e36596e928e9bcba0e42e1e7d71f", size = 341129, upload-time = "2025-10-06T14:10:57.985Z" }, + { url = "https://files.pythonhosted.org/packages/49/36/99ca3122201b382a3cf7cc937b95235b0ac944f7e9f2d5331d50821ed352/yarl-1.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b7c88eeef021579d600e50363e0b6ee4f7f6f728cd3486b9d0f3ee7b946398d", size = 346776, upload-time = "2025-10-06T14:10:59.633Z" }, + { url = "https://files.pythonhosted.org/packages/85/b4/47328bf996acd01a4c16ef9dcd2f59c969f495073616586f78cd5f2efb99/yarl-1.22.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f4afb5c34f2c6fecdcc182dfcfc6af6cccf1aa923eed4d6a12e9d96904e1a0d8", size = 334879, upload-time = "2025-10-06T14:11:01.454Z" }, + { url = "https://files.pythonhosted.org/packages/c2/ad/b77d7b3f14a4283bffb8e92c6026496f6de49751c2f97d4352242bba3990/yarl-1.22.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:59c189e3e99a59cf8d83cbb31d4db02d66cda5a1a4374e8a012b51255341abf5", size = 350996, upload-time = "2025-10-06T14:11:03.452Z" }, + { url = "https://files.pythonhosted.org/packages/81/c8/06e1d69295792ba54d556f06686cbd6a7ce39c22307100e3fb4a2c0b0a1d/yarl-1.22.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5a3bf7f62a289fa90f1990422dc8dff5a458469ea71d1624585ec3a4c8d6960f", size = 356047, upload-time = "2025-10-06T14:11:05.115Z" }, + { url = "https://files.pythonhosted.org/packages/4b/b8/4c0e9e9f597074b208d18cef227d83aac36184bfbc6eab204ea55783dbc5/yarl-1.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de6b9a04c606978fdfe72666fa216ffcf2d1a9f6a381058d4378f8d7b1e5de62", size = 342947, upload-time = "2025-10-06T14:11:08.137Z" }, + { url = "https://files.pythonhosted.org/packages/e0/e5/11f140a58bf4c6ad7aca69a892bff0ee638c31bea4206748fc0df4ebcb3a/yarl-1.22.0-cp313-cp313t-win32.whl", hash = "sha256:1834bb90991cc2999f10f97f5f01317f99b143284766d197e43cd5b45eb18d03", size = 86943, upload-time = "2025-10-06T14:11:10.284Z" }, + { url = "https://files.pythonhosted.org/packages/31/74/8b74bae38ed7fe6793d0c15a0c8207bbb819cf287788459e5ed230996cdd/yarl-1.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff86011bd159a9d2dfc89c34cfd8aff12875980e3bd6a39ff097887520e60249", size = 93715, upload-time = "2025-10-06T14:11:11.739Z" }, + { url = "https://files.pythonhosted.org/packages/69/66/991858aa4b5892d57aef7ee1ba6b4d01ec3b7eb3060795d34090a3ca3278/yarl-1.22.0-cp313-cp313t-win_arm64.whl", hash = "sha256:7861058d0582b847bc4e3a4a4c46828a410bca738673f35a29ba3ca5db0b473b", size = 83857, upload-time = "2025-10-06T14:11:13.586Z" }, + { url = "https://files.pythonhosted.org/packages/46/b3/e20ef504049f1a1c54a814b4b9bed96d1ac0e0610c3b4da178f87209db05/yarl-1.22.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:34b36c2c57124530884d89d50ed2c1478697ad7473efd59cfd479945c95650e4", size = 140520, upload-time = "2025-10-06T14:11:15.465Z" }, + { url = "https://files.pythonhosted.org/packages/e4/04/3532d990fdbab02e5ede063676b5c4260e7f3abea2151099c2aa745acc4c/yarl-1.22.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0dd9a702591ca2e543631c2a017e4a547e38a5c0f29eece37d9097e04a7ac683", size = 93504, upload-time = "2025-10-06T14:11:17.106Z" }, + { url = "https://files.pythonhosted.org/packages/11/63/ff458113c5c2dac9a9719ac68ee7c947cb621432bcf28c9972b1c0e83938/yarl-1.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:594fcab1032e2d2cc3321bb2e51271e7cd2b516c7d9aee780ece81b07ff8244b", size = 94282, upload-time = "2025-10-06T14:11:19.064Z" }, + { url = "https://files.pythonhosted.org/packages/a7/bc/315a56aca762d44a6aaaf7ad253f04d996cb6b27bad34410f82d76ea8038/yarl-1.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d7a87a78d46a2e3d5b72587ac14b4c16952dd0887dbb051451eceac774411e", size = 372080, upload-time = "2025-10-06T14:11:20.996Z" }, + { url = "https://files.pythonhosted.org/packages/3f/3f/08e9b826ec2e099ea6e7c69a61272f4f6da62cb5b1b63590bb80ca2e4a40/yarl-1.22.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:852863707010316c973162e703bddabec35e8757e67fcb8ad58829de1ebc8590", size = 338696, upload-time = "2025-10-06T14:11:22.847Z" }, + { url = "https://files.pythonhosted.org/packages/e3/9f/90360108e3b32bd76789088e99538febfea24a102380ae73827f62073543/yarl-1.22.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:131a085a53bfe839a477c0845acf21efc77457ba2bcf5899618136d64f3303a2", size = 387121, upload-time = "2025-10-06T14:11:24.889Z" }, + { url = "https://files.pythonhosted.org/packages/98/92/ab8d4657bd5b46a38094cfaea498f18bb70ce6b63508fd7e909bd1f93066/yarl-1.22.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:078a8aefd263f4d4f923a9677b942b445a2be970ca24548a8102689a3a8ab8da", size = 394080, upload-time = "2025-10-06T14:11:27.307Z" }, + { url = "https://files.pythonhosted.org/packages/f5/e7/d8c5a7752fef68205296201f8ec2bf718f5c805a7a7e9880576c67600658/yarl-1.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca03b91c323036913993ff5c738d0842fc9c60c4648e5c8d98331526df89784", size = 372661, upload-time = "2025-10-06T14:11:29.387Z" }, + { url = "https://files.pythonhosted.org/packages/b6/2e/f4d26183c8db0bb82d491b072f3127fb8c381a6206a3a56332714b79b751/yarl-1.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68986a61557d37bb90d3051a45b91fa3d5c516d177dfc6dd6f2f436a07ff2b6b", size = 364645, upload-time = "2025-10-06T14:11:31.423Z" }, + { url = "https://files.pythonhosted.org/packages/80/7c/428e5812e6b87cd00ee8e898328a62c95825bf37c7fa87f0b6bb2ad31304/yarl-1.22.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4792b262d585ff0dff6bcb787f8492e40698443ec982a3568c2096433660c694", size = 355361, upload-time = "2025-10-06T14:11:33.055Z" }, + { url = "https://files.pythonhosted.org/packages/ec/2a/249405fd26776f8b13c067378ef4d7dd49c9098d1b6457cdd152a99e96a9/yarl-1.22.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ebd4549b108d732dba1d4ace67614b9545b21ece30937a63a65dd34efa19732d", size = 381451, upload-time = "2025-10-06T14:11:35.136Z" }, + { url = "https://files.pythonhosted.org/packages/67/a8/fb6b1adbe98cf1e2dd9fad71003d3a63a1bc22459c6e15f5714eb9323b93/yarl-1.22.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f87ac53513d22240c7d59203f25cc3beac1e574c6cd681bbfd321987b69f95fd", size = 383814, upload-time = "2025-10-06T14:11:37.094Z" }, + { url = "https://files.pythonhosted.org/packages/d9/f9/3aa2c0e480fb73e872ae2814c43bc1e734740bb0d54e8cb2a95925f98131/yarl-1.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:22b029f2881599e2f1b06f8f1db2ee63bd309e2293ba2d566e008ba12778b8da", size = 370799, upload-time = "2025-10-06T14:11:38.83Z" }, + { url = "https://files.pythonhosted.org/packages/50/3c/af9dba3b8b5eeb302f36f16f92791f3ea62e3f47763406abf6d5a4a3333b/yarl-1.22.0-cp314-cp314-win32.whl", hash = "sha256:6a635ea45ba4ea8238463b4f7d0e721bad669f80878b7bfd1f89266e2ae63da2", size = 82990, upload-time = "2025-10-06T14:11:40.624Z" }, + { url = "https://files.pythonhosted.org/packages/ac/30/ac3a0c5bdc1d6efd1b41fa24d4897a4329b3b1e98de9449679dd327af4f0/yarl-1.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d6e6885777af0f110b0e5d7e5dda8b704efed3894da26220b7f3d887b839a79", size = 88292, upload-time = "2025-10-06T14:11:42.578Z" }, + { url = "https://files.pythonhosted.org/packages/df/0a/227ab4ff5b998a1b7410abc7b46c9b7a26b0ca9e86c34ba4b8d8bc7c63d5/yarl-1.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:8218f4e98d3c10d683584cb40f0424f4b9fd6e95610232dd75e13743b070ee33", size = 82888, upload-time = "2025-10-06T14:11:44.863Z" }, + { url = "https://files.pythonhosted.org/packages/06/5e/a15eb13db90abd87dfbefb9760c0f3f257ac42a5cac7e75dbc23bed97a9f/yarl-1.22.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45c2842ff0e0d1b35a6bf1cd6c690939dacb617a70827f715232b2e0494d55d1", size = 146223, upload-time = "2025-10-06T14:11:46.796Z" }, + { url = "https://files.pythonhosted.org/packages/18/82/9665c61910d4d84f41a5bf6837597c89e665fa88aa4941080704645932a9/yarl-1.22.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d947071e6ebcf2e2bee8fce76e10faca8f7a14808ca36a910263acaacef08eca", size = 95981, upload-time = "2025-10-06T14:11:48.845Z" }, + { url = "https://files.pythonhosted.org/packages/5d/9a/2f65743589809af4d0a6d3aa749343c4b5f4c380cc24a8e94a3c6625a808/yarl-1.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:334b8721303e61b00019474cc103bdac3d7b1f65e91f0bfedeec2d56dfe74b53", size = 97303, upload-time = "2025-10-06T14:11:50.897Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ab/5b13d3e157505c43c3b43b5a776cbf7b24a02bc4cccc40314771197e3508/yarl-1.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7ce67c34138a058fd092f67d07a72b8e31ff0c9236e751957465a24b28910c", size = 361820, upload-time = "2025-10-06T14:11:52.549Z" }, + { url = "https://files.pythonhosted.org/packages/fb/76/242a5ef4677615cf95330cfc1b4610e78184400699bdda0acb897ef5e49a/yarl-1.22.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d77e1b2c6d04711478cb1c4ab90db07f1609ccf06a287d5607fcd90dc9863acf", size = 323203, upload-time = "2025-10-06T14:11:54.225Z" }, + { url = "https://files.pythonhosted.org/packages/8c/96/475509110d3f0153b43d06164cf4195c64d16999e0c7e2d8a099adcd6907/yarl-1.22.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4647674b6150d2cae088fc07de2738a84b8bcedebef29802cf0b0a82ab6face", size = 363173, upload-time = "2025-10-06T14:11:56.069Z" }, + { url = "https://files.pythonhosted.org/packages/c9/66/59db471aecfbd559a1fd48aedd954435558cd98c7d0da8b03cc6c140a32c/yarl-1.22.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efb07073be061c8f79d03d04139a80ba33cbd390ca8f0297aae9cce6411e4c6b", size = 373562, upload-time = "2025-10-06T14:11:58.783Z" }, + { url = "https://files.pythonhosted.org/packages/03/1f/c5d94abc91557384719da10ff166b916107c1b45e4d0423a88457071dd88/yarl-1.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51ac5435758ba97ad69617e13233da53908beccc6cfcd6c34bbed8dcbede486", size = 339828, upload-time = "2025-10-06T14:12:00.686Z" }, + { url = "https://files.pythonhosted.org/packages/5f/97/aa6a143d3afba17b6465733681c70cf175af89f76ec8d9286e08437a7454/yarl-1.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33e32a0dd0c8205efa8e83d04fc9f19313772b78522d1bdc7d9aed706bfd6138", size = 347551, upload-time = "2025-10-06T14:12:02.628Z" }, + { url = "https://files.pythonhosted.org/packages/43/3c/45a2b6d80195959239a7b2a8810506d4eea5487dce61c2a3393e7fc3c52e/yarl-1.22.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:bf4a21e58b9cde0e401e683ebd00f6ed30a06d14e93f7c8fd059f8b6e8f87b6a", size = 334512, upload-time = "2025-10-06T14:12:04.871Z" }, + { url = "https://files.pythonhosted.org/packages/86/a0/c2ab48d74599c7c84cb104ebd799c5813de252bea0f360ffc29d270c2caa/yarl-1.22.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e4b582bab49ac33c8deb97e058cd67c2c50dac0dd134874106d9c774fd272529", size = 352400, upload-time = "2025-10-06T14:12:06.624Z" }, + { url = "https://files.pythonhosted.org/packages/32/75/f8919b2eafc929567d3d8411f72bdb1a2109c01caaab4ebfa5f8ffadc15b/yarl-1.22.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0b5bcc1a9c4839e7e30b7b30dd47fe5e7e44fb7054ec29b5bb8d526aa1041093", size = 357140, upload-time = "2025-10-06T14:12:08.362Z" }, + { url = "https://files.pythonhosted.org/packages/cf/72/6a85bba382f22cf78add705d8c3731748397d986e197e53ecc7835e76de7/yarl-1.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c0232bce2170103ec23c454e54a57008a9a72b5d1c3105dc2496750da8cfa47c", size = 341473, upload-time = "2025-10-06T14:12:10.994Z" }, + { url = "https://files.pythonhosted.org/packages/35/18/55e6011f7c044dc80b98893060773cefcfdbf60dfefb8cb2f58b9bacbd83/yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e", size = 89056, upload-time = "2025-10-06T14:12:13.317Z" }, + { url = "https://files.pythonhosted.org/packages/f9/86/0f0dccb6e59a9e7f122c5afd43568b1d31b8ab7dda5f1b01fb5c7025c9a9/yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27", size = 96292, upload-time = "2025-10-06T14:12:15.398Z" }, + { url = "https://files.pythonhosted.org/packages/48/b7/503c98092fb3b344a179579f55814b613c1fbb1c23b3ec14a7b008a66a6e/yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1", size = 85171, upload-time = "2025-10-06T14:12:16.935Z" }, + { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" }, +] + [[package]] name = "zipp" version = "3.23.0" From cbbebe2a181c2c99c26fca47fba2fe6230868eae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EC=A7=80=ED=98=81?= Date: Tue, 28 Oct 2025 17:59:16 +0900 Subject: [PATCH 9/9] Revert "deploy command" : will be integrated on next release This reverts commit 9abf505da2f0e1c4a925792ae257bf11dbaa4d88. Update main.py --- fastapps/cli/commands/deploy.py | 377 -------------------------------- fastapps/cli/main.py | 32 --- fastapps/deployer/__init__.py | 15 -- fastapps/deployer/auth.py | 216 ------------------ fastapps/deployer/client.py | 131 ----------- fastapps/deployer/packager.py | 264 ---------------------- 6 files changed, 1035 deletions(-) delete mode 100644 fastapps/cli/commands/deploy.py delete mode 100644 fastapps/deployer/__init__.py delete mode 100644 fastapps/deployer/auth.py delete mode 100644 fastapps/deployer/client.py delete mode 100644 fastapps/deployer/packager.py diff --git a/fastapps/cli/commands/deploy.py b/fastapps/cli/commands/deploy.py deleted file mode 100644 index f9d26bd..0000000 --- a/fastapps/cli/commands/deploy.py +++ /dev/null @@ -1,377 +0,0 @@ -"""Deploy command for publishing FastApps projects.""" - -import asyncio -import json -import os -import subprocess -from pathlib import Path - -from dotenv import load_dotenv -from rich.console import Console -from rich.panel import Panel -from rich.progress import Progress, SpinnerColumn, TextColumn -from rich.table import Table - -# Load environment variables from .env file -load_dotenv() - -console = Console() - -# Default deployment server URL (can be overridden by FASTAPPS_DEPLOY_URL env var) -DEFAULT_DEPLOY_URL = os.getenv("FASTAPPS_DEPLOY_URL", "https://deploy.fastapps.org") - - -def get_config_dir(): - """Get FastApps config directory.""" - config_dir = Path.home() / ".fastapps" - config_dir.mkdir(exist_ok=True) - return config_dir - - -def get_config_file(): - """Get config file path.""" - return get_config_dir() / "config.json" - - -def load_config(): - """Load configuration from file.""" - config_file = get_config_file() - if config_file.exists(): - try: - with open(config_file, "r") as f: - return json.load(f) - except Exception as e: - console.print(f"[yellow]Warning: Could not load config: {e}[/yellow]") - return {} - - -def save_config(config): - """Save configuration to file.""" - import os - - config_file = get_config_file() - try: - with open(config_file, "w") as f: - json.dump(config, f, indent=2) - - # Set restrictive permissions (owner read/write only) - os.chmod(config_file, 0o600) - return True - except Exception as e: - console.print(f"[red]Error: Could not save config: {e}[/red]") - return False - - -def get_deploy_url() -> str: - """ - Get deployment server URL from environment variable or default. - - Returns: - Deployment server URL - """ - return DEFAULT_DEPLOY_URL - - -def get_deploy_token() -> str: - """ - Get stored deployment token. - - Returns: - Access token or None - """ - config = load_config() - return config.get("deploy_token") - - -def save_deploy_token(token: str): - """ - Save deployment token to config. - - Args: - token: Access token to save - """ - config = load_config() - config["deploy_token"] = token - save_config(config) - - -async def async_deploy( - skip_build: bool = False, - skip_confirmation: bool = False, -): - """ - Async deployment workflow. - - Args: - skip_build: Skip widget build step - skip_confirmation: Skip confirmation prompt - - Returns: - True if successful, False otherwise - """ - deploy_url = get_deploy_url() - from ..deployer import ArtifactPackager, ClerkOAuthAuthenticator, DeployClient - - project_root = Path.cwd() - - # Step 1: Validate project structure - console.print("\n[cyan]Validating project structure...[/cyan]") - try: - packager = ArtifactPackager(project_root) - packager._validate_project() - console.print("[green]✓ Project structure valid[/green]") - except FileNotFoundError as e: - console.print(f"[red]✗ {e}[/red]") - return False - - # Step 2: Build widgets - if not skip_build: - console.print("\n[cyan]Building widgets...[/cyan]") - try: - result = subprocess.run( - ["npm", "run", "build"], - capture_output=True, - text=True, - check=True, - ) - console.print("[green]✓ Widgets built successfully[/green]") - except subprocess.CalledProcessError as e: - console.print(f"[red]✗ Build failed: {e.stderr}[/red]") - console.print( - "[yellow]Tip: Run 'npm install' if packages are not installed[/yellow]" - ) - return False - except FileNotFoundError: - console.print("[red]✗ npm not found[/red]") - return False - - # Step 3: Show deployment summary - console.print("\n[bold cyan]Deployment Summary[/bold cyan]") - - summary_table = Table(show_header=False, box=None) - summary_table.add_column("Key", style="cyan") - summary_table.add_column("Value", style="white") - - # Get project info - try: - package_json = json.loads((project_root / "package.json").read_text()) - project_name = package_json.get("name", "unknown") - except Exception: - project_name = "unknown" - - # Count widgets - assets_dir = project_root / "assets" - widget_count = len(list(assets_dir.glob("*.html"))) if assets_dir.exists() else 0 - - summary_table.add_row("Project", project_name) - summary_table.add_row("Widgets", str(widget_count)) - summary_table.add_row("Deploy URL", deploy_url) - - console.print(summary_table) - console.print() - - # Step 4: Confirmation - if not skip_confirmation: - confirm = console.input("[bold]Deploy to production? (yes/no):[/bold] ") - if confirm.lower() not in ["yes", "y"]: - console.print("[yellow]Deployment cancelled[/yellow]") - return False - - # Step 5: Authenticate - console.print("\n[cyan]Authenticating...[/cyan]") - token = get_deploy_token() - - if not token: - console.print( - "[yellow]No authentication token found. Starting OAuth flow...[/yellow]" - ) - console.print( - "[dim]Your browser will open for authentication. " - "Please authorize FastApps CLI.[/dim]\n" - ) - - try: - authenticator = ClerkOAuthAuthenticator(deploy_url) - token = await authenticator.authenticate() - save_deploy_token(token) - console.print("[green]✓ Authentication successful[/green]") - except ConnectionError as e: - console.print(f"\n[red]✗ Connection Error[/red]\n") - console.print(f"[yellow]Cannot connect to deployment server:[/yellow]") - return False - except TimeoutError as e: - console.print(f"\n[red]✗ Authentication Timeout[/red]\n") - console.print( - "[yellow]Authentication took too long (5 minutes limit)[/yellow]\n" - ) - console.print("[dim]Please try again.[/dim]") - return False - except RuntimeError as e: - error_msg = str(e) - if "OAuth error" in error_msg: - console.print(f"\n[red]✗ OAuth Error[/red]\n") - console.print(f"[yellow]{error_msg}[/yellow]\n") - console.print("[dim]Please contact your deployment server administrator.[/dim]") - elif "timed out" in error_msg.lower(): - console.print(f"\n[red]✗ Authentication Timeout[/red]\n") - console.print( - "[yellow]Authentication took too long. Please try again.[/yellow]" - ) - else: - console.print(f"\n[red]✗ Authentication Failed[/red]\n") - console.print(f"[yellow]{error_msg}[/yellow]") - return False - except Exception as e: - console.print(f"\n[red]✗ Unexpected Error[/red]\n") - console.print(f"[yellow]{type(e).__name__}: {e}[/yellow]\n") - console.print( - "[dim]If this persists, please report at: https://github.com/DooiLabs/FastApps/issues[/dim]" - ) - return False - else: - console.print("[green]✓ Using saved authentication token[/green]") - - # Step 6: Package artifacts - console.print("\n[cyan]Packaging deployment artifacts...[/cyan]") - try: - with Progress( - SpinnerColumn(), - TextColumn("[progress.description]{task.description}"), - console=console, - ) as progress: - progress.add_task("Creating tarball...", total=None) - tarball_path = packager.package() - - # Show tarball size - tarball_size_mb = tarball_path.stat().st_size / (1024 * 1024) - console.print( - f"[green]✓ Package created ({tarball_size_mb:.2f} MB)[/green]" - ) - except Exception as e: - console.print(f"[red]✗ Packaging failed: {e}[/red]") - return False - - # Step 7: Upload to server - console.print("\n[cyan]Uploading to deployment server...[/cyan]") - try: - async with DeployClient(deploy_url, token) as client: - with Progress( - SpinnerColumn(), - TextColumn("[progress.description]{task.description}"), - console=console, - ) as progress: - progress.add_task("Uploading artifacts...", total=None) - result = await client.deploy(tarball_path) - - # Clean up tarball - tarball_path.unlink(missing_ok=True) - - if result.success: - console.print("[green]✓ Deployment successful![/green]\n") - - # Display deployment URL - success_panel = Panel( - f"[bold green]Deployment URL:[/bold green]\n" - f"[link={result.deployment_url}]{result.deployment_url}[/link]\n\n" - f"[dim]Deployment ID: {result.deployment_id}[/dim]", - title="🚀 Deployment Complete", - border_style="green", - ) - console.print(success_panel) - return True - else: - # Format error message - error_msg = result.error or "Unknown error" - - if "Authentication" in error_msg or "401" in error_msg: - console.print(f"\n[red]✗ Authentication Failed[/red]\n") - console.print("[yellow]Your access token is invalid or expired.[/yellow]\n") - console.print("[dim]Clearing saved token...[/dim]") - - # Clear token - config = load_config() - if "deploy_token" in config: - del config["deploy_token"] - save_config(config) - - console.print("[green]Token cleared.[/green]") - console.print("\n[cyan]Please run 'fastapps deploy' again to re-authenticate.[/cyan]") - - elif "Network" in error_msg or "Connection" in error_msg: - console.print(f"\n[red]✗ Connection Error[/red]\n") - console.print(f"[yellow]Cannot upload to deployment server:[/yellow]") - console.print(f"[white]{deploy_url}[/white]\n") - console.print(f"[dim]Error: {error_msg}[/dim]\n") - console.print("[cyan]Possible solutions:[/cyan]") - console.print(" • Check your internet connection") - console.print(" • Verify the server URL is correct") - console.print(" • Try again in a few moments") - - elif "Invalid deployment package" in error_msg: - console.print(f"\n[red]✗ Invalid Deployment Package[/red]\n") - console.print(f"[yellow]{error_msg}[/yellow]\n") - console.print("[dim]Please ensure all required files are present and valid.[/dim]") - - elif "Missing required" in error_msg: - console.print(f"\n[red]✗ Missing Required Files[/red]\n") - console.print(f"[yellow]{error_msg}[/yellow]\n") - console.print("[dim]Check that your project structure is complete.[/dim]") - - else: - console.print(f"\n[red]✗ Deployment Failed[/red]\n") - console.print(f"[yellow]{error_msg}[/yellow]") - - return False - - except ConnectionError as e: - console.print(f"\n[red]✗ Connection Error[/red]\n") - console.print(f"[yellow]Cannot connect to deployment server:[/yellow]") - console.print(f"[white]{deploy_url}[/white]\n") - console.print(f"[dim]Error: {e}[/dim]\n") - console.print("[cyan]Please check:[/cyan]") - console.print(" • Your internet connection") - console.print(" • Server availability") - console.print(" • Server URL is correct") - return False - - except TimeoutError as e: - console.print(f"\n[red]✗ Upload Timeout[/red]\n") - console.print("[yellow]Upload took too long (5 minutes limit)[/yellow]\n") - console.print("[dim]The deployment package may be too large or connection too slow.[/dim]") - return False - - except Exception as e: - console.print(f"\n[red]✗ Upload Failed[/red]\n") - console.print(f"[yellow]{type(e).__name__}: {e}[/yellow]\n") - console.print( - "[dim]If this persists, please report at: https://github.com/DooiLabs/FastApps/issues[/dim]" - ) - return False - - -def deploy_command( - yes: bool = False, - no_build: bool = False, -): - """ - Deploy FastApps project to production. - - Args: - yes: Skip confirmation prompt - no_build: Skip widget build step - """ - # Check if in FastApps project - if not Path("server/main.py").exists(): - console.print("[red]Error: Not in a FastApps project directory[/red]") - console.print( - "[yellow]Run this command from your project root (where server/main.py exists)[/yellow]" - ) - return False - - # Run async deployment - try: - success = asyncio.run(async_deploy(no_build, yes)) - return success - except KeyboardInterrupt: - console.print("\n[yellow]Deployment cancelled by user[/yellow]") - return False diff --git a/fastapps/cli/main.py b/fastapps/cli/main.py index 7beec14..5cbb603 100644 --- a/fastapps/cli/main.py +++ b/fastapps/cli/main.py @@ -4,7 +4,6 @@ from rich.console import Console from .commands.create import create_widget -from .commands.deploy import deploy_command from .commands.dev import start_dev_server from .commands.init import init_project from .commands.use import use_integration @@ -124,37 +123,6 @@ def build(): console.print("\n[cyan]For now, use:[/cyan]") console.print(" npm run build") - -@cli.command() -@click.option("--yes", "-y", is_flag=True, help="Skip confirmation prompt") -@click.option("--no-build", is_flag=True, help="Skip widget build step") -def deploy(yes, no_build): - """Deploy your FastApps project to production. - - This command will: - 1. Validate project structure - 2. Build widgets (unless --no-build) - 3. Authenticate with OAuth (if needed) - 4. Package artifacts (server, widgets, configs) - 5. Upload to deployment server - 6. Display deployment URL - - Examples: - fastapps deploy - fastapps deploy --yes - fastapps deploy --no-build --yes - - Configuration: - Set FASTAPPS_DEPLOY_URL in .env file to configure deployment server. - Default: https://deploy.fastapps.org - - Authentication: - First time: Opens browser for OAuth authentication - Subsequent: Uses saved token from ~/.fastapps/config.json - """ - deploy_command(yes=yes, no_build=no_build) - - @cli.command() def auth_info(): """Show authentication setup information.""" diff --git a/fastapps/deployer/__init__.py b/fastapps/deployer/__init__.py deleted file mode 100644 index 38a4f50..0000000 --- a/fastapps/deployer/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -"""FastApps Deployment Module - -Handles OAuth authentication, artifact packaging, and deployment to remote servers. -""" - -from .auth import ClerkOAuthAuthenticator -from .client import DeployClient, DeploymentResult -from .packager import ArtifactPackager - -__all__ = [ - "ClerkOAuthAuthenticator", - "DeployClient", - "DeploymentResult", - "ArtifactPackager", -] diff --git a/fastapps/deployer/auth.py b/fastapps/deployer/auth.py deleted file mode 100644 index e2dc064..0000000 --- a/fastapps/deployer/auth.py +++ /dev/null @@ -1,216 +0,0 @@ -"""OAuth 2.1 PKCE authentication for FastApps deployment.""" - -import asyncio -import hashlib -import secrets -import webbrowser -from typing import Optional, Tuple -from urllib.parse import parse_qs, urlencode, urlparse -import html -import httpx -from aiohttp import web - - -class ClerkOAuthAuthenticator: - """Handles OAuth 2.1 PKCE flow for Clerk authentication.""" - - def __init__(self, deploy_url: str): - """ - Initialize OAuth authenticator. - - Args: - deploy_url: Base URL of deployment server (e.g., https://deploy.fastapps.org) - """ - self.deploy_url = deploy_url.rstrip("/") - self.auth_url = f"{self.deploy_url}/oauth/authorize" - self.token_url = f"{self.deploy_url}/oauth/token" - self.redirect_uri = "http://localhost:8765/callback" - self.client_id = "fastapps-cli" - - def _generate_pkce_pair(self) -> Tuple[str, str]: - """ - Generate PKCE code verifier and challenge. - - Returns: - Tuple of (code_verifier, code_challenge) - """ - import base64 - - # Generate random code verifier (43-128 characters) - code_verifier = secrets.token_urlsafe(64) - - # Generate SHA256 code challenge (base64url-encoded per RFC 7636) - code_challenge = ( - base64.urlsafe_b64encode( - hashlib.sha256(code_verifier.encode()).digest() - ) - .decode() - .rstrip("=") - ) - - return code_verifier, code_challenge - - async def authenticate(self) -> str: - """ - Perform OAuth PKCE flow to get access token. - - Returns: - Access token string - - Raises: - RuntimeError: If authentication fails - """ - # Generate PKCE pair - code_verifier, code_challenge = self._generate_pkce_pair() - - # Start local callback server - auth_code_future = asyncio.Future() - app = self._create_callback_app(auth_code_future) - runner = web.AppRunner(app) - await runner.setup() - site = web.TCPSite(runner, "localhost", 8765) - await site.start() - - try: - # Build authorization URL - auth_params = { - "client_id": self.client_id, - "response_type": "code", - "redirect_uri": self.redirect_uri, - "code_challenge": code_challenge, - "code_challenge_method": "S256", - "scope": "deploy", - } - auth_full_url = f"{self.auth_url}?{urlencode(auth_params)}" - - # Open browser for user authorization - webbrowser.open(auth_full_url) - - # Wait for callback with timeout - try: - auth_code = await asyncio.wait_for(auth_code_future, timeout=300) - except asyncio.TimeoutError: - raise RuntimeError("Authentication timed out (5 minutes)") - - # Exchange authorization code for access token - access_token = await self._exchange_code_for_token( - auth_code, code_verifier - ) - - return access_token - - finally: - await runner.cleanup() - - def _create_callback_app(self, auth_code_future: asyncio.Future) -> web.Application: - """ - Create aiohttp app for OAuth callback. - - Args: - auth_code_future: Future to resolve with auth code - - Returns: - aiohttp Application - """ - - async def callback_handler(request: web.Request) -> web.Response: - """Handle OAuth callback.""" - # Parse query parameters - query_params = request.rel_url.query - - # Check for error - if "error" in query_params: - error = query_params.get("error", "unknown_error") - error_desc = query_params.get("error_description", "No description") - if not auth_code_future.done(): - auth_code_future.set_exception( - RuntimeError(f"OAuth error: {error} - {error_desc}") - ) - return web.Response( - text=f"

Authentication Failed

{html.escape(error_desc)}

", - content_type="text/html", - ) - - # Extract authorization code - auth_code = query_params.get("code") - if not auth_code: - if not auth_code_future.done(): - auth_code_future.set_exception( - RuntimeError("No authorization code received") - ) - return web.Response( - text="

Error

No authorization code received

", - content_type="text/html", - ) - - # Resolve future with auth code (avoid race condition) - if not auth_code_future.done(): - auth_code_future.set_result(auth_code) - - # Return success page - return web.Response( - text=""" - - FastApps - Authentication Success - -

✓ Authentication Successful

-

You can close this window and return to the terminal.

- - - """, - content_type="text/html", - ) - - app = web.Application() - app.router.add_get("/callback", callback_handler) - return app - - async def _exchange_code_for_token( - self, auth_code: str, code_verifier: str - ) -> str: - """ - Exchange authorization code for access token. - - Args: - auth_code: Authorization code from OAuth callback - code_verifier: PKCE code verifier - - Returns: - Access token - - Raises: - RuntimeError: If token exchange fails - """ - async with httpx.AsyncClient() as client: - try: - response = await client.post( - self.token_url, - data={ - "grant_type": "authorization_code", - "client_id": self.client_id, - "code": auth_code, - "redirect_uri": self.redirect_uri, - "code_verifier": code_verifier, - }, - headers={"Content-Type": "application/x-www-form-urlencoded"}, - ) - - if response.status_code != 200: - raise RuntimeError( - f"Token exchange failed: {response.status_code} - {response.text}" - ) - - token_data = response.json() - access_token = token_data.get("access_token") - - if not access_token: - raise RuntimeError("No access token in response") - - return access_token - - except httpx.ConnectError as e: - raise ConnectionError(f"Cannot connect to server: {e}") - except httpx.TimeoutException as e: - raise TimeoutError(f"Request timed out: {e}") - except httpx.RequestError as e: - raise RuntimeError(f"Token exchange request failed: {e}") diff --git a/fastapps/deployer/client.py b/fastapps/deployer/client.py deleted file mode 100644 index b50b6c5..0000000 --- a/fastapps/deployer/client.py +++ /dev/null @@ -1,131 +0,0 @@ -"""HTTP client for FastApps deployment API.""" - -from dataclasses import dataclass -from pathlib import Path -from typing import Optional - -import httpx - - -@dataclass -class DeploymentResult: - """Result of a deployment operation.""" - - success: bool - deployment_url: Optional[str] = None - deployment_id: Optional[str] = None - message: Optional[str] = None - error: Optional[str] = None - - -class DeployClient: - """Client for interacting with FastApps deployment server.""" - - def __init__(self, base_url: str, access_token: str): - """ - Initialize deployment client. - - Args: - base_url: Base URL of deployment server - access_token: OAuth access token - """ - self.base_url = base_url.rstrip("/") - self.access_token = access_token - self._client = None - - @property - def client(self) -> httpx.AsyncClient: - """Lazy-initialized HTTP client.""" - if self._client is None: - self._client = httpx.AsyncClient(timeout=300.0) # 5 minute timeout - return self._client - - async def deploy(self, tarball_path: Path) -> DeploymentResult: - """ - Deploy artifact to server. - - Args: - tarball_path: Path to deployment tarball - - Returns: - DeploymentResult with deployment information - - Raises: - RuntimeError: If deployment fails - """ - try: - # Read tarball content to avoid file handle issues - with open(tarball_path, "rb") as f: - file_content = f.read() - - files = {"artifact": ("deployment.tar.gz", file_content, "application/gzip")} - - # Send deployment request - response = await self.client.post( - f"{self.base_url}/deploy", - files=files, - headers={"Authorization": f"Bearer {self.access_token}"}, - ) - - # Handle response - if response.status_code == 200: - data = response.json() - return DeploymentResult( - success=True, - deployment_url=data.get("url"), - deployment_id=data.get("deployment_id"), - message=data.get("message", "Deployment successful"), - ) - elif response.status_code == 401: - return DeploymentResult( - success=False, - error="Authentication failed. Please run 'fastapps deploy' again to re-authenticate.", - ) - elif response.status_code == 400: - # Fix: Check if content-type contains json (handles charset) - content_type = response.headers.get("content-type", "") - data = response.json() if "application/json" in content_type else {} - error_msg = data.get("error", response.text) - return DeploymentResult( - success=False, - error=f"Invalid deployment package: {error_msg}", - ) - else: - return DeploymentResult( - success=False, - error=f"Deployment failed: {response.status_code} - {response.text}", - ) - - except httpx.ConnectError as e: - return DeploymentResult( - success=False, - error=f"Connection error: Cannot reach deployment server", - ) - except httpx.TimeoutException as e: - return DeploymentResult( - success=False, - error=f"Network timeout: Request took too long", - ) - except httpx.RequestError as e: - return DeploymentResult( - success=False, - error=f"Network error during deployment: {e}", - ) - except Exception as e: - return DeploymentResult( - success=False, - error=f"Unexpected error: {e}", - ) - - async def close(self): - """Close HTTP client.""" - if self._client is not None: - await self._client.aclose() - - async def __aenter__(self): - """Context manager entry.""" - return self - - async def __aexit__(self, exc_type, exc_val, exc_tb): - """Context manager exit.""" - await self.close() diff --git a/fastapps/deployer/packager.py b/fastapps/deployer/packager.py deleted file mode 100644 index 6100344..0000000 --- a/fastapps/deployer/packager.py +++ /dev/null @@ -1,264 +0,0 @@ -"""Artifact packaging for FastApps deployment.""" - -import json -import tarfile -import tempfile -from datetime import datetime -from pathlib import Path -from typing import Dict, List - -# Get version from package metadata -try: - from importlib.metadata import version - - __version__ = version("fastapps") -except Exception: - __version__ = "1.1.2" # Fallback version - - -class ArtifactPackager: - """Packages FastApps project for deployment.""" - - def __init__(self, project_root: Path): - """ - Initialize artifact packager. - - Args: - project_root: Root directory of FastApps project - """ - self.project_root = project_root - - def package(self) -> Path: - """ - Create deployment package as tar.gz. - - Returns: - Path to created tarball - - Raises: - FileNotFoundError: If required files/directories are missing - RuntimeError: If packaging fails - """ - import shutil - - # Validate project structure - self._validate_project() - - # Use TemporaryDirectory context manager for automatic cleanup - with tempfile.TemporaryDirectory(prefix="fastapps-deploy-") as temp_dir_str: - temp_dir = Path(temp_dir_str) - - try: - # Create manifest - manifest = self._create_manifest() - manifest_path = temp_dir / ".fastapps-manifest.json" - manifest_path.write_text(json.dumps(manifest, indent=2)) - - # Create tarball in temp directory - tarball_temp_path = temp_dir / "deployment.tar.gz" - - with tarfile.open(tarball_temp_path, "w:gz") as tar: - # Add manifest - tar.add( - manifest_path, - arcname=".fastapps-manifest.json", - ) - - # Add required directories and files - self._add_directory(tar, "assets") - self._add_directory(tar, "server") - - # Add configuration files - self._add_file(tar, "package.json") - self._add_file(tar, "requirements.txt") - - # Add optional files if they exist - for optional_file in ["README.md", ".env.example"]: - optional_path = self.project_root / optional_file - if optional_path.exists(): - tar.add(optional_path, arcname=optional_file) - - # Move tarball to project root with timestamp - from datetime import datetime - - timestamp = datetime.utcnow().strftime("%Y%m%d-%H%M%S") - final_path = self.project_root / f".fastapps-deploy-{timestamp}.tar.gz" - shutil.move(str(tarball_temp_path), str(final_path)) - - return final_path - - except Exception as e: - # TemporaryDirectory cleanup is automatic via context manager - raise RuntimeError(f"Failed to create deployment package: {e}") - - def _validate_project(self): - """ - Validate that project has required structure. - - Raises: - FileNotFoundError: If required files are missing - """ - required_dirs = ["assets", "server"] - required_files = ["package.json", "requirements.txt", "server/main.py"] - - for dir_name in required_dirs: - dir_path = self.project_root / dir_name - if not dir_path.exists(): - raise FileNotFoundError( - f"Required directory '{dir_name}' not found. " - f"Make sure you're in a FastApps project root." - ) - - for file_name in required_files: - file_path = self.project_root / file_name - if not file_path.exists(): - raise FileNotFoundError( - f"Required file '{file_name}' not found. " - f"Make sure you're in a FastApps project root." - ) - - def _create_manifest(self) -> Dict: - """ - Create deployment manifest with project metadata. - - Returns: - Manifest dictionary - """ - return { - "fastapps_version": __version__, - "timestamp": datetime.utcnow().isoformat() + "Z", - "project_name": self._get_project_name(), - "widgets": self._list_widgets(), - "dependencies": { - "python": self._parse_python_dependencies(), - "node": self._parse_node_dependencies(), - }, - } - - def _get_project_name(self) -> str: - """ - Extract project name from package.json. - - Returns: - Project name or 'unknown' - """ - try: - package_json_path = self.project_root / "package.json" - package_data = json.loads(package_json_path.read_text()) - return package_data.get("name", "unknown") - except Exception: - return "unknown" - - def _list_widgets(self) -> List[str]: - """ - List all built widget identifiers. - - Returns: - List of widget identifiers - """ - widgets = [] - assets_dir = self.project_root / "assets" - - if assets_dir.exists(): - for html_file in assets_dir.glob("*.html"): - # Extract widget identifier from filename - # Format: widgetid-hash.html - filename = html_file.stem - if "-" in filename: - widget_id = filename.rsplit("-", 1)[0] - widgets.append(widget_id) - - return sorted(set(widgets)) - - def _parse_python_dependencies(self) -> List[str]: - """ - Parse Python dependencies from requirements.txt. - - Returns: - List of package specifications - """ - try: - requirements_path = self.project_root / "requirements.txt" - requirements_text = requirements_path.read_text() - - dependencies = [] - for line in requirements_text.splitlines(): - line = line.strip() - # Skip comments and empty lines - if line and not line.startswith("#"): - dependencies.append(line) - - return dependencies - except Exception: - return [] - - def _parse_node_dependencies(self) -> Dict[str, str]: - """ - Parse Node.js dependencies from package.json. - - Returns: - Dictionary of package name to version - """ - try: - package_json_path = self.project_root / "package.json" - package_data = json.loads(package_json_path.read_text()) - return package_data.get("dependencies", {}) - except Exception: - return {} - - def _add_directory(self, tar: tarfile.TarFile, dir_name: str): - """ - Add directory to tarball with exclusions. - - Args: - tar: TarFile object - dir_name: Directory name relative to project root - """ - dir_path = self.project_root / dir_name - - if not dir_path.exists(): - return - - # Exclusion patterns - exclude_patterns = [ - "__pycache__", - "*.pyc", - ".DS_Store", - "*.swp", - ".venv", - "venv", - "env", - "node_modules", - ".git", - ] - - def should_exclude(path: Path) -> bool: - """Check if path matches exclusion patterns.""" - path_str = str(path) - for pattern in exclude_patterns: - if pattern in path_str: - return True - return False - - # Add directory recursively with exclusions - for item in dir_path.rglob("*"): - if should_exclude(item): - continue - - # Calculate relative path for archive - rel_path = item.relative_to(self.project_root) - - tar.add(item, arcname=str(rel_path)) - - def _add_file(self, tar: tarfile.TarFile, file_name: str): - """ - Add single file to tarball. - - Args: - tar: TarFile object - file_name: File name relative to project root - """ - file_path = self.project_root / file_name - - if file_path.exists(): - tar.add(file_path, arcname=file_name)