From 8996476693e186ff802495ec7ad3575b2945d7c2 Mon Sep 17 00:00:00 2001 From: xmanrui <841206367@qq.com> Date: Sat, 15 Aug 2026 15:12:53 +0800 Subject: [PATCH] Complete unified IM bot integration --- README.md | 8 +- THIRD_PARTY_NOTICES.md | 2 +- docs/images/imbot.png | Bin 42848 -> 133162 bytes lib/client.js | 703 +++++++++++---------- package-lock.json | 6 +- package.json | 2 +- plugin-src/client/channels/feishu/index.js | 106 ++-- plugin-src/client/channels/weixin/index.js | 74 ++- plugin-src/client/lifecycle.js | 86 +++ test/client-lifecycle.test.mjs | 185 ++++++ test/client-ui.test.mjs | 20 + 11 files changed, 803 insertions(+), 389 deletions(-) create mode 100644 plugin-src/client/lifecycle.js create mode 100644 test/client-lifecycle.test.mjs diff --git a/README.md b/README.md index f52cb2a..b418a37 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ - 飞书:扫码创建并绑定机器人,使用长连接收发消息; - 微信:扫码绑定微信机器人,使用腾讯 iLink 长轮询收发消息; -- 钉钉:扫码创建并授权机器人,使用钉钉 Stream 长连接收发消息。 +- 钉钉:扫码创建并授权机器人,使用钉钉 Stream 长连接收消息,并通过 AI Card 流式显示 Harness 回答。 其他 IM 平台可继续按同一渠道适配器结构接入。 @@ -26,7 +26,7 @@ npx -y github:xmanrui/dsh-im install 重启 `dsh web`,然后打开「设置 → 插件 → IM机器人」。安装器会用 `dsh-im` 替换 profile 中直接安装的 `dsh-feishu`、`dsh-weixin` 和 `dsh-dingtalk`,但不删除任何渠道数据;原有渠道凭据和扫码绑定会继续使用。 -钉钉接入时,请使用已加入企业/组织且有权创建机器人的钉钉账号扫描页面二维码,再在钉钉授权页点击「一键创建新机器人」。若提示“该账号还未加入组织”,请先创建组织或换用已加入组织的账号后重新扫码。钉钉当前可能在该官方授权页显示 OpenClaw 品牌;扫码后的机器人连接、凭据和消息均由 DeepSeek Harness 管理。由于扫码结果不包含扫码人的 staff ID,首次私聊后还需要在本机页面批准该使用者,未批准消息不会进入 Harness。 +钉钉接入时,请使用已加入企业/组织且有权创建机器人的钉钉账号扫描页面二维码,再在钉钉授权页点击「一键创建新机器人」。若提示“该账号还未加入组织”,请先创建组织或换用已加入组织的账号后重新扫码。钉钉当前可能在该官方授权页显示 OpenClaw 品牌;扫码后的机器人连接、凭据和消息均由 DeepSeek Harness 管理。插件不设置本机二次批准流程,钉钉中的机器人可见范围就是入站访问范围,请只开放给信任的组织、群或成员。 ## 设计 @@ -61,7 +61,7 @@ Connect IM bots to DeepSeek Harness by scanning a QR code. One plugin and one se - Feishu: create and bind a bot by scanning a QR code, then send and receive messages over a persistent connection. - WeChat: bind a WeChat bot by scanning a QR code, then send and receive messages through Tencent iLink long polling. -- DingTalk: create and authorize a bot by scanning a QR code, then send and receive messages through DingTalk Stream. +- DingTalk: create and authorize a bot by scanning a QR code, receive messages through DingTalk Stream, and stream Harness replies through AI Cards. Other IM platforms can be added through the same channel-adapter structure. @@ -73,7 +73,7 @@ npx -y github:xmanrui/dsh-im install Restart `dsh web`, then open **Settings → Plugins → IM Bot**. The installer replaces directly installed `dsh-feishu`, `dsh-weixin`, and `dsh-dingtalk` entries in the profile with `dsh-im` without deleting channel data. -For DingTalk, scan with an account that belongs to an enterprise or organization and can create bots, then choose **Create a new bot** on the authorization page. If DingTalk reports that the account has not joined an organization, create one or switch to an account that has, then scan again. That DingTalk-hosted page may currently display OpenClaw branding; the resulting connection, credentials, and messages are managed by DeepSeek Harness. Because the scan result does not identify the scanning user, send the bot a direct message and approve that sender locally before the message can enter Harness. +For DingTalk, scan with an account that belongs to an enterprise or organization and can create bots, then choose **Create a new bot** on the authorization page. If DingTalk reports that the account has not joined an organization, create one or switch to an account that has, then scan again. That DingTalk-hosted page may currently display OpenClaw branding; the resulting connection, credentials, and messages are managed by DeepSeek Harness. There is no second local sender-approval flow: the bot's DingTalk visibility is its inbound access scope, so restrict it to trusted organizations, groups, or members. ## Design diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index 98291c4..825cef9 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -1,6 +1,6 @@ # Third-party notices -This package composes [`@xmanrui/dsh-feishu`](https://github.com/xmanrui/dsh-feishu) at commit `aad650feabadd511241aa58b236d64273d5e397f`, [`@xmanrui/dsh-weixin`](https://github.com/xmanrui/dsh-weixin) at commit `76d076771b2c84fb4c5598c2344d486695eda080`, and [`@xmanrui/dsh-dingtalk`](https://github.com/xmanrui/dsh-dingtalk) at commit `e21177c3ba845f42d8f72f68b63cce67fe1a1d55`, all under the MIT License. +This package composes [`@xmanrui/dsh-feishu`](https://github.com/xmanrui/dsh-feishu) at commit `aad650feabadd511241aa58b236d64273d5e397f`, [`@xmanrui/dsh-weixin`](https://github.com/xmanrui/dsh-weixin) at commit `76d076771b2c84fb4c5598c2344d486695eda080`, and [`@xmanrui/dsh-dingtalk`](https://github.com/xmanrui/dsh-dingtalk) at commit `05f5a319b52ef7f0952baf5d603ebc144657262a`, all under the MIT License. The channel client components are adapted from those projects and preserve their security model: secrets remain in the DeepSeek Harness Host credential provider and never enter the browser-facing RPC response. diff --git a/docs/images/imbot.png b/docs/images/imbot.png index cf9bfac7d0dc4fcd238fc28c8998bf807960cce6..2a79daefc685def9ab9eef093ae3ecce2910a86b 100644 GIT binary patch literal 133162 zcmcG$c{tSX`#!9^Q8ctrF_wBOg^+D*SxTwwyX;EFHjG`CF-2%Yi|o7XS<2YAP-Nd3 zW0@rTp52(4=bpmn^ZovQe?8Cf_@m?SYUY03%e9=>d0w~Yn(DV9^lbE0R8)|=cW!D^ zQPIMvsAzhR(1IrpH^mF6s2)?@y?I^tk=gu6a4hpJQlm2|pdvBBY1YfTVRcRY_^q4Q zZ|Sy*G51;hUtM5;E>0NVjPVGt`2(sKpnm zLYE0;YA~xmKR-U2R`mOlwx{6VU!RA6eRcxAHr)kpjS_~>V?XhVI}VqA)SLRYAq4GX z5Gv}ZNIz^#ueL>7Ct7lPt!e4#>rcvq^ZomyFp3fD z`}+J;+oeR`lZYz`yu+EU%~=AiO>G)#{2T`2^6O%*%B!a*Kj_57!Y{=m5*$Yw(AW0A z{a86x#OejB$iqARPO0jt;~G~R#U0=3OOKW|9i80bxfIh-K|;+thqQ@yU{<*f4lcZ&7w-D(zky0Ow!2$f)al0;i<%JWOE3p`DAFax*4OuxaU&PHE-z8zDi`GJ z4|9h7FK8~DzH-lkmWdMqR(&aPcv#HA`>k!tinC8uf7#Q1^^$~qpP0-Hv+K|2 zZLP~=+`Fy^E3_N_`R%1z{OOEHI;c_T>=Qb!-yH}4dFPdMMzQ3GnCv#QYaRINaM|jv z$Nn20f6e@3K3~%>0sHeAMIP9|op5QK@%R=n8F^A^>;HJJ787TKEAr`=XWSp=wWNEf zgIip*Jh~dqzW?x?^DL%(PLOl8}f83$#9CrUX)vbX4*I!Wh=`d8>7XJQybdmi)%-gpo3m<%=FGLKAtd57tYwH?i zAv41=^D{d#mok}>Jr3Ua`cA9_5@z53@%)!0di`0IS)Ezq%Vz&T_oj4mJR{LhWNsi| zq9syDgY~spk==X4636&KjO{z(WK}z1dn%FGa!3WGh`Ncog;GM@MqM7x=xyvBaN~Tz zP)iBt;O8-0#uFQeNHASO(W}me5Gd;-rP@rPER8H=AR%Ul^Sa^6!F)7RF;`x1C^`kS z8u2|N9@p%njAc@z!}aia_D_X^JsU`Bu(7n3C@?WHQcikemm!x?J++g(bKz~s{`M&p z4Slk_-Rz=Z#Cat1?rgxARcG(*H>wBg%I@1(7z#+sz$6#?(Y{qcbvcOS$tmMic@7S# zPl@^)SaS(=V~#;SydHibbmzlP`cCE!-ihOw800u8OELP1g_R-<)e&0mJG9Ok`+Y=`|QhfX>$6p*?uFp55wX zn}@}{Z+@Xky|qW?u-F&G`Q==%ME#J&6DCCUq9~DSsNV7tCO*lVYq(| z3hT~=;+UWGn$S!kQTx|Ol-}(*cO2_kL){H`d>Rl^HM{t(dvbTxJW`(zyWBG+&*k?)HgDpy zuj5gH_*3aV`!+A=KFAxnPX^pP)1BL|rib}7*Rh(x+cw3FgMIpKoTaHe=|P^Kc6E5eeH zB5`d}ve5V2i*S!Xu0=JF!i0Gp)U5(S6q#hYX<738Dy(IFOwbnfje64Qu=rDQc|N6t zed|j)TXxY0<~Tep!J?~MAbh{>rt9eTnQwU+#wBWHW-a)Nm9pmkA!JRg%hEe_Iq`)! zRdEO08phJkivGA@)jWM+W5T-jjn|?^2@d&X6a6y&mOCY(MUuHz&1E|eN*CYcbI5pL zh1IHr@a^$)5>sk9^V8cQT=M57J>2TY=jr9t-RYpSJ$H>C2(aJDBl(CVaay?%MaM$wjwdWu-A--ep}YO;`fe zCFwh#os+Lt=C@es1@gYNwlX!5j??WP&e~vHK%X9DZl)B zkgP#Bgf4LyYByWL>B@L6VpODW^?fufT-YA;5Am+S3r%2i@~a&4j|!!JK+i?V%P8in z-13J+OSwjDkC^!kb!LjJ4X=@4w%#sIswm}-O?;#&D|^nOg(AwRgWK*{IgcJsRAy_D zcyd1tG#Y`g9ubqyySv;_Ihgel{ep!NCbzZBT_e@)iEop@`-+NMkNDuJYpal@E0dUw z|MKyl8|UwVkd4>1U7ZrTDhl5fy4AHCQ;c75O2vq=QR`wt2i;f$SWjvCKp_ ziYf`?*z7@%doL&7ZY9xiJ|O2__cG;*+<4y!)&>bY%z>Y04-cJ*0m=2eWI9+}!>#%$ zKIZ52FfrE~VbM8k>?7Yt4G0cf4t{~wmQTwHF73u_%nWsK>f9OE^(;6)JET{IR=uTr zAu02_xcGv>2?S#B^EZ>-Fcp6ZCG-`T1MXKMoH!LT7qGfUmuFwHxDk-rt*w85=Zme8 z(eTc|MKa@Ju0=hvLqA$>crW;L+l0|Wc6JCV%yrRIagicN<_2nmcfZwKxTQu1jd?y+ z-Rnz)NJJAi;al=6 zV>V(8irq94BYe7Dbh2}eJ%i~a@GccqlP~zmmWhe8G4?HuZbiKjGNqwA-VeAZo4krb~(etC+a(jiDo>aUhz7(fYEN>tsjbYTjv&aj!MsC@dkFVGPM>k_p zFL3^Sj*Gx}I6wCLt^8>H(k27j89RbZ>1Q8{d98t0?BdcBk92KoPRLrf{XD`oAd7Nb zV#@U&pJte+&)Xf~GqAmze?#Z*q*ElH+=_F3n{5*Sk#OM49`<%p{wY}Vb2hwKVsv_X zg!jGl!&My(jf#wBCK%##*&NgTVS2pE^Gtj~?ymdczj^ub2R>fjtYRzglnv~6B}<39 zM_`Us3+k&|r!=C=*Ehsvy*EV85Q&K%md-@Nvzq>Vr-Edw=rzIwAI*)$qVIZ%e$sLi z!gZM4$b{Ry^NjcqiIE2#ZADpIU!DX6IQK52WIW~{hRLq{>hVx|nX@Us)yavT9=oj* zT9HlfSSG%{{-OD_$wPa7+9GM}@w7((B-XQUOmVjsjB(%a{I2@s`&1(OKKY3I+$eYE zMU{xHjE3~AyUK#=yO{HudaCF>K>pU^+Y6Sy&(5XH_61!f=IGUlU%c=od3K91^5)8= z0JT;YCtWdYXF^rSwN*ra`FMN9e34Ok6Y^!Y$@QBph3(>mMbF2EHGWli=F7aiMd#J? z@b=rkCnh!MIN4sUms=>RJhFYYnH0cO(xMw!CJV!u-*I<_KFi6!WT^Z2g!@MP-!KT%&7e#707P2<|H-G1TOu&gWq z)^5Zu_@q+ZelOtG{X)tcA<&a%`0q{$__*3m>u#=JeR#-Mjx&gBo4YUlblzvT`xddKUwUmY!{#Zwk8EG)xifC@VLQ8* z*mMU-{nw$24ODTRQL8FRTF2V~OUH$7UWmgD~;x6ZhE8l1y`TSF5fKDxFA8K#!&Uh~kJH zStslgf1)S)xOOcR?+4$A%Vd*wi;^`s{}!K~{lbC%-ivHFkoYO9tAi(&PEiA#yyb)*5j zC3ozLf1pM`mBR+-a!ZI{^0h(!XG0DJMe6U&=HxG|8}(wio+`gbnKj7B zL_u$%wFKYaO`1@?-R))Wg@k&3gWTU59U#mFLAXx5ik~$0W(;hY921`dsp_dMTV%V| zvm|$1iizvOdTrJ3!LKsx2rHzTT-6T8J1z4uW(a}FrNWTtbFgJ)tQ5Zws6k}F!h zlVgLr7qE|hb&+wxG7GD(?^f3;b(kRsf1FqD)z)Ph^M^Q8azqY&sQ9-wXm`1uX%d79 z2}(Q}k-vGq@!6Zy-5!_U0U=VZlh}*3DHM*Tcq#$+^A!fx2TJnv(Mqb`lD@{ifp>hy zkKg3xp52}G4`GsLJ09mbV`Z?_#@nLLOKih98Z$?v>_xlg$d}zoOMo5OP4~|UwMcb- zmkwV-ZI*~Ci8M)IJI&Z}KL2J@@Hn79 zF-ag4WP$ZxoW9dCWX= zpS*a^2LK?4X2)me?!MEoc+*jKS34&lq%UUxtM=3v0HhAmAy=+E(oz*NBLCifl77v7 zCnf=g_ek7nMi`&*f4s>tIzi8CF-P-+9xK8oU|O_8t|N2+CHBLzM(P$Yy^T3+sk7xn z+r*b(o?J|l-{a7=cw&770}K-$0aFOmaU9I`4G4*s@r(gM#bTD|Ctq<=y)iUi`$p(E zTcSlZ$vX&bsC1VJY-NsJSy6N59Zjt__I5_O5Lm<&!v~>K#h)O8DIQe=T>lo$?BW-S zJex_UaaXVPkf4K410aDHjr#fir#%(|kI`$Q{aKxT3|8(j=h!&Q%zpXTcOJY&Dzb*; zr*m$R6B;9)R2=JA-G=;ZQt*lVBp4gb;jBD03lL2Rz)M1S4a)&{qhghs)It=&qoOfz z<66HYqQawNaG59a0v*(G+-ZH~LYAt6RZ zIao#F3SNj52@!B>qh`7m=iHPfR2_$ym5}UD$S~vylV1rptoAKK8=Y&a4cfu%c=Vo` zwO2mx|NO>qYI$#rMJllc&~i>3-q<(rowfYko`qnZ%8WpMhtO1}8@CFWS@sf`Bc*Cz zF8T1`D>6I$?OSb`)7UImgNkES4c%f}T(`_E{5HzY%x}~4Iabb`_NlH5P!+Sthvd7D z`VqKnK(HB0Jjsi<;5I0;s7ECm^SA%O#L|Sg)rGejM(=qlcT_2%;;O=gUzlr0#Z}3O z9Jjo@vftVE{+1))mHQVSE!X_0-uE+mwb)6q&y-%TjG-OBM zo19SAKj9|!`4l%h!o+39%4715T-twU7>^wn=j6@vomKOXNkZc`!R+2ox7T`BNVfV@ zf+O7xM2s&n@?FMO1})}SFRMTYtPKq8OC~<3kxB)wAcrChHoo9nPys=(?R3|@i6?X% z5!!SdzNJz{-&k!`j7GeN;x|06JRETvZ>uF5kGO}SNc2W#UhCpIM&;6L((N;{o|W#@}QCBxpgf4vI3JZ51z)1Bt$s$Sk;s#n4Nm=5!f$)_+S*Y zXeBFkv6!UFs5d^YAyzf&En^vz!waWBIh-z$CpOX2W#mLc<&>{Q?ejm*g)pE95P6a^ zGnqtxL&3*( zsYLn+A#={wVGBdP3n4onVzZK~o2#!jDIU;%Mz%@Vw&6|^%`tXv-rh4K32d{eYi$qr zQy!J(0tOQ{TYJg!7$PBO}3Dko)DH~rRWx^*x{1JIygXH^9S1jL7m9ac3CWry#? z?Ig?v&%V0@+Xo+1;uH$8wp-WH()lo>uK2oe$@NlWZ)MbprrZA+$+muSeQtm*pw&_8 zoB-2WkB0nq1~p4c^#?f?Gg`H@>PDrFZwHLD!ULNhh5@aAc;H9hn^xp0@=(XF{F_0xPxP*qI#aHP#?%T_TNvmjf`SWgS7S6egR4TwMk6pV7IBL zF1*<2T@E-_bHFB3Dg65@Cij3trlLA&Inn&y_Ii-{e(t2AI(K6CICnPXE6+E;aYkn( zIG*YSbl9>+mat+lqi6}ftNF^kf=*Pq+&FnSz4DRPb3Xik;mNb2z&m zOCI9SoXe}}lD`*@v*#NMChG zR)+Ba=PbD5EIYh6yk^iJ%iQL%yc3&V#MNn8uCliZIB%^37>h%)VVdoZR0Pj7e<+1!M=Fa#m5fW2+ww_ZfEI ztV-9J%7gE29rb9q1vBcmt06L}mKl&YN11SmKBRBq#ZHg*kL-0c*MplZAYPp7a-o45 zNh|w2+?{)ij%~>=#ZjsW3OijC>y39^B2IFVE5#VGiz(yW`b zkS}UjR_wEF_u8*;AG`i%spry1M+r0zF(*zc?L~(F%u|c`+EY4Gp&rQJyDdG;(%7xt zRUz%g&0u*_6JR@eY!X363*Sg@i-`r9_aFB;2hVhj{O)&4-UUC0;ZotX>Az=C6uZB2 zt=|wBGcyJdz75@zBV+brLv`|vLT%a4J_A76Gyh?dchi1kGCJH9Crj|ft$tK(ioL!pVvdeZNKa96oq8wFg|0qp-V z@Rdn8g(B*`w!yuUxz>rMWLS(mu>46-inZSZj1%|74ufE&!u&|v))XBfd;{i?C``or$fp4;7!mHeSV6AExYiZjuMg@J{hOXMNB-j`fZt0y z9vfUzK9_*eR0Q{=37S^K=GP?L zM{iIEgGV6WrqQtFnG_^bDaO7H9#RV793RPI-^*eOaKC3KC|LaQ%@$^ajZmmwj`Dns z=REd4fb5QWm9uOVEoY-Lb(YIA()k5F3`oQeBo2pVKR4a!rA9JYd80LPp$HAWymH+w-6#i5SDciLT;sa(#Ago)HTz+YRh6)XYrqlQ2+aCfM zvvcyQ`;VQydUYboKV--e`Q9Th=ZqO5@%I+FdcHX+cTBi3nmbHddP}O3`Tb#fPv76+ zS;;aV_M_|f)`L^>D_8;L9}QW*U}f_q`F;1|Y-fF_*yiZwPJS2+VtP(LRjJ zZFvknR(Fe6MLjS8QU)rY*0e~|H!iLF+44U=(FMeyV9avudoIL$3Hiz7qR*n}DIQfZ ziV845P|-rkc;?xXf9Ir67*5fy$I8yqF8CFE@?K}FXbq1Id?WCO-!lCC@NTm(*Z;BW zU$rEc8=xF&EutNFqmxhM0aL<)$R>6X+sO&7&P>bn_30bAWV0DSkrMQmmsD#THt|9* z%f`?aLwd8ij;XASp%3xn1O7A&0gZch)OKJ;zk#WI2s}I1o&ICIQR7H^2Mc0L6vlTF z@zjbQ<`z#jVbIf%>yutP_SfC)gbq%t6?mb8g}ljtDcZ7myUai2Ml1FR_gLkBT*B(Lym3L-k7=dJCv=>QcG%?;Z)$^J%r$^A^`{r|+{ZMUt_L$* zZO+LbR4XcR9Dx@*3`K~1PZYo|s}Kxs3fxNb*_tZPwkZ&{???1xXw<~H14=*b+NkX2 zXrf1`IYry}zf7D(rQn1CrL5-EIJ773Hy?yafkcaOBoK;iF2RpkH&k8itADeO0->jTm87js?#3}dHisBKeuXca>}+* zB(IzSV`OYrPx^MoJ;h*%M0|x?p}5`J*&3;c2qrl&fR-R zrQkwYDqt8xKy@2gd?=T{=r|&DHAwr;$A!072i)%53JjX3wo-#h}Eeg7o)4 zB-<|B2!pj4d|cKzx`tjtt&-@tc7of0{@qH!d>gwXOAD>RK>gjy_{XF`3uGA&){m-1 zcoH3Mm6nk`u75HcSk{p1XBz15J_GZi=nK*YPpW@JbtauJrug(P z9itEI_|NV+vZK4bJvyN;L$e%+JL|lS9X4LYvg-Q<5LI72WxkmKAtuRB;ig}soyXw5 zTLZ!T?I=FGo^&-z1jjYnK5BJ-GZqAQEgj^DF)Z-ic9FSP9$nQD!+5}7& zNQb-W%#Ou%e=7Gc6rHI9=m6i}UQ}r{553l$fbMB4*>0ntgkp#K+ppGQTLBe4obW^#rl^N*qm_o%g~B6;-Ag#pG^rAfDDg%Z}1JQH|DHs!jfKy+zA* z@>>uDx(ipKUfLY?so7pgSf79OVh0jh)Uhy1;4JX5{;H)@8`MfzlySwHlk6z0o5Xve zw4yn)KkB;PW$fjL`>}~iVV&a4d^n5LY(>bWO1j&6?YQGKXP^>}$NWe;bF6R>zHQfA zMLS}EzTF|0;o4qxF4r{eVq8k^!-DfMQey-P6M}vnz_hLMi<5o(A1HkwCQdI;Tk?ZX z^@>s>238|Nqqgrybri=2FXLvH)JB>ulJnL8~7(I9=Sfh9)Xt0Ne_S|!eeYA zUM!}5;i2df4n6^?&}}uA6so(MLj)$et%K3neo@>PQyT>R5(b4`PU<$|!pzeIjF6`i4lEFERAY1f@iWeBI_CsUfpYVsN zRT{pSQa5Flji|k|Re6IJN@*U!t!l2%3<*g#(m)l1d>`n4G-2(XA9S5}rlB7j&kpAF z>n)z3hh=;7@(J*}4^9}+q_Hwd4}ji_sYh)^1F?Wg2+nI&9Sek=OBA;+&IO$MR>s%g zy*&=|uC@kWHc#!%LDTDxK|>LD;tZkDaFeHD*7Qmurp8COAX``y??IO9wi}N*FY#eB z@ASD9jJejBOJXyXt9&-a)nXFZ4IR_J1CQd%6)=f5Q`{R;q3@6F&RTG{e+*f+s1X_{ z8UuD*hy z0;zK3(#Q`cvrX`AvdNL_R7eg75$^lnhyih~RZM4P8HABM8dGr>+F!lC(R3Q2`;8NN z_b6;lrc~{FQFIB6zeCp%A9g9xF5V^VvLM!Gs5l-U{w}YpXg|$TX;x8?#S3ZdvqD{6 z$++(rX1#R#@zx`};j{EGOkyBzu77AiHIby(fJ2h07rdzH7=awy2NbRasrN@=4;OGn za6FN*Kt<7EB*rUXks?Ekj<+=F?52x>7Xq6t+bDrdG~rk< zm5MFK*F@Vz$mPHA*G(UF$@cHg>I)fa?jbpjz_$X}%fGaB^J_+Hi_QJM_4(<%n8jIgw^q^8I~F7Hj$sQx4>kF{ z)UvQrS}6XN1_}%h;aI+1T&X-TZS=n*GZdjaa8m0h=OxEy+9v|(VZ$g98mK4UHPHc` zMdSx#XEVZ-m6h8v5<@kltf$)JCqc81Z3m%4d>}T_Z>MSaQI7!VQV5gAkw%?qd=TvD zlqv(jtnU*TR7pE~2c~7Ja&w~am!6d_4>k&jrL4j(;jA#k^9Loymyzr=>U$(UUQUe%`wdui@;lWwQLZFg|+k-}-hoCpS z-)(NRo>cj;>E!BS1q#87=zOcL#YtE?wLyyMd!rI(VcCjx-iVW|RU*4 zY`ycy%DPHyrxEB9kW>ManBIxNAii@rO1f?Y=Y~|}ajUlRf0Ty?Y@l%NDF*s7UA9Qcbzf~{aq!4kj zJztN)IK8*DeA?y7?pgTo2RO`3t$o(iGH6QE#tr0~BZ0sV2a4+vpLW1`Sud7t`Vrz- zMt8@;(4QO0zFTiRmR*ps#>h?mknNiWDOXVZVHblAeXSXQ#G_8A&A z%*w@*{N-oeOVWL=O~5fhLhX-c89P>SKT-#cZc(-r9W6U2U)*!pD{^F)0Ghr;Kne8B z3v_@XN|Vrd&{{T`SFt2cLF}U%u@*JIXYFfIL&V=cFqtf`j|CS%!bE8}I0s1qtx7F* zJm#N{m3U9@;5=0czukX)65uyL`PjkPg9v$(suXLVS5@un_0fE>?nul@ckLlf7zFGz$_o`uC3uo<@ivZ!gW&0nu4`OBXl>69 z1=Nxr5H3WH21B(D1#;QxW^~kSbuV%!6R%=0;mCQcvxn0t%`uj&0W&mrsd+FX64wZo zh0ib2HQhMWKtZK1&nz)AiRE1IMbjKB85wPEFa16N3TbRwIy()`qMVV-?B|yJ&#{Tt z45pyfTL_ic>mSMe!+i_ z%G(?N&Odu)?%waOk5FDmNpQQ(K-@biimtsub_&6QhH>;|`&?UzYmKNp zaqS)c>9yV6)eiXJZx(n186E=^@twB!DFov3xJaRk-g7*2r_^e-u?1+zz%~9JK@6?BF48OpFZ6b2?=bA4`=so}ZIRsuGBtmwI(Yu%iUymsw-KWee6Sg<0 z$2SKx_2J3}`Oi(+DosipQSCU;ZA8|L;6!$9s?ckf8_4y4G__7wB{r`ukdtTD9p!7v znkz?>V{KRG`AK7Z$+gCi<3c7FETfY~+*%;3q)p~b_qge5>uP>XMxs|!czF0ue0QusA8RtcMsbRBqi7dC$zCH> zoT>Q%F|Oo;S05SOh&}9`>#p7?ShbrT*YGOdB0rwgN8U~U;va|~a+PDhibcM#8WS1_ zJ1gFgY_@9*$G(Nc59HM~iisvR?ZjHbjt zLfe1s$xP1@w@kt5lC`!6`&7jiJJ{6^)G8q|UG`lkhq5a}rK`d6Hyqm|?_ zg;k?QN8SOCM}@&0Kg=sU;yHFZ^kTF$wE~a8;2qKS_#DrjYUHTjrfkpLf?v9EzMRML z?TLYkC~I@yk!yW0qWI^cxh;OAn=p!ie%SB|M3g>r)<8zs=_@7=6Q{n^)`MkEnXBF< zS5@LFy$aFMkT1Kdd`4`;p}z;G-nH2y-30Df`9OX~i4%+oCKa3W3liU8c54~MVS&~s zblnTOEDg;2i{>N{%dv;)FSxZEn4`xfzMW)2WTK~+0_xg5y=#!()qMkQ3#2@+LRTh2 z8LAo;jPXmd8(Fj>c=EghS1DpWgeu_yU)Se6$?A&1VIt!V8uWfVf1u6le-5l^+vT?y zA;eFwX0ur_G*8xNC~+M^K4U&}7L$WH)_R+Zsp%fl1JyFhZBUS0`(ybS6Xfgc%5F55 zbbs2A3~%FFoO@WNg(5zCK%AuTA-i#i*!qjNXQ36+h+?>AfLiWK>P!uXO8c(m4NSGg zt@nxw4f<>~rYZ+~)Y8wp@+lyZLuMTl(Bd7&>{vN8MGd_-ElnJyXXjb)b|7!IAy$b$ zFM2o`;U$GWG=p;N?OI&@x<}&X{9NkPQc2cYLWPCm6eCoaC-c1GZQ21P0n11D4jngBWisX$$ZB(5;@wo`i zZIv%+KHCd^A!o$}Cxpd17wg(#9n}a$*|s0c0U>}z?tx=2`}RoXSC3xIRV%X9@m{{L z(`pA-%u~3DGT$8V(LSM78?-oKCS>f;qR0?y$gD( z4lOZn8EB?>{(qgsSEK3&X&euGe$?0$s0Da$M{Ny8Zg2vkG9UiIZ*C{ob7!d5HZjx| zej|!@;m@XvZDrQN1)!-vQpg1Ag>55q{28-RCF(@KnV~VeFnV?|!u9Lw>3rrpSyi?p zc~u-&`+gLW781`0LcSP0r5#y6%VL^-i3@;mP$w1n9G8OI9v=a!jv2oy_2eS)4Cs=(SSB}N~}WE1~EVw3}fPtTTOnHnhcDT!MU!*?OIrN{BJ}r59`cw(f7@ z*@Q4vpk?9rp|;U!%b(Oun`_GAd9lk7 zc9BZo*TO0V$>O9`n|Fw!+ilqIHwh+yXv$1ReyT{z6f4L^2B1X{y0~+8f^5uvS0Yu@ zYOOb~5n(mM1ZXi}2QjN6@BM_{qp#`Mc49x_NkT(Wg8f;azk2a z9V(M(AHT4Yu(ZY$YRj6E=_>wCsqx)OgiV17G|wO%Q(Ln5o)>!@vc>;p%aK%vu}~~L zy|&FTU~R5iJXN_i8*A&3z5G@+<*M?NNhn9f(3}{zqI?DYl4Y{hgj+jI;;t{aS4j9EoBWEa%y*c@ zk4qcYkbmuj=m+wy*O>sNj_r6Km1VK^ktBtB`NhaZmf-dx>bkz;2pj78jeL*KEn7A< z)SwA7`OqVxF?F*MEw3QWb#KnV%QUy-ARdG%mh2Rm7a5;N`tSswc@c#Xd*h$CDYqpl zc^U+yB|H5m5s()~LCRu7ic6;H+FS#uqTz7-WaNzqRX~BmVTj8)%e3QwPVtVrOwrp5 z4}mcG3p9W_eeeOjuXSq`bGtVdKP6K&E%A?af8_5X>?&nEBATPltG0M>o^!9>yH0Pnsx_a zuIq7u{Ex`2&yaFy;8K!eNts9#aDwaN_c#SN)of9#*L+P1ndwg;riF_xXp=WvHXn3K zbQt9E9mNsb>igiE3|m$+nUb+y{Es@d?`loZ-WJcP2MyE9>7AjpzpUF5Vv8_kMYL8$ zaS_=nVV(=ZXR6> zxhUA$s;oK&Ipa>b(G)KGR{N%2O>!Ze){%`!1^?91hEF^gOqv-u44P{-juE zw!K>}T*wA_9NlRYkji$oT^GktYlbJ9R!9|68t6H5?cga#&99q9e<#9tKfV$q{tVjkXZG;Q1V7+s8A`kehi4Ev86 zOL|(733=qRmA$58#7me76PIUNv;xK6H2V%z77#p3em$qXDE{oq4lTww*o^vhH0||z zu#rYQxA9*(^c+)>C>cO*>5g6WUqhlF;{?1`L1xnqMN4@Z0R^vQ8>5^Yb~s5@g~e6_ zB5TIn#?C73t9#tZ=5%Ah2}^$IX$42(FX@AfXt;om$`nnxRb6K43m z?(Kp%ZC}h`t9jFttD$fvTSLK$F;whts}{`75jzKUJdZfG)6%x#CBKC0oh6m6Qa(F* zP?-jSTjY3drG21W4t_OWv7)aVn7#@j47%hV6O#e^1ujbX}wv_~P7 zXD)Wus9Pyuh=o_6>H`w45lo%vLBDI{-TZlYu=d-)Z# zs=vHr4(U;{R1#r-3cczyL((@Vc%E0cg})P0FG#MT-r02=iSe2AzYzs6jZumknr;}M z=j#utT3TG!qRK?eK`>3DN`w%FoQDWU`K+e zgk<{b>jA%{^FpSG_Wd{KwLs@`vp%?fLA^^V3*?jCEXbtlWP&k^UG6mku-X&u3uFLi zpU!DyS2vf4kWYHrYA=)MQ99_3afVgjnt9+u$KvO+-wZ}EPt+x`)4gdKrY`Mzk&a9M zZ1#P&(;rj&xaHp#R_aKv|NaHB=ExpdWkBB@-h208@JCMw`g9>`X{@ zz1X>2_f_#E(0U&eX}!H_QFuLI#jjLr+G3or6p3xC9gj#bmIK!rXatDA zXz#CA(lWBKRfrH1qDxU%K&E15UjD(ACnEnOSvq=D|dvN@XbLO(t2;~E?Pj}?`c>od+p=0W(=f<<$c8lJKmH3F4g zD4to*n6jU6S6lSak$faMtWtDb2lO=bETuXI1u(#Fihf}BuHI5^#Gq{01)iiioj-L3 zartZK=z02cn1N@ZGfT>N5?732VGV>N2X1r95#_Fqc&~p-OPgYLe9F7L?QFO``P&cN zPuen(9RI6y{}*HcXTXN(eOxqDs1WdoUJNt;hV~rMLNTD7B@O&L#&)wc!%b}%+^dnZ zFVJaWo7tY->{&X+C_&GGc!;y+eh2Jzfq#q^rMC#3bygX$=@ic+qeO?y#+N+D=%6X6 zD{hO+Z!C2}&nW8_oc9)5ckn_-Jc(G2k(wKdOZ%Z-Xzs=|V!o8hSuZbS!f%jHcSsQX zomo|v2IVm_Im-6B}(&_y~p(kW0Z(AwZ<2!7l4tiq}1fxs%@VVEqLcF4Jj88PkNw;13fhsdmVeH%G#I=3bBL zo5eK!5$^Sx_6U>Lfb#GBk+A{oG}_ThiAVwbdGaAERH0nr3sFnU^;vO`qkJfSW9q0e z3q>)_%tV%-pusBh4hT!&A|-XAv_WiC7`tpc$+)Jm)zI}pq*(<>6%p~>WuR3{Tm~Sa z;|q<+$5lH7hJuEGuBz$-5qTeB@m94qC|t;XL;JYDB)u`_LQLJqqwlJ(axtDoY$`jc zyesQg8Z=6{Y@BauD!57q`LLER2q1a#yTG7KY*FL0d$zJ_02kXsr|`p{V$ z)IT_2cF~!CKwDSvYbWV2+wd8IPZLaj4Y?*@55!!kCmGO#H%B9Rv9H13sLOEXO6Rra~HQf$$>}IV%-}_LS(kMg0w@abCR&XMTXVovHnIf~spSDK?XiC8uSA zD#S=#Y3_#xIomgN?6RP5=lG;fIgfEcGBR{7P9rFUJ`^OKQnfq=4#Y2+BhmcWPTD~> zH%LuCu)*#dY+|9tY?*D$h|7=9!XCV;%BKwxSGn18Ere4>E>}$E#srsvZE-N|yAgPw z?C7Gkga#v!)r$u(c%KPE3UElw`CNn?fG26xKQ}fl zZh-FDjwUgJ>y*a@?9;69CQj*t(&b9`4SB#-Bioh#TEdy)EhM%G+PGOD;-dK@}w?6GA&wmW3=xq;&N1H6-Gjloo#r)r>}&38|Ktq zv{=JdVDMc_3;fZM;bZLATHF1!?wnve#lE1Ha$Mnj;0=NGLz~!_BCR`b0G^R^7|pf+ zyif!bs?26taF+@c2VXQ%M%Q1F=IEYLY~*<4K`UjQik8*84)C`vmQQ-t-mA?<#7{fs~Gv-(bN7So( zp7>ArNhBd<23P25mS+E%2C4`kFLBRPmE@sPb(kv1frq&Uf+@8#E#2VdYi?7ZpygSHHaE{(3+mvV zG0`PHzEzB6{yZaiS$*9JAOIoX8h5bWSy%CK&y*cH=j-lD zy=L_J_LBLq9bq^}iY{aPGN|?DU*=~Bcugv+JeY|C1S60i7fzZw=7&pWyM%?X@EZ)S ziUr&xyt+J%D;B8fMx2o_K0oxOtkANNkzh0{RjzP7EwKa$p&eawsfK{3E8Qr;qlCtn zQdf&4p39xN4>H!24g6Wejh-qw#=xU^A+wX$BH9OdrlT)oJC8AuMW;5Jnl52GA6Mr! z75hd;-0S&&*n9JMsMr60xK61P4RunAa2#bTq!=MoM96NCZIrUkjGe4g3PotMZ`m39 zHnx}~OZI(+u_Q5!Wr(p2=DtRCzMt>!{(kSr{kZ?W=fBzB%k{pl*ZO=uFB#yltz&Fu zs-0$~1}r3YokfY!3p+W0aA=j4WOuL3fAJM*UYzmhkNpdqtF+|Tssh1r!`9sceOuOk zm6AgFP*GSJ(sc7H#`c)PwMRMiHv-S^zPB#umEv4w<&BrOK`mWnOo~lV5b4O4nz?&2 z;|2{OQkt&!CIBMp{C#mMde}`X(=zR!k-1DqY%aY~xZ<8)=HGv}F9_>B2Nj@|cRmhL zA=Wo+**F57$bxaT-#hVh8P~73-M-aBGP?q^@O`*`8D=7Iv6GUquJLr+TOK^HkN%6J z@~c}jGJ(SVJ?%$4!!0-h|9Y=t5u29?bW9>l!=b_H89DB ze@ebvN*XTu!X!Y}0M6n^ejF=wYnAphQxpKhVP$%oe$aaj9GWZeP< z+*0y)C@frLVc=`mV0T6J&I&WEDhdvu2p4xIRBNIiAM#6^@#Ix-xikE@dax3ISrMkY zo5ZdleZ8=#kh<4J<4sN;GmW< z6}x&(HEgp#C;(+CA7XJuUaVB9X;*P6+xL6FX}o!{;<;5v;rjs z?|RPhm)$YH;ng#1&aMmb$8X&TuxM_*28Yj*rJz@P?z}XMe|O=*fRCcXT7?HzHO4wJ zCwXev=Nw`+W*Iu)uAD4^KBWQOqQwgvcc>(z1FOwza1KhTS845xCw-m$e`}^OzWq;n z$ItiI|M_h`e!hRK{JZ%3^Zoyfm;C?K&HH)4{{p4{{}=H8Gy)iEebciSC-lj&9$ifI z|7yDK!A$1@AWWxh5`F6O$G68Viq%OXOFF*+Mvvq6e&PU>R{rw0)a}8%csvQZzLKI{ z*P0PPN64{c)jF|vThg_wcAcg<-D&7a#AawjZ)w67r;IG5@`=~10I6|8`uFi(aWERX zscg`ZqY%Xer_xKG+H?X7o!j(sO3dNrfDM!2H^LB?(;WO17>b~Ik>KB@V;LwSY)3Ng zqocZnU9ZhFK!LR9TG-P=(DCd)k*20t6!l-2TT3XT->rL?i&=2{QtD+No67h6zFn;& z;=BO({%HBHl_mZNri@oN7F4&({Q=D99(pquNkgwKHfUqWyY!9ZR}`bP&l^&lVGZMO4r4zJ`d~I zL_c}nh_0uj#LUq%)?KtDS0FmlSvCbacDHL7Vi8&LSTpt7+X*Y3xNcu^Kl2`>9q~!-Hji{^$2!e3G0**cTxTqDRO-oEY57Qd(i~vQs^1`tIU!k z%X0y|h?#M~qsp@X`0<9v&N_N&vTO?D=(m2@!yLDJpf}1U&ULc5Ik}7ma4r1h^DVFW zJz~c%H2my$F{a$RH;gO(Cm`&8Fx&1iqm1`|TQ0py$#L0#1K_F`C`v&`HHiLH@^Dx2 z@!tUZefxx@L1uvoIRZ6koT-}VD%J&XowmXJV&d`z3xnSR{1F-VWDT;XHOUGlJt|Y71Z|94!yST3@U`; zb`Q*J;H|d&`JPgm-rVwwJ!sJLsKx%EgK*=ro8PRu!!B9`g9cE#xHvg zmXqTdRFe;!aC*LLeUP`=0|qLYfK;k)nrKjMZ@*ir?><$5v#EgDPDE)Y%65p!kF5+G&*pFwF>29OkA4=SW7GL=sjw1pf73HR?5T$@bjUG!TkSa;B_wUJ@-GM z{VZwhW`*H;-as#>qoR&>U1S~-2ywrJF% z7OItSb;9wt5aV?VP{B1gc>>W7{ra3$9yTNKgG$2fj`~i78WFt2QtO*Au-uNjHt1f- zD%zMqk@M`s==ip})v1@%sxEkx1i`r4Ef%8JNnf==WPfWrg=kNd6JPVqMd8wlal*gm z_&%={#sgF?N=eDyZt~#Jc@X#aHIXt$;V^FLu`w+Gpq;1mSSp#|`kR0V-sm-ASrz2^ zw%+v=3+`Q)0S)nr7h?u-vwb!(%aei|q4^OCTP=W&o;llBkz9-BSFJ;`a?8FmL9fo} zx~_;;40%Bf7(!B`&}zu<3kckM;L1f4f4oe8%+GEpG}FCSu#JSF%!O~VLYOj=PN1#| zDH2*9;0w&Bt!xsI1-qMt+pt@c-e?4&s2=_s1$13)Z?y^#o~tvEQPU{|sVd zJ0*Lyl-Eqz$NJe#wtVr-P6kHcPt!PXZNndaU~qQ%62GMz_Ps9v5X(JV z%_bbFT^KUsq_0N16=6Ov1N9vW2m@X2c#0og)gxYUcN{)nvOx4CsI`>OugP)7r>RDG z`sw=MFJ|A)HigTI-Xg?`lxk*ILXyo{2-K?VJd&sSXA=l{` z`1TKJndo?%NkJoXZqsOk|C)--Aoboem%1Cr5P7Ldy(3b>Vm%iSh(Z2F2EgYQiSzn}L+ff@!$EV@+y3Vt* zZbIerKqA%}OwseAqraWgzMty`QS@3Im!+B$ob<=O?^^YEM!@=VWa0|_`>>paoZ zULoxq5lguw=ee-Pk(M;E4VaS~MKUhmvvF5#Kqylh*f+^=v@zZ5hIvayu(Oc1YTKUs zrL|k~jFRsAUB>VqM<`cX@VNIV0rqDNz_!O_#bG4@X=&Z)A>cSmlvE%%RVAf|B!`|VQPV3J^s-j`tbvv{Lzc1wqTQmHnHeF+R z?yI(y6L%{rtQWRY--W}zh)is^Qhi@U1%#es_Hm;v$a<_BP36tHiW5K07T;?)0?tcjxBac`T793?iU&B&u6w*@tYY)=7FsY=vpxr&0n$3PzZbc12n5s7_6~( zwK@%@oFj*iaso9It_R+0oR-xd6f0Q||GqMxv{0{dq~OZUIjjdSE(y8(2HCHfdZFL+ z!$t_Wj|-)qjvXIVf2?IoSbs{-$r)ckK3W|0begYwAYz_F5oa0rcD`*VzGS*Rk(Y?= z?kyI%dp-ePlk3=r6Cbkw@ov@wKAHtE?CDY$fkj6HKn#!k*h@$~84ni5&glJujUV{? zv|A4Ai*Y=vvbb$`|EdXNmgW0xgKMV~Wj&XT7-4O8Fk?Ca@B6rf!~T)s^T`hv zL@qeL$schgiL*t9-?an7iH`&9H8ai0Gks|I$T3IxI1Yrzyo`_f+XeOZP=hVHUW|=aGQ1 zEnJS+{nY3&L`-YSjG7`Yoh|ojGpwQH4Xw;mWP>=rtVlc@zT@)}K$sSN4Azr&5YH$2 zO0-73vqVkH9>vP5C>@2S*CoMAwrkfHcKOH8OcsVPRHS^*$&bw^mnWeHd!umI3}me{ zHrLDX#3qkGraEnNr`P7|%cY$XjHJTIW)%b`Dwya7@_@sK@~rQFA+FJHYM>(l3!rU< z8CvHx<+K)gyD?Rh+(7wMe&j?NT*5rB3 zqP(C1opL0;aek-<4?P&Hu5`$b=ODT&H~pSt^*Wiu!7|DaIZT}56uBKXF49r;{-I{F zk8t@#6Z^byJtgX=Mh&E9+~v_lxd(*Xw3;B6UMrWmfte&mi>2Y8oO%WQoSwT+f^Z|^ z+_}95!<$nMEB1$L=z~b&4ou=8SH^3HqLjb+qL1BRKMEiPyL0U-c5^#=Y{!MIi<~EJ zMaN_%+r=3&%efs@By0eb`kfIh6hzD!Gp9MfG0e;L9Y8Glbt_c+?z8(a%O^4kDu$XL z^T&+}OpvFd35P$}7l?a;xUf{Kf3L+!bL=9_lL)N-zRwowb{;f6ousHeWQZJZId6wMANZKUm64Vr2|IS|43ajW*f!mB$Ltcc{Y<52R8UT@g_G-E=M!^cZ zvl%#u)L)Uc{fjbukgNLg)@bpG5<}en2EB+5c(eg+-6&(!FeCff$_0!o-ogVO$Wgsx zn{zlgh*$sWH+D|Mg9aUgp{HRIR?V?0$=mNBA|SDzbBs#GFRs8h-i+G(_$~~Rl0-%L z#ijdbWy?+Z6oqGAtN6Sh6l~}P*P-0{6Ichdm0uE z_iAW2Eo#|KU`OP^aXsxxR}_u?Xl&T#6~G$pNRYxWDq2~04D>@7jxJn45%2G})!0ys z!5o10FA0hZ5`@Cc0w7YYwmb)Sa_^x>&b48p#4D}@v+-&&tTy z^(sW^YC@IdcJG1%8ggr@(3RRcw_Np)h%UJKR4*mTJ5g)IO6Rw@;r3X$@d7SIXZ7J9 zKss*JXRS#fT3aO1s_KpwMZLM;T-6~i&L}lShrdwr6h0gcjV>dWq0^>)TTUTHeA|#Y zkH3+V>8;y8jBw@hS0%NB`=JEe)fX#z^=c9t%^ksulMkcOjf*u8zUaB`26DtFo4J?c z&VV6!?A-*?3r5{T$fsc9ZdiL^?bs0X7-gtt{Xv7TgOB@G??Xx~NVkU;(jZ;6G*-#P zYAQBk^Fsf|*^CRbqrM0H8%l%Rhs&xm`MRU`27yqTHC;y;Grv;sApk;s|4OGQ<JG6Q=`##PDMYx>NvDf; z2@(hfJH{hO_8PZ&^*0VheD*K#6hIDH$M$blH61!bO7N*m?7MlHi3?CciW*GI5!5%M zDz1n;(~7Z0`T}LAdwv`@?l#{)RKP+-wVVe?N8e`)<`a1S2nAH<)za6ZqyBEAulO3; zVT!d)Jf`)bxI*>jj%G$wz05w=0(KLzM)U40o_5q=tO@Fi-^OmxZJ;J-ze`wh^_KYw z(V8Gqg=!epQ|&BpHiL{IAvtdck?w#+5e}YQfV$FZw$bBkU9%@mukP_9*?>PZu0i>& z>UilErHpadZ**prX)FB+6`9)ZJu5V9Q85v~OdX54YKM{U8o6X3+cn}vVl)BAK`baR z_+c{?As$(`6HFlXSB-vaGy(!XHTQ{IQ{#nOV#$_ip2>A<>&fOjERJpZt}scIi?;pU zOv_37?kb1tvJK7>c-KT`==T95Vq8SBd-&aIx4ZTmz&O;TQxGmJP+#U*9^a+oD{xRj zNogfgUjC3SX63nNE2xfNb_7a%r9Ph1mQXH^Kka5NY=Tn7T2<8#;&^W53S*-d+uQ#J zn`QP?6XI2J(4g)tc>`p9!&Jr$qu2RzCZ%l$*>ouOpua9XIjePl&7}SupqDr@LT(F6cL$UiAn?P;P=c{(jb(#8deiXyVXjCIe^HYU z{bSh0ifoRMY3CjZH;XV|o0mH>?EPTO9+ZGo0vjBfq>2}+a~UAN?V7ymI2X61qEs|H zuKrHDD1}7p| zu28Q6gGF)=wn7fOH%8;Er`z*q{i2j9#R8t=4oa`OEL>vk@O4jzimkG6cMY%DbYLrT zj;TI*F&XeSNxqNtX3t%q8HI0 zrPy;JvQvh+Cp*TnZbC?5o$^TSbdZIoN97pl;PxxcE5upW{ssE2EmjeVcG07LU4A(a z)#PPvf*3_H`!)PVOM!XK{d$#mK`(o&LGfZM*M%8s-Jb9eRkI`J+DSLFtdOVoB^S%z zbnBc_cMYAXvqMwQZLYxSsNgj9IGc0oH-dr&dh0mDMbvAdl19zvqy2lj7tP^jkR4XZI7KL3PYbBuzLbznt`=Dkl^5zSm|2e>VYbrXZ;jf*! zLRa_AlW{UjIm*LuC_qZh&=i{rrFVp3EQ{%@6ra zMg#Bp9EaR{f=HdCG{TE_u#_)}V4(mjl6at=iHm$Bg_dBTeJd!n^%P>p{(+Q$gT>Qx zC?Szi)Xx!kjsda4mmlk66{L65Rky3X<;OwWx#SNB^>hzZwr&^J6@9M2Q-xCZbiv8N zyP!!s=8eSnTnbQz`p$;lE;TQx5|>`%b7H#U1~TSJlrI6)a9$@&Vk z+457!sA`}niigE+d4$D-Ck#Zn-Xib1{aoB#hQ5E%pI_UOt{nY>1Y%n^OM$cL137gL z@%E3A74cy|x8&94JI}q3`>nK62n$KR&um7UbO6+ zi$^?l(z`_>05?~eyEVGT#|-U%o*=%URHTdUECj61iXjB4;ue^h2l zt)OjB=R9mPB8*=FqZXM`^{~vmg2zwa^@%~rP0%M|GV!1CtaDtn_pR93%*jgDB&vXE zFErxx25g++v$hvKY?ASHvDlI2r(v^KtcS-3SU%xX8&^CM=VB*-Gm$xsu&tta(kT&u zE!}A97*-Tvp0c+i&ww4+2PlS9Bv%8E+TEiRJ_O=QzZ`62_tIJJ5RtdOL8-m36oxq8 z0pV-^15yZ*{&28m+HL~84unB#)INzO&6xQ@EwC$zb?KuoE^?Alpy}KWVyC1ut7c7g zBo4I`lt)VOHTIL^EYRpm_I_hf4Q`Bzt(@0@W9JOwEPZyiHfV~%jH)J7G6x%_O8W5} zz(+aMpLy%|KP*bAk^};BzBGc*dbqYX&t=s1L9TB?e;(-&WGvA4*Zz%U3;*Iw(m0N= zz4~{4@+CVAPNb0)Y}=4AODH$8$ng6UO77Y`S?)2qWsnV^kHlSa!Z{|cYWQKcOg6hn zAcw{Cws=x7){8}pdPm>lLUDW4(n>BDOch(JMx#HXi z)CqxSB*rwOzU-!O^0XOofDE&(8t6;ub*Tl1FIzphY`b)f>)5P}57utw<=a$7y-A4e z-$)fOtu^mAlnU_iNc+2($oHAOy;>0}ioc|jl{*0-BKaUZUFEuTvAAFV6F}mB7`Ey8 zmNC|2FLbVF>m;Rc)hOcTl=;=$6urIH&^(%`59xSuq7d?*NXTNJ&Q5hP$2pm)pi;z%tm{Yc2}${~n0DdTGSohZov0 z0}hCWY|a~5T``F!6waiT{Jw#u`G7F9RwgcuD6tBhBK`-guY&%y%;ki%Ln?(|H9QkV zsy)dY18Q=a>R}7a8mVnA!V76fveMez%Kb8cnblCzt{tlrxYIqtQA%oGm;Q2lf)Eg> zfk}S5KJSHJ=%`yW=5!H>@39gfAP0yofg-Xt!K^;bJktG->m2Zl`fnqo^JPdCNaBQJ zkO?p|MmwvVHS>*8`TVAt4kO=rYC1q+HXw0M=}ABUJ9if;AS@k7c&9Tnap`AiT_M3A zId}L~t%h0y9{8?+SM247n5vCZcu{j7&Xv@;ve(Jg5%@O>$!OBWFXbHal?swAnDA;T zdTnh({b8p5mLs_!$Of=bc(3+DZwf|oW|$W_gMw;bnkT*A^{Y|5k=$o*2iqAwbj^K+ zT?V-HQd=4^2kb79TW`4sgw=vTVC8;3&Ns!W$EvpC;KPmckyb3t;MyN7`=9ArwdW_x?!_(c*i@YNiPZ%c&Uq5(as^$2fAU?RdH9n z;$o%bDsue?mhu)#HRJNUz%D;`qp{-kNDWP>@1G4YY@DXl$MT<`M6kRX(ngaXVwR4D?m@RolD$73~+1ZN|l72lya951i`-{Kh zhM(`R-v!tWP-Xu44)~E0fSTQ_sQmimA<(+ov%J6Fw*n^iUZvvK`*%#g%3ig$!fojt`2zOH(`eLO{#)gF;9cTW8`V9pE)^&LsGRoXG_(Rb+_^uqX1qyzTmnr zC4WNQ&X*O0tZ^L&ziBOBjlCuQtLgW?l5wF~@`UbzF8`-|w{#8sg#CgTQ6_i^v4XoC zuw#LSb1Qbf(r3Q~Q=ToolWh0%+kN&xa_6Qy(CEU12V2nSw535@oU)>Ft$NJuK@&Eu$_AMXnU(xEy7cZ=$mzEBfu9GjL)d*vcqN@pMCrJr|bTVi+BGEF+b$__u$vPFEhG+Z-7_Dh>{|~ z6<9FNtL z^UD!Q@?99t;u&LbuK)g_B???uwtk}-SJg88ZK_ZRMw}WyzN0Yaq!sBm>%Icl2mv25 zKBvIOHzNcM&a*(*=H-q#5YmJ~?6Whvlpm1jg15kkz(dA3sbv_rG`&21?;okzTNczr z`lFk$HelX1xK6>lbX>5qrKW|!~gU4A3yl@CG0S3Bj+kO z7^nAa=^jiJT>0g_feQ$$Z7;l`%YO2LAN1EWyUpl#h^Q46J7~l5ZbYOCVN#+MxPHx| zb9*Ct4p=a`vG;oes*xP_YqZ}mW{LV=h>b8Wb}(X^zy7j!ynhJq!M!*9I>oD>u@E>f zEcjOk_HQ6j3Kk{59{-p>J_?rrPHX(%zp^sYe81e6i?&l(8@I_IFAFBWKpM4f^=KDB zjeMu(taqA3i`_T-2N!BOzGYl!k8|g38U+0u9Cd*=MwQ3!^4x-jXM^Z;%{aX{_L{pj zg8Pqy^#W{cjS;moLLt}DrZWQD=Mlyb~5`fGqLMuc3ie<*2Ye>0rMZL;CDE# zVAO}<^kW9aan?M(8_p{cXt<;Qk3I*6_9dzl81Q-L-5+>#6RM$bY@oRp%i%x0V%-CBdSbC_6P7s^Zc z0y{#oj`YNs@FriuefQRMvyr(Ks(OmG6xA?ISWupegKsx2t`JUS{gL3s5=D5(ESF0B z3!!7+{A;!TeB`yP(A53P$TmviYouQ>IHoNjYn(pOgxhx+T0|SBxpw$a3bjPhsaDYq-A3VG2l>LV3ZN-0q!JjE7@~D<(Gi|U9xIK22%7nA&ub48Yqm%@EY5)y zf=;#_5Fm6PgORqom^{$v%S$Zcy9wqB-tEuIrVetk!xH{;_6>Y}3V}(sJU0~fvCCF{ zhW6?4o_XD@S*UPe9+y%*9}343M_q{K#qI?be))d$9x%3Rt1+YNHu!B3WEEGEdIle+ zbN6%37U7Hyaf;b8waq*yD;O=ueyfpTK)`Env`^MIebIXkG?aEs$wwNZ3ymLwkp2yZ zMZvGQb^$2L633&(=pRR%`tplE@+ULqa`@;X=pn_?71Cat(-EblzCo#~_4+u!uB$}U ziTr-&19JxG-8sek6<~#Z_<5#%djk!7P^3%NNo7A7XJz#%-7eFn(7%3bI&!?6Sb-Q7 zz;;I7d^3a0yStvkXb9=^-RhebPam}VV_7BnYS$r-6N1_iql!VPsoCGye;)Ox^#Dq> z+nW+231@aari@c8`Snj;Zf;WC_46}{2)8YA?)oyuiQb%3w4&ww2^GRz{m&JPxgQWV zbM9-8UiXBfX?P@H6FeISRmFO@xgO$fl4DU%xsQ0wPOMF$Rl(M5S6@KbN#4A@04TxP zn*WMJX8@rNRGmXawEoYf;cZnJa_G#KJf~Y_5?8q}9UE#6b}nhdnp;oLZ(7qjYuB)a zJ6&PWN8R2Kjtuu6%e3ZrzYZzfHqvBt{Ud*lmE%}-UxMdupofp9P{E7FwECtRE5Tu} z%LTaQLBfv9t^aBM{YmY8UJ${m0VDPzH!I-nEFsk zA+qd`DlL`dFDChG*A?2`~dbjL*?f-bDlR&O;ki#V7 zD8TrPBgHCb-t;3A2WHH;?A_)^MtA+&ke;!crXJ}FCl-oRc+SL~Hj}dZ;Z`%k1~5{b z){d%*!2w~2|M3QNat@DDX?fzPpK;L0aK-nBXt~N7ASm;WtvwghZRkr zqgcXo_nT$W@qC>k3B>2@msHSN%p4^(8Y1t{nQ11;Uhyzxzm@Ty#uMYsi>Pevmuncu zo+TFxYK@MUDmoX)tV|{sDXI#xb)6?H3DU&Q5p=X!=`t76{db6ppHZQy*{lD#CSelw zLFyAKZWa*f*OfSP83V8y+wpbF$i5PHu2L83R@s+^QD{#}wo2WVrokDB6Z6%!5n;gA z#qc$#4BL;VgqkO57ZoF$c^JN(-wH^o+srE{=K5>xxECaOV{gr4g+kQ#!17>@?^zZg z57D2c7jtY!9w4>ywG+{=%{d@Y9>48bf(MWesA6~hKMm;KlI~)~6P4r@)n)EJ$_6!O zW{D)<>Ksw&i!e#TY3kymzC7hC)smAO2+o;ASmRCsfrQBAwH!rEQy zngnnSt))NzPY#{r^?MM`BX7;ht8kz7?6n6N3~SuZK)EA*Kjv|-YZghTD={Qu)9b+_7KFY4QSugyUh>=#s;fMe#3~-IynXg zFOVI0ib$KEOZy$*PlOXy)-eYTo_2?M7f=)Cs#FggfrjY%3w_1(*Gd*_ce3@OC=`0N z7_ycHimFL_afZ6Mk$^E>2V3s4!q?E)*9%-tO9Hlc!+N-t?)@0>D|o zbGFPQuU(8}#QW#!n?Q1NAL0!z3R1f|^i$(kCt7m0Y+OhSrUD~B`}V&_p6?41t~&oS zcv=f)I4f1akOlr<;@9iuGLM#=^7s-kdA$y1c8qa2e*_9}KWYEBYh;=pu)UgruYSYG z3_%3i!ZSSt##B90;l$4wMplOn)EghT3XA>sSEmqXueqHYEMf$)IvU9@vacOxeOAQ;IqE4V$?Wu)}riD&QPgM1Wv@UIf7UEFq`3EG@}amzAmLgr6E_HTn^Cc zDI#}4%J||$agaR79fC}@?kF$zpQc$;{0Hd5&7||OS3K(g3dds1ecpImabt>-e+kT3 zE3W%266pGWTo=MB;sGO&mK(78sBCA4O>wah!)R54_!7}EDI~c961RP7$~O#hfHdO- zC22ab$7+7^j2tQehnWE$>z<=VVXXFgACA&942Mlo{wnpPDEQ2$50|(**c0=K*ycf1 zjX8(OCL(9NT->#<;+@3LYWnl8P^TpwG_3tSU&A~W2s|wrP2JkxpQf$gz~eoilSg6$ zxB0j?hmmnDX~H~ar3%^io-vR+73^~yVWc~@Fg1E{eo$Ps09M)!}~qgxV|YxDY%_1<*)+w6RpVLwpxCZv9{T2U_WC~ zM&R1CQ}ElO+$l{l9Id7t&H1w{Y{Ds}FQA}r*9IttRlb|++Z)YB4SK4)Yh-Bn?4 z+l`0@m%7N3aqdS@*&$Y)6~M!a3d6=SSxp-{=+xgsm$YpK&q)%Pss9>_C(*B@MVWUNfpn$N~l$We1Q=+9( z#MB~rr49?q>$?YnV~Fu19J%06!J}h!(6MU4B)cSV5vyls0A}P-N;*7m;KGB&4@~vF zq{n}J)N#yLPhVgAp3|qk-AbfG0sFyu zZUYlrN+KXtKFEud--&c+$sFSyhIgR_VgXRXRyF?-{XoqKFWI)g#%kdQfI=|q`2tD! zQW|J}wkOGnBddbm16|~z;^l@_uS+9XP-K(KFC| z(}jcysuGEGJ|QqfYyqh2p7brYSRU*|&0n)@(bxsqCZ391E(|>z6b&&rqMbg*G7U+! zNe$2G3NZtsmE$OM--6~g4A=sy#lVwZEzUYmIE*^(Mzr&o;M4Cj~PKt0G`7Y@ZueIT0_Yn?^4>?nqQo8LzRjubauihdNGS26AYN zoCO0j`Qt#@$rPw~DLoYya-!PTuzJWb9Ye*&mwftM5dmPr6FQfQ4Lv?%3() zaM;%Uu_~6}R zz21?XuR#*Z8b}yLvNpI>?lAdF9kaby146 zld!<=L( z-oSuhTf35FR>8s$oiJ`i39KgeNIFo2FxknlOWE-v0VY6QaB_=D+~FG)S>oq=rZ`!f zbe~q%z*Bl(No)dah)&lDDIpvCkA54=Sx6;E+W-%#$7HC2aaAk)VNk8q<+amhIX9|| z>s2y|&Kd7-169tHG1)FsSZ>8!-FRn?9dUYLG zlb`OJ^qeRXF=fM6UuP(c>X@iT@Ptaaag<6LJ*!ilg+rRtkKJgH624S9@tx`M+#tuG zs}*rA5oqLD6mxKkJX*qg&grg5gKh?KXpCdm+lX1&O;PgMK+)^8cCaS&=Uli$)a5U4 zPpW7%=#|^if)Yy`;8}5?)hDkt%;U9Di-YM{eT53iTW_;U>kR?`le!^a!>WJc{8C#TH#{! z&2ygP<1-^2D145oHm$~+iTwgd5l?lLep8233~NCaQL!wkV0_O3$^F|H0FRs0P8ug~ zrpZ|T$RH`NYg6x2c@N4)HKh(v{MpVz?ztIJS1>6(skQNvMo-uDwI2`bwPac4NI&_b z=|14FTqPe#Lzd-to}^yG94t^(r$6~ZO87&IWB%xA$|O&0;Vl) z)Z<70nVc!0AaO}Qu0_sZi%Z(HXaXOFl*7=)l9DkblRCr!&QTAyDiM7l{g;?$UQmXQ zcRz*ra|NlxK5#6=OG>?siOb#qpZ>LAWZ*~jH-&1m6XzW^XPKd?Z>9+?s#wbgsqWk- z*Ioqa^g=nugq2TV9m3xr$oAn7YOn5O8df9DN(n-$^96fCmZu)Vr<)Wxy-uw8{kE+Fy{cj3mh`)`JOjW;1qm5x4%UAILrBw##~sg`DJvnTqdL9|0scC?uxc zYnkqVzdD_M%4M88afQ6oH@|7PR=d%1qKnX6gxK`i%`^nC+?BI-&n4x>Q^u6lLbwq^ z$KoU(Viq9TDHE&SGtN0#gxww5PDlRCT#nitb+`AG-_{*&Y5)69a=J6w^gcH(Z7JkE z(z7G-(z=@pl<*q(mg4t%GO^F94{76rbI88Yv68+}KJG9;$CK9_R=fa0P<`@ay;5<| z;{Ab=^)YGlT>5U}-JQ{o@GQ5vSdaG0-e^mS~*Bmhe; z+quVYf4`Z~?uc;XO!4a%Ja2=z`jn|DDi+j#blF(Ytf4n6FN7G7BlWmaEE@t%0^fDU z`({tTCcj`v&;R&i=XL5UOw5lX(9aO(Oj-twWLoYWmRQ(;o)-~aSL8n{6_A^PA8(HQ z1K-{4#B5wCU z*m?U6w%YMG>{kfgHs}=&lV+ZX3sUor#JbUO(FJNhox8_xq}yxP<7qH!1o!w@Au;Y; zkV)V*N{Lmo7FR;$!nUhO0!>zp(y1er$R5dg}}d2iQwrT9Efx03ZQn*#Np5y+=!X8*_6FC&V%@~ zK@Y?kFB+Hh8z;Oilr>ycu8I+KtX@q&cADa3a?fygt17rU=mMnkP8RPj%&@@TaR!%t zXbjD_3Y*nK)dz7Nk^20OUTLuP&y4MTcTxR@B*%qecaKC)+0vX`%d}0Fe7y*zh(gl{ z3%fZ0m(ISS1zC%2mD<8g^ux1UTmchMHJU^uWL?OiRMPy3gzRIt-g*Xi)|dqX=!lAr z_OqMzvn5ln(`=;Ofy5z=na}+Fd)Leu01uM1c`g^dy{RNwUllanTWw_F;22ERJ5Vu3 z+A?pWo;l8`r_m5$S8H)4v5)zc!Pe0p-a95)6CZsGo|qcd=i*-nsErl`b@tNqYcXIR ztiQ>~n&xy_^&;^^2!wV)qjhcrCrHZmX0fdm5XKi-*)^n>-3X_hHRPnSJX+D3`Sk#Rl>=`=h%21MAK>s|M5j&_kEL zjn#18)NStH0c5A=JJ)pf6}Xc}0mh`YdAIKvBD?u>_XPjK`YS*zFhmy)@e-TFAM+3hL_hFqsYr8*mtG43Es0JQ%&^ zKCm$stT*R!1UjbRMa*D@{&u!~e<7X=42+giuIJr*1qwWMkqT`wLLVOWuiS(`H?zB) zu2b~d5vUghpRCWo`0HGI zsq~kB>>KFtlmvU-n_1;>l6^TjZ_Ql}8=@#bg>)>W1R*#O*{=XOhFtD6g*Om297}!vflfCP>R9Q?^?s0`-{`MYH#o*kE;IO_R5H#C%3j5B<+=1S8od^ia?5efx+Cfa+ zbOUAh2&URW=KcLv-5ii+c4p-&yneE3ie>!^6o)swK<3P)-0g{VX1}qMXJ^;S#QJOQ z&;`jNYCDR5O<6=2zfLJT3!!B4IAGF~K=4o%5Y*M_qLs9J%6q0-9>Qe*a2oQ3 zw6Ci#6Ms}SGZ&-e8UUC^GwSm*o8Q!HkB+RQH%t30ekh}o`O*);-MtL?b8tBI=FVUi zul~n*d|s%^^5Vpe8XXWDojopFLEU;LL7@F za0T(1nDfxPZnSDf+EC70zTATEy4&+}o(k4mFP)V>Z^}oR0rTUym3KsUABNcP;cel^ zEWLW>ySI^Y?iWyr0d>lAL5Ag3ltte}P)Qs9A>`-p5N~S>c(X9rVZD?$JgV4B0xy7s zj3^)ptE_iX=XEI|LhIhvKI1ArzgW%yzRh9%>_``vg2?mN1%IXg~T;WB{!Jiz!Rn7LrNgXb`kZnKIw8x z_(wE5qTeGN0@o|zbHL|Ugb_@5k*n=M7qy$O`o|FD#5eM+br;csHIQt{_y_m|>oamk$T(!lcd)_Typ`=Ic} z9ms?NWHNK9$-K5Oq%&AQp0=nEe0hOm)QFUE0s>9Vu+S~nZI^*6H|Wgnq^@W3)X=N4 z0Is#fZ~A+Qd0RBRln6kX!`CROT~~MLlY?`mT6F0L9LS= znbAjKK(j*3xAJ;y!WDd0&!l(#v+Jbj2a1}$xme(glXRb&85Dpp&3xeYB9=|+6G;nFI8VsC><4xhhVxGQP#0VrsuP56{ulT6nT z1BY0U^!UKVteO$j4&iEOa10cZ?1n9%ewf#e*&hqjdYSMd9F(~5PJy9eXb>qCq@=qC=@<|}PyvS?Iwc1fY8aaT#$WlKb3UExTxUM` zL1yoHYOQC*z3z1hfAQ3jdwl#v9P1vnm9jKf^Ebl{^O6gqOq)z#mRb&_ zf|nI{GapYY)b#y!Ln`H1XKYM?&6}}zH=M}wUrNt77?cKXinm&hdzt6=--9AE7GLD7 zvs2Fk@L#;&@1vmovcPrdY6kAonZnd@^vdMB4|UI|*B&lIBs zHIO15LIhP^cao_%K9AZE_ji3S1z?rj{b`^3vQ7+{R!+Cuv`y_Tt;Kx^7wV{4`$~;k z-h-2CMPkxiViO3U#g1fpTlX8E0D-67SPpbW0Ltg-^+|c5h4s(+bla6;jxH_r2QJ!M z02KaZ&<)c3>C6rFfFb~l?hB=!H7(1N>mLtbY<)p4KE^wV`vd443sFq~$_tpJ!~}0* z5Y4?~8%Zo%VoUyyWLSuNVgc7FHH^9f^+{0+lDG7(-h?#&9JrJj)MQ!A^u)A+s-aU& z;L7|&O^I`auD_~@b*U1;+o@8VB>$sQxFRd=&;#c)$EYr@Q-fk*>=vn zA8!RJ(J7EVlHY*_4yCR|S?KC#OHyOXIc1#{q+(ZT?Haf8CZO@QaD0NG;Fg1wvC z&Qu=gI{668KGHo=-BQ=<&Vdy2{FiClcCh0S+)he7zbh)t9NL%TXv9aGGLOcixu;oa z!7L)&EUqpsJ>0_dU<;s79XzDZ_mq?#0*YyQ1lsi)VXi?}73LcolLJ3 zr(B7d;li83gW5kU~pDk~2bAh;Fz z+bq>2KzkGA8Tx%-%JX6zAiCif_CGSHyx9kAzsrT|y%ksnP$*YirOlN9RFzMo=tMwo zcZ}_UjfikWX9pGA>F}Qil;`SXdMDf;~Ai_;yr`4x%7{NYd$;zXb%!ta?}NW zBPOYr2Zmb;;HFTD_htbLpI4Dn^SAt6QDy@-{g93UsJyHv71>Q`Ga5z!pas@06+fV! z&&@tU#)$}&`6fq+X(w`LdA54YYa)4k7~WSK>Eo7Pda20)-p08&wFHk#)=HF(wrpZ2 zQ2VP>;K9F#I~-s*!=@2iJ{SZ)*FE3jIgu_EK6^3IF)VkY0b!b!TTcS2D|UY9)`k%T zH9EFLeZqS!S+E6Q-Xg`3{zMxFTXU4lUrnK0rrc$4`Ez`RQ#B)GH*2KFv^oq(+_Mt( zRdh0+5X^n6q-Wv6)Z~iRp8?e`IE>9mf;}Bi3)BS!Zo(2f`u60^lWD>M)qaq(Hpn!^ z11?k7RG=`%pmRxDv?lG!c4%elvy1#TW%U?okvhtU!)I8hN$7miX6qe~-&WiwB{5?l zh+g+^7xQ0Ly(172M=veuLZ^X2KptZ$* ztDJHq4)vipjq1_%6H4&aw^V2506`#YHQZ9zYXMKI^pt_>AK}DSE9i4YnUv}${~`_G ze8m4Nn)T(7_|{bxBJhzoh4aP>4a$$@TkAS~CZE01p^hrIV;e`Ea>(we2>X3&rS=BR zZg^H^^KR`FqVRC?Lla%qp&|8ZNUEUh=hJHXU)vO0FMG`ayAdWFWV>1J122#w_~H26 zXGs>|-X+#EDuLDTj3cEx^NbH4+ov(P6le|6=w_Z*TQ1IO$Yj7CpG}pmt3i|M)e_mK zM_GYxE9ldr3^A6@`4`{t1fQs;JEkID3~{UOHcWK#*$%3qCN#1DlFp&^xghZ}uWA{X zWtLNQwAtu70C22^821ZwzFo*vJlwU<`b83X2^b`SI&`c%n9BeVuMR9>;&Yf)hNM*8 z1y3H2P1bt)IG<1JvcT zk#E#?ps~N8Z%4x2eh^)7oHg(z&uuEN$;&yC`6}o+T@k$F{(ms^Hj42^j_w}j@5h{^ zNr{}qZwaiOcDt9p*458zlT*fGbR^d5?qB0D9%+ov5OtH>QgTU=3Mpj&K3-bX%b*Wb zbSq_sJkJDf*|e5=BIssM{=it2YtA@cItbRsXG@n2l(8Jjyn1uNNw?TyVR^p z$6peOpS^l&qES;#Br*FNY3sc+@6xvcIu~p~GMyW>$XV*zKePjm1wG!GwSokD#PG#4 zP7RHQ;35|YdSD)UUXBhBZpfmZLz(FpG-qoI$z+IQy!X#1^I!OOJoYByy9n1f`v8`r z|NO&vG8Pd~x@1}F6V z7V-(Q1Pl9HwG^NYO#`Mwq!#p#pf+a%!nH0J7Tx2mD25EUs8{Wh+@MdD_RK(5&HRp! z4mi=DxxsmD+i$J0&9Q^MoTc9Xca!7jM7>`_*4%CiT$gU!r)Hw2-L-Rlv(dQY!g0It zt!4^$XVibsi9G4kihHHKDf&rya-;&2WqGEd(cp3Wys__PE9G5EV&Y@Ol`mfs$T`-- zQ&8UuE54&_`iKOxeT)6dYuVsHsbZ<6nt7>?Kj$HV;Owhz>SES6ZOh`+KusA9hqiUD zKh>*(UL(`Hl|dJBUrT{najr0%A>Rsh<9OP3 z7zK2J-&~>8C&nM!p7ViA$q21rUKHBdix!S}iR|8Jf7d3fO#`(hYWf69^?n1T zO_a1zdt}1y`<760RXe2ouutGl8-R8xZe)?$IDI4Nj)?UkU?3ylKRPYE+sKF78eSQp z<->b)vHlV*3zM`DJ1LdgZ7Z;D`@-Shb{5Vtrp*a3kXug83Rk;7aY$e=2QH1_puh&x z==vdJrrIzMF^Uq=$)vy>A9*pYqdq8&h-A@A=|rJTKE=E$SN3UR*&On-Ygq(fkDU%L z3)-yW%d4Q|FN|}wTUvfxwu&b@8}ZAi z1e+0sJLFQfBp(`qs*`#(jx?&q)`_>NS=FRImM&Tqsiyv20Fc_*L=XKen(b+H z?78>DhxMu00=1{BZ#q&)iFSu`s;zxn(^56+Sl4W|&Qb0(|8Gvw)hIDCWp<$7* zN;Kcb!qaT7hB)*vc(+RbqeYy5O(qezeN_trC-Ls=d{=ChWazIrD-u1CBX!C z*?TmHD6(*5kStZ&%ok%0d|LhY@@5d@CE%_7N2q6|%Kn36-z%`KRGqWREEh^(4+vI@ z%wFYbus$98Qk82py2c?VCjEX*Kp`Kl-tFdKz%hq*_G~&iXhDicx}II=C}^>PRW?Cz z1jOb1_)~aotKP<8wWoBAr&bzC{`$+NI-CB9_IXmk5Ip)*+p=NG-~=Qb8ZTBGs)+Qe zd63YY>qqn7=p8H4is0>9{}p1J`LIYq4GrlMbum^ilp~N)$)U9>vrS7eQPxirb-iPN zg(WWVq?{tGI#BmJ30*!*>Ik>4=I&49lxpAcJ+a82f_+g@4I>&%!xi=d^&kDaB1&KX zB#^$(5h?GOi>9gJ?+EY`6{D9h&|oa;s~nQje_{7jAfw{liy30cU-7S!7m>H6qo65z{+t(p9eA-b8-zc{66xyS9;~55gU4`mK}03^ zumK>)sy-5bW42$tls_vHN$t;7rY~ooa371%|7tc#ruFNbra?aU(;37^A}E&^n9tP3 z%t7i8A*TlZpM-g!c8aP0AbaE=%p|~6<2@FV(Lesx(F;z#(W+!fT3Rj?T3tQg5hM?} z_@OF*6W;mk%~wK#nF4GJ)1`7Ihel+~Lex(N5!sa5?@A)(f}yl(mF5t$z9l}^sm)KLEq8z|7x*{WxxhhRp5EL1-buhVNL zA(#-$idwJaCfgtDms1@9FAjge0wG%YaJlX>rL8I^05@@C z^4<6kL+)18-s;en8?>I*^BbTg$zMRr|59{ngz|e;Ef51>%~NS&{X73w3cy3aM~8KA zbVDdgJ4Msqh5g;X*Ysb1dOGrdr5Mexsm z@Xmjxar0>IZN^yuy;}NJn&74c>wnK+bU}1bK$`)98>H30CUNt9`rIL7>*CuW^Pr13Tt$7n0L%x9Q%O38-Pz%a&{V-<>KP1`irm$=-+#1 z-e75O7(j&qHd$_D9CcRXFLa5-_4rN6Y`;NPn-|Il2La@KxV4C;4uFJ=M^t>)(AO6tZNn|zGu}I*Jrk>1T+rDkJA&iJE9rKbQy{xfQ{UT%^R%bWN|iY; zt+suG#Q@II&VScI-r{HdGh!gAS`q~tY#}l75xGPxz^BeZ01@bEMbhHXQn&5uPu(v6 z#n9-TLqC9Pdd9_Pch=@3hcP`nDs9O7hVZc#R8A-A?cZcnpkE`tR#_+EfCl=eW_3<` zboE}G-Wve=vyTAfZP_>cX8-y~Lo98!(wEG67TUjgIv~`e0nXIIITok_K^H(!Gk8Nz z160xfFi%MZ=#QqD@@cW|;?Z%Q&Qc6?1f4%L?ri`U6a@WZ2)IxL>*^sBs50?{{ExR+ zYXpHJ>RMADAXx}b2^#pX(7%LO%7({h>@EAX4WK4)$AAAQakDQMMLNFsHaBVro?0k; z*wS~Ia(x-q`PA+$1s6}M`M&NUz-a+liVBNk@3;^Mm<8~<=b3$_^3jbu0D(s5QxjdXLjiKLC(7*p5M^F8SfJRr*!uv0U%wvKtG2q&=L{Hs`>+Zj;c}dtAkMz zR{-99!$LjZvF1F~X?}i|a%D?iM%fjW4OFI_`5ZqNn)R(~IhGKb%t5wYOM^~E4j-fojCS17mZBYngF&RgRT4qg*DGr4=w zDF@vIkqlQuI>BEw;XrRLXE*ePI=J_SQ0M~w!`*s!*6*sAyaX77j;?MxK%@lP5*Q1l z0{RIb3h~Xgp;ZE^>U2kKBO?hb^r{3Yg4Pa?DHScJ`d!Rw5yYowgo|1hk8H+|_`Sv3RT;Nqi@eQqZs+o5S)ZDG}GC}zDaztm#c!RM|z^t(= zwEL<9Amu*%UXYl{aJBK&bJvd|?S@HPuzB(`sG_N)7D`!zOMF+A3OFq@CXB@>{8o?KTyUkL2qaX{n) z92oXZrzeK%X$u#}=wkt$D)Z~>sQ^-mGT<~_pW+19*k!+xV@b4rlKJ6{;uCSjCp-bX zQ~cB{wtP?0DSf_?OFoc&AnQ%8DT#%dEK5wRVVJD?(bnZS%hkIzZz`iof8!7VYx!i{ z)UmEJUe(~H3!blmpWDFaD$eGagTz6<&>^$Q#8S9S5&!;2iJHo)`B}OJGo`}!veJ>q z24Lplrfp9Gk;}m&ZRL;8#`{&)&+R18ZsoPLn%g9>WS`Ur$`)nt`DD}ESW=?bjQvHaoYRK+B{gB) z5R0XUL9Wj_Pn*CBr{~hFM`hqsmD&A9=c%Vc%>S7XFtw!ja#56=%1abm>+=wfsAaqo})gf{#{m&X7cm8($i(HYYkaQ08X` z;0?GOoL}p{GwH=83u%Eu2>T7mQjFPyXOr2ilV4(DJ14# zbSC~7ZkJsY2^VqA-T&?;E>ocxlQe$nV-n}z%rV>rf*zoD9gAt!Pc`SHV#^;3+yy?e zJbe*wHBR|1)P>kWhe7zEkS4%otCe##uzm^Kcqh;B))>4o!~{;pDwc9|bc{778d6L| zrE%WP+Lcs++$M4SBmow8730pkZ34iWOtN(2K2Q!;a){TJX>9ehy$X`SKJQ={1SfeLL z95;dMnN?>CMY^$s$9G9*!~1J4;IPM)4xy0lvA4dfDI~8EVO`k8As(S347N1z(nRMl z0*bVWd!dO-RZUMeRuX+tpvzX`E^NH?74*mIdlVcAqJ)2~Q$#(*tC*$FK%44US?8M; zcv9<6SrvOp)x}dF+6>IRDm#&I`Gk)ZASU-p>00+V69pMl(=79DX>1x>ehfLmM;HZk zkhwYZEJf2SCJiOi@K93oM@&a&i?-VsIQ_bQM1ljXgeoXZJaXs21BUCVoTz)(-w-kx z8MLJ^$^S_oF4GRljr_G(6imJh!ZrQfikd!0(J<;e?Y5wUq4Q(WxM4}R5!_fhPpdC? ziH;h1Cr`fW>YwK~J)?Oe6&8pMB=L1cbY5nWgtJe+Tnw|} z!lzr~>VGoHojY-NV&NDqy84tMu2*s|+-eI~=I?P7iaoqJHx$l+$Y=@IATb5k`_w9u zHUn$VEz{9U@>dPu{jHjE^98TEB10lgF6J1v*7#}4vXfJ;YWNIxI00FUAT!?A*q!hK zX%z?)29csM7R}oS920q6kW{l{cpDI!Nd!&w&u72*X8sZ21>ex*$!S(`nS^LokEAVr z7s!bGTPsc9yE!LEUA-eVwO^OCD(@**Cw$-pcV2QL0)K4Jt*19%;^0#jrs4o?Dnl9X zjOtn%3tO$kvO~+NgD*bhl%W@{8S_dFT*Q_ox$OqS@?n- z`-5(XRq$m_XCDWVGC-7iUmvo6-EqYM-JYynx%zB^MS~kweLh};^2RN|mrtM{KmW1% zkD<`NyBP|hVAzth`*i8z##`dw^3$Ds-RuSqr|PJ3$NMoJGNBkHjl6i=X9;>DV-@ ztBA2|v4BhX;>kW~a14*f7>&PIuTKYB`tt0f#HBDNw;A85L*q{8QN6N3ON@Q3aLT-u?{#2qBGMtZh&soNY?a}5KZW)g~1Q&WRTwuC*` z%Mfnuii!BA+v``VZp`8FMatH<38nBTHoQ84(!_Be@SZj5RbuvQga?VU7dSr}OfWt^ z?g5%E#1j;Boh_7C06H7!bnZ^vrhxiLN@b zhy|dAX9`A5yd!*oE(a3yGGg)IixTzInuT-`RUF!r)8T}yu_5|HI zEUY5A8uu7!hiLy_z(K>u=4L z4l7jrurk4fd2`*I zT~KuVcEjjIcjuHwzI}jj0aoBvsFeGHI(Y4H>p8HE8tv#-vjTmNQWfggrH+FD-g<7fR9R!ApevoDyTt;aY7&fH?Ok2kP5O3wTY|M8@ouJKV>| z&&1-XMAfQeF_v&$697D9`CJ>jj<)s(AW3II_V$x2I~527@~37aQ!;OHYzWT5)p`UZ zin7n^t9_Ro~KJgpHSzTJF$m-I;)jq8P* z*vPj}_fVP%v|`jv65{JlorghSnz4Dx{Vl{9^lvq+5FmA9NIjh7PGc*4gvf=87? z&V}*2B5*xkET?vk%ndsRFJ>f4e`wF& z)eV^q@f^rP=bf!^G}*o&p!i#?$k=brrhl6-x-9 zL%-O)JP!NM8s4Y6_$71^_~UKWJ>I+g?TpNUX~Thej~UIhl^g_nr^%+~A5Azly$X21 zxoCfv&OQ0gpLr)8%dfVCw}$I`UujKe_wa?-<&FJo8-WdY7{2S(4dzOJG3K7hI`G^> ztIP!_`RUAjzPL%vSf#F#s$ucij52FcJ$L?~pSAJD)9c_d9^WyorsJj_Hf8dN z{aZmi7xL+6Qwfjn3C}Lc1sXra_}9B|*`7Rw(P=eRBdkGNT1swk7M+pwryV)a#)>fa+vU*cGKf=z^z3>(xVBbx#+E~QDhXgBi z)2y!NM1Ex(9MF2o%K6_#iu#==*Ix4Lo6nggHN_N<0$b5H&gn)A-?xqGz+yo0NnOcA z@dcf^p}1>0sdW0uQ6y+J9#u`|rAu{(HMyPBnioDpTa`u0Q4}x@w*LydOcZw>c1LaC zk&g2g8`=C^K+i6IVe*I;iOVN8-h@UXmt0yX)6AgGl3Z=K1#|Yt3G<^VPj5T&qRp59 z*7AjUHEdwSbwWxoFd&?j2}Vi|zWewocmAWI6kg%dII5U0f%XO$N_AAL=I#RHE_yO8 zQtxMfK7?&c^Jmaj{QQE3bUMkl%)4b)p}?B9G*snWdk%v1SU8IKgZ7`1i~jG0Nm(BI zc~L@GZ>h>cms>xtBx)%sn`#@-x-d=4)W>f|(s1AlD#a8R+g51Dj+huM46(X3eeXKY z8M&DJ2)k}Cu+hW$zrR%Bf#XYUZHxt@k6A&laOU|f#nnM z3kVGf7842?RRZV80of&IObddG`B|OVp07DBXDbzxT?!E&;6ZR)m zMahjmsyj^eDVr*ay4&@A7U5Z~^;O@*CiVV2&oKr(Z~K3FUNVZh1lDw-U8SQlMdu%R ze6smbfvqM{Yx+uN#(r9{KE3QlZGP(qPMthyQ6w3f@ysbz2{&XPn z8}t(`$;J^hSV~ZzZZFy$H_hrjtqtj7^R*v)HkQnPGtyz+h5v`wVvkb6$7b`7bpq%> zoE7a~BW|LhZ z;lNQyvF981&L?Q-jz7_)0cM0J_zOO6OsCwkH@Rs9I?;)r4%mW}KlgjN^P<-LZB8vd z0un!)s5Sc`@}W5}E`Gkl%ZvWbP}YC$D3f4htd??9Z?WDxI^R=X&0HQoq!tP;a9Igv zt#4?4Vq%;hBHbaOjyemCJ<*ZB^e=}=k$h$b@ah6Kfwq^>ZS z9G9tIS5*VQsIKJT5Zid)<;nCf3*!v?F}d1Nv#|Kq*4gm~&tI?A)4yQ~e+#iPrzrN#&`iaKW(DX-xGe=)jBS*6oS+^KP>G4W4rxcUf! zjxS80RZk3?dyt(AziNN2V0}Y*P4lV*9ju5}f?QnwIz`x{clCq5B680y;^+KQiA?i) z4`)gYueoDdn{A_Lk{iv3ZS!|zXfhmc=0zVC|CYkI@>iism2l7q-cw%kzW044JU+YS zI^oeBg=<_FlSZj;NS~@|daoZAgevqwrx&KpbD*<&m))Ve29dKxbrr0OG6ViH* zU@JvTy9$NW<2YDA*eLHFr6x{YcYp|2RGqRR2_Jw);~-5{j(Oai6@rrGUmxBu(3uwf zGB4F%A^N3x4SDq7(PAt90&BqcX*bD1N2sgyjv6pI^QprG@EqEO*10OW*_^Y{_Zrd> z(9P%+Wc&}!71-^I5U=rcJ=JSl;e=yL6GYq*O{CK%ssaBvlb5WGaq0`;zKzpOpfr>C zFF{k~cMR{6+aH>od=z}y|*lmFrLIDlSw#SW<9W8Lp3nKkyXFa$cc_CUDR85!xPGHRoK9( zzjkn{0#LcC+XEZ<;`LU2$Hcl>;FMAHp4}LYn}{s&<0e@2ssGjLf?%YeCM5KWrQ>*5 zJpp~dNp@MN&z9}(J6@F3w?{3{*}$gPaVT*MBwfLUi&^C-=PGqFXU69WJ>8jRz$PjM z5@N?WCn38152xwNW&J{@M^+HyU*_DloH#^lf8YsvXNl&*Eqm~e=jP6+12v#yBCZdK z#_0AF{Z=w|C;Vep*D}^<4PC3;oZiy?&%jC}Z2nAVwm#bn_3ks}Ov&p_$gtizZosGl z6O(2768UoB3$ePG=MT!vyb&C$FOd_b)BPh^CSr~J{E{rfx%`JMU z>ytV?^E>PL40}4FrT*QYP_#(WKvpb~O)5N|dNsBN4HX~*d!t^Kl4h5ZYMPld* z(|1av{l;Kuo_7hkGi|j*Ie51f)SUgWd?}xzCw`K~RWC|(f@m+XY4eD4JO~jJv0GCy zF|%~|)yvoex*z-aM@jw9=x;Z%fj(7HuKz{m{m$JcIgPgDF=FU*3rK8r6x^r#l(u|#J?fk& z0?dU&2oOtygn^pZx!RMl(aTEEkJ);^Oc1@7V}AJkzW0c*pB5Odll8Jb^PC3tJ=5aw zwR?Wqfia7>bLff>dHcDQFtl-lxYwmo!(re2>!UUYYQ#MTMC5os06krVTpAivAJ%s_ zz6pyrB@a0A|AbS6W_VuMS(P}1X-N>Q_6l8cwh_dR-mXhvPfSF+eO$6@XNjr#yqnfkebvrzII`2$!%?E-Z{&6u5U(AUvBx z(f$lx6BrQ~Ii~(TvwjDWffDLS%AJ}_LsYHV1Hs9Y({j$M)>Rh4(k8-zW4blUA9w5+ z4A+o+_Hg4X*wkVv06R9Oh1EQ1aRA!OZ(um*2sXuL=F;b?8Nd zwcz}?-YWSRhOQKHd?3}F;P6V&o7p;K$kv?C%h}U?fK%P{VQNa%XA!+RR1ryy@p_kC z7^rlW5sn1{V)lW1Q zsSP2*yYOY!z1LmEs&-bIHY?~iX9tbwy#}5mPtp>8K9h#LzV1#moWcHf7;$ORC5L)P z4Uk##E5}82iJuJoZq=#gSXg9`Sb(uibH#ZmK9*CFY?*zWz$U?x2E;E}5k_X;EtnZ# zofiSc#A;&gFV#gc)djNlYp@!~0j`KQ@&4sc$i5P;eB?_}T!85+BX#2`GEx4d^a`CF z7m@;3i#YxfT|0<#W5l5S+qrc@BLrit4Gp&q=~v>h-7jN`b{R0IOfFLl?tX6FWPHJa zWZepN*VDPbe);k>E|i91TN?LrCDKr^?0%WHj+ z$JmW?kyp!hes%{i6OAMz_TQ;WR}wS7o7CHN>k*0XqtIc3(7R0whD3`@pNOjdpao-ZJh)LR9Z_fW{VqsD!>+{A&3X&;x z*=$e%(CMUahWflFX=-VigBy{R?jjqfmi}$=nN5ZW3-_mw#`1CLMeR?|RDnD(p&uDU zZEYyke0-)gi~v)Qj()dN1QevyS+Xd?m!+qR(sYuDnWi>K43MvWOV=qU!A=PxuIzs2 z0$I9GchbDTbM|zGkrOx(j@926oEohn-U?r3lCmH+KQo4rRQ4S@e@r)G(Yz~oun$=3 zHh4?R)~g4+%afj0kOp;Lq3ropBs;tdM@S4=1AbN4W{ZWVNfWi5P25CSz@yS9>IiVt zGu4`SmpMA8S#+y3L<~PWc!5o}{t+=UPIiKAOI19@Ye^bL z>A5q6NzN$a*&Qg3D;TBcKhyGj16PpyeRs$gteio>_8pvdzE@&r^|zfH zSVx)ldLu3T^C&09PYsMwfw}tfXjEc|76~$5TT62~n16?5dSua_05<6bxM5{DG$<5- z9*&j>y{M^EByle{vq0ewIoCy#gA1Tme;eh5o$HM$!iOV{&JWIVfbeW<^&!gzRwqO@ zqYo)IU66jc1)L#qHa-hTtXl!-Xd*!qdZ61FXm?&4E~~)uNbylM5xl`_l^SfAV~4Yq zU`vK2&-hBbKQ}D{yFg#AUC;;~YH+mA3)<620bQ8H1_;1k1zfX?Mo^38Y}WXTO> zt){su1ZP{ys^WSEUhM7>fMXIiMUerMV5k!sXnvp|4_xfd%@k&gJxK@+Tsp+hJ7{$c zuL*0hD|R@QCM(?x<1Ktnm^_5XW(yyB8Bn)wi14uF%u!cqh+Ob_n6oKifJ2O%wOEko zqpyx;uyUawvy2nIiZiSOo!kj%m@hanq2E4f?i?U%NmK%yEWpU-(8iVD&CJuwnJp)H z5I67V-Dzf9ERN>{G;<#TZg4tB#lY87P{4H3d=S+p)B_KZf#OT&lQ1Bhd;st_A-K@I zppIej({;n=IdvDwEtsi*UB4tV zcd4SyZC308?Gc-FUp z9dVy?aLi`IHxDY3~&|QzNT2(YN~)$eb;_h$iw9;!JSN z{aotJz|B9!@-g7H2}nFa;_`S(;U{^vg^Fnw7%=+S&prX=i4we+1rK^vaqZIVgi>L4 zygXYtmJhO(wXLn!a6CbaG48UiIZAwVrpYDwKl={3t`pem-#9uKS_ABJ2eXn;PJ?qP zk=4DxYs$2bPn&l;;=?aN9{e~xUfOA`cOzSeH4{l5o=^=`n-y1c1Q?h$mA}pK@hSp; zqS5mi?T3K5CpVfrE3c?zdsh8&Z|C)dWeE6h6|2bXE#nC;UKCX5EM!?9!K{xvYr50X z<*8c^b$bi1@+VuK(Te+`eJ_m?Ccb!?&cL=&HMi3;Sb7(~CvnrdJhu$8{l^xE!%0#* z0rn=N6_`^_!_{pK$7L}<%@^%-pIEGnD8zrM*TENz4X<0@sw6D*Qqke3mF=>zAko`q zPsHhdKqBGQJWVZ@qzb8R^Qf7pSZGZ}m{=8JNQEzcHo>Gxlf$MP5j%T9BV+@Jl>0Kx zibsnwsZMSIIo%#BBw-JG;f@a|A>$n@Wfl{hTxzzW<;l#sM2f9{jRoZ0fXT#U+Ns$x zH4R`|Cy_r=*ryfSv$un~1kb4;lL?O(r0}e;7`HX@6qMxScdpG7>m}7N68~ znR_7N$nq4x$7F*~eN^%WeITzdx2msN2TRz;oB~SvPPePj1`*`vKUX9_DQ8Pm zPGQn`N|@xLzcn?kwnA)aoO4I-(*c*@t3K-?FA=q2s<_S1dj!W_(&k#MnDI_MO&hC? zLuATwP?C1JK7r!?t|OC^M&HRZ=Jbrjgh6NIHKb7%!gH!si&izZ`ZgbL85ZBg)n171 z!+mrCCwG2n!Y8NqJz!=mhEn2nYy;6Q-DtXN0#^-A9`T*8xy{!kUw^E=3BxR)qIxL* zXwg&ckz9IOD{q>IYJ%DE#gahnC)9OG9QOHvjwY<0xn0ok(}o%iX_Ccm(Ty~=%n0kFS5cPHPLTeNto!30M>Z-bdLU^d7=#GN6b z`C9}OP$P2Kc3`(i;!%nia>H6#KWlcIfk76w>i^tv0UzP3r7eVM5z7)MGv#foE~Tjx zc>K=hN<7U7zpv~3&ON7zlRmlJh_bRrypvx_2~}oN5)OjMM*a?7ZbUdaJN|HjkkD4~ z_`_fPYnLi$8<->*lpOY*rq%0zl$1!DT;A={uZt%lfld z#dX5Z8+2Q3Nu4dGsZ?kpE;Z8(s);hx`q)GgRCzX-1^PhiHbAqq$~(gw&wKf0i?gL} zoDH`kX0k3PW|96?g%p!ar{DXB0BQTs>x2YLHj&s`I=uOQ5M?M+BFzhNVBz)(vav)O zRbRjMYAm>t(?Ft8Aq}*h`kq0)VnEDD4D_H82m#~{2~Sv0mo0kCDnFN7H9K#a8*Rlu z&-J{rM?j-e7Hg0?Q-^4w&;Qp1HU=U?x7|UhnY_! zZH{&nlLptE9-N%)jndT=E?;wP?Z;(WKRY&eiLj;rPrKV4+S!wH0q02lblxI?!) za3ZGskx`PHq%MG*DL(9-*F;76-76D3)`GLDHR?k~HY?>b~L|xdi2l=FvUPm@o z#Q@SS;;Yb@s&5FJJ%ko(gvUcFHbHKT;$6QbV8%}D#~1W=6p4bHb|zQ3q*bH)+R&ei z5t8zLsVo7TYC}y=REKleM4IYO1HWec`&vE{8H<_f8i0W;G_z3B(42*stk{7!WS} z6(qE*j(aX$`4dK0hKXzPsOwU8XInUWTQZLGJ)G&AT?p(x4C+*`JR~KSBd2{2WAtmf zO_v2m^$bL@NuRQo8J()93*SI=t%t2Fr}y#)qs7J))itA&NMRzon?*xxIrII7pQ^=s_HPH5%_)M>cTF`fBFo_WSw^YRs(X%3dTzS6;XPu5;l#z8h=@) z-nWja_wl%#dqjEM7NQY!2b8A|kmShp8-vN^*(CRa@f8Ev;y+zmS@s!OgTYP-oP|V6^>&Vb?^B|@5 z10|enmN4sRD$e$`K{1fu~sWS7eLg*2&~TT70vJX zHJ31)HWZ^UTeF>B4!8XMRb5}qwMu>tKSYzcYZW*(HG{Y~m`uQS zhL1Wked6h_yDLa+ktV_*sX`pSx8~H(;O@UV(&j}RI%EeV>Y)p9TLx+2WtCy;QZWEp z^+y=NEDPo4{RwBe*56*(OUNez3D%v zK0Z8g2%st|%BOQ~z86CQJ zqpxGX{yK?jBJ)H_jI?An(CX>z*8K`!$u)Q`lK)!GIlRp0Cfsb)`MRz_tWlb!dh=Ny zI2s=n%Uj@S?3$x|I9!{)bD2|3c{}tSahc5E{g#?~?WU5W^@Llu=}tC|2&=*Mo<5i7 z@6WqTaA@{gD9ndv@*WUey>+PkEy!sDsr5s6)8$i<;971yp8e45sxlhc%AF?8)=bya zvDUm1*LPf6V~MvAO(57RKGknTU#|fxAz>~fez@rKwELkxB=j9O?z6osp7*l!MENly z)l{-D8%XFnb?D>Z-oyFzsnCfld_mqri%U&EsthcFit$0GoX?HwMHRi+jvToim{OR0 zR*agEeRFov^SWDw_eTwHtB8@eM={iF|AxnXIa2hgWXLVn<JJ5lSRPGI?djMw{t@yDF%{&>rI>0ab@xue#)F4gX4gGF^A|%7Q8DYW_~R{$>sn(GyIYPsvj6M)4+mT(umNzA_aM+!>$BxRgBHbsY9<4u`SE*%S#(+y~&o8F5&P|+YSbnj=@B>tgwD@Gb7t9 zj3@#@LX(WIxHHQ}TJgTzKP{w1fH2rxOM=4(z~72+^(Ff8m0v}i$BFBs?RcJj%zGxI;H zfh*EoZ~T7HE)ba-&pwR4bmIBeJvt6pkzRX5Re>`(57;yzOo^!~-6!Fm7d8^6bDO*BQhj&{>;07~z z2GS+>f9U$^xG1|VT#*z6L=cb>5HScTfuW=o5b0F9B!`l2X^a6;M5IGnVCV)%Qb0O} z97?)t=(!ue^QrfqbNTTn?DyUKU2E-C&$BiJ`AP4bxCkcRkKh3X5!NyX_~NVBJ6qR+ zi~fpvap!{UVV#Buio4em6%moSDh)4G8Mm$-fSk$!+x3ZMj`X_Cta;R+pIf+LR(}|I zlulI>3A95P52F6jb6HGpepSTjA?)Osbgz)I(m@?2j>oKdXT^51D6eq!QeEqrFaSVs zph-dJSuQQDt`4~*A>U$}X_I&E8_Ms9g}p@X)?Q=_&ooG(82{iyeI46Ek@uapx-7;# zLipP&%)|}t3o+7X>Xxtfw>)UMlfg}g8N4Y9k7Si!xs1NsA_nLlw2Fbl?84UUu(dl8@3 z=P6S!-Or!TKVs}O@1hWpiV%ouXL?nVrbU!b+U15w7W}5*UAHf!3I@7i7;5#D z)Cs|2HVJ!cU%Klt&OgrLXkXT#dficVw}7`n$sMiV4-~l4Z>Dpdzvh47e0auRDpMxu!Ryp1njVe=>Jb?>bNseN*{qzW@FdX(AmDe_KXzUiS}z+?*CDP7fE)RobxBB?s=pubkIFyG ztTO$RS>3qtJG0uy^B%OZ)EO~)FF(zHJfsi+Pd-=>1VPIGaW-R=>XfSz4U=eDZ^YeHjHtFd5P~Lq%F8cxd0F(I(9$Z1S@DwP%#D zWZqFK_LkB(3~k1Ha25Mg5XMmFfa=(3zXj4L?XZ={C;v3iP|kw^@L%(GXlR^;)9&Fi zaG%GF#SIM!E?NFRiE0sdzwGPnF$#Ve<*sQaqI4n)qMn<706bWnhUY=lTXfG5n|o1K zo}bRR zLr8G-K}7(mf(w2$gQ0zGlHZf(q2jgFZ(VK`WfxNPUxD2fFO;lrwsc^Si_jPOsbw^Vb z%`L-&lDjQD>88C_8lBcK&`L@?oKb$7kkTZw$LwVcbW3GOQT06JFXG z!-8_R7*U{Csu11;D%5#8c@mNYG8-1F7LDuo`iyXg0?{I6$Xfs8m2?W`T^j|}XttH- zpk{JK^)W8<6&1s8cgYDL-S^WfT&cFBTcFgs2%Jrkrm#Z&D~0FIGi#fPMjtWyDzJZ) z-X7Lw%b|yXbYGS$mCHd{zv^!lH27z67*Fj_Igza@VOn#az?>BQJ34EiE z@S9k!;|9Vz{xn_z8_uPG=!sHAM1~n9T%>Y*{2;~_p_C|~?TM68?+k=TI+^$*84sJ5 zY(4|Qmmx4nBWNk`Y8kcq?4u-T=erV<^@E}&9CmCjd!!dd{dyA_l3fgxfHsm|?o&)w zSOtX(uL{+%wvBL3ZZtZhsq0B}Rz?Uuv!tjx-?e$(gpmamM$c^ZYfV2X3|f8Q_fV)h({cvM02R}N zR|Aftpt`A)w3#?j?}oA4%8sIg3I0_oA-*ncKel0_qw*qWfA_*76g^&5+DSYq!G8X zd~n38cMNuC%ZB>fDC2e(NNb=|_|}fCt3Rr;HA#+>NO9fjua>WJX}j$%+n?K>I49Q5 z9zFJ<9HeoASy{yPzIu7a86g}Cn;@xglBX+Qy*z#XB48G+zZvF228wCIe==W8ZAh7- zNa~_<6(mA*%oNY7^++z>v2*ySFjy z-*td&{z5*tw4Kb0{?cL-#7IP}XX07t900ZrBFYlg>W`!EWB$8MRxi<5^ib;)l}NU zR68oV`3VW1@Zk56TzT(IO6)uh*$@kGVKEpnb*m!GOx1C>`b;2o{>@g!^F{qr*O&le z^?O&j@vm4L!$gW(3|%YgWZ>fa{#fCU$YzdZ&!Yk_TgL2i$prZI^=pX*b_SyjT*8q*)kaTHNQ@f>${qbT}!Iy_$nYL|HNl}r=W-??1L@tkfGiq53 zc=&jy>r+^`xsw8~rvwf8`t}#VwI85U>zW`w(38P2dScRjsqZ4_+R7eE5PX^|5>t2$ zy-yzBpE1u_P*WFw`b^#f+05%E`^)wV&uFg(B)9X6 zQa4(>2zLxJA;OsdNVKqJmW55$R!Nb9>)}N~Jx?dsJx)bx*mZ}erRE9w0)g`+qq~q5 zG1(j2o8BS(j9`*g4zn;z;QmSbYRZN8mnuXjYCoP{biZRWzsL1XyDW>M%Zt} zg?l?-o0(3b+Y$N1de7WvD~&+7JJ7y1G;I-dA%>2)Pn=&5)_-KQ#P-I3d$fS4-wk-6 zK0)N>H*twQ{6=}KnLsO!-af43ATS&y@X9tItL*J!A7a!+w67M-8{6rM=o3J4_>g&7_aV##L5Ym>ri8_E@Pn`HQuHKCO z;7)g%$VE^cGfTLmfQTdmVd-Cev#Mm!O9r2B^w}ah`q6LG#GhN`2a;NTd)?zyJH(i; zp4Sp#_)!55LP_0ZP%tvJ`g$>Q zV~j^JlRtyf>E-g7Va#DyxDtHbW+o$VaF0_v-yk=W;~S6J{;fc`S=v(oHruL@1dS~j z3#AnE5`s+SkIlsryAX5CD9|(|6*2vYPX~8eEQZ@7C-MA69F~9{d0CEdyzHx+`s+x6@R+#3%Pn8~_(?7Ti-T3wSo6=Gl@x3P6M{ZL7M z?aiQ{jhNurD0!dir*BhkWV_L#hp|4LoM|%+6aYS&X6G9Au5ArWu>-;oyGLnjbr-eq z?IQ5xW%cy~2R=n*#|X1&{(zgAp!F2QKfvv4ge2W|(s^Ap#UAqfwBkh%C1gC;*L@wn zIJxjb@3j{%dUorSyvkuyt<>eH^K-)5%SMY=UDQ?QDc-z8)fG#*NtxoopN2bqU+m+4 zXMED!h@@Oft>=Hbx3-6vW4bNeM)zs%)@LzhvB7DC1AcKwWZ%vsy*i5iP` z(%5A;`gKjN!mR+#JGhvb^|aO#)}DcAor*S@E6!A;Xp+ z(r5P3W%a99~R+fp)zTrW5bE$JBt0&|w5q8A? zm&+&ZbpfhiG(}e%8K(DUwpA8Uh>=^F2M#>Mrn#8sQZBtm^ z)w&s)^Yd7)HP(Kz-*UvN1NcPJFEgoPBK(=D`p$yNWM(cy`Culq@L;{>9zrX>UE>Xu z*3Th4R;yG6J|-Z&JHn&p*4e)LEi82gnVtKYlDtz>ot#TWzpTzA@BMTY_w++fr4s3p1%Q5a#<8`HFf zs_M(|*{@-*&DsLcPD7q8N5k(YY~CDl&u0ne_rq&fCB9vO8CB)9!*0wG) z`Oz7mXmn8OtaFt{-fE8eFX+!16@Y%)9kocyq{%E2n%fm}C4N;+jZXBiuY{pqQ{Qm? z=^(dJS;EGl43fY@JAIPnM4^YB6+H58HUkNSqSy*SUN^?Fu7_4U-;L5rtSnE`+d6N{ zlNkF*r`6DtkexlbSxW4GJedUqVz-Iu^sd)9z?->m>Jq-oQ+5;10j8e_*B~(+q;TJZ z?b|I8>PH<;%)AVQlMgi!LcYK9xI+R>{6y;)@2pqxE404}PMVNjp{Va=ueVOC(hrTZ zLq<>^9@J;7;4ItI%3B>yKV|z%xK!>U2`)nHg2p-3XI*abG9qPuu&&nwta-k@d;6ef zV>H;wVUV_I!&(_AfPf3I$}@KxJjDIfySC`;fz$BTx>2UA)kfYNC^U%dvcA0HSQeeo1>$jKwC4Qm6SsvJHSxu@ zJg0#nB04Ba;v^iSb!$i=v&kqhI^ZI7gph-SV`VJeg9bypR+{w-bTz8=4>YXanC1Tk zH-U(IC+wSshKx3XLJ#VhUw)Ssc70`Eb?6BP9NA~GoP6QF9!Mq6*&{F?#EzhWq&mX) zP?m-cIt;N~%S({iRcax3ta>ct=+;4!TS>?NXIB*QCd4>_tUGY5NkY$;c0yGa20IYf z*mJz$h^rpdr3M*gdIMGKQQ!2sK33lYcx~-s_H5;K&>Argp5}*0zW1ZFBJ&mg8RBcO z%=fg5f9R=SgNl3~O_$nVeQzNqpan<^4;QX6XWAcfUzUx89_Gvl(JgaHDu?YKZB8}W zcCnToA=Jw2FX_6jBT!w+$|YU+Aj&EzHE2(TfA`rbMeSXLS{4GcK(ss@2dd3$@M8 z>Mho_s|59wqibu%^7Mi^{mjfJ?TNI@wnr_#h0lnjXAiy+Ko;AK0;#LP=i{Q`D$6=N zB*-m()e8m&Aw@O50P*B=O4~cuP86{`p_gVQXRCz$!g4X$_wiIK^d0eP2=i>Lk$S=(J?_fz5qTj!5q32qX=# zFG#X5enzJ@#$I6|e|F3v0&QJbMBDd4^u4Iw{^?=XD}QXQ$`X>G13K2q*Z<5KM(3%^ck1eugY7QY<5Il{sTR<1V!}@ zzA=deGLJ9L9U#SI_F4_Sx*}wqUk6Vldw%x0&;ufqPB(1( z=5k_OMt@h*&V$Jtm=ARucCe##T~O7bet!`fhS&F+uWaJtF||mO=nF3X~grkF~c|87q>@_H^>u9En%%^QH&=>60g>f$=RXo#uXYL7Yme$Wb_ahCFXI4Z5;E zKmv{3z6?_V7e}uV^CgEfVCX$UbgwPUy4NWuN!+lYGcmWXO^I74J{En)w|t*|#!des zx`UId25ZHt`>G4uJdyy=6N*fU6!+QFL;iXsJ#6e(J7+){6@GId!O*934>-x$|I4?u%@796OqSgchYdZ&0O6Ocm2azX`AaGfO^!8yDUmp zHR&2GW5ItO*nMFLb!|E3J-zF2n9}h~CRdJWX`zv6e}3hlIGMa=jW#80lJ_e}F9-Ja z_LU@AJ~lbCmFS0gvK(KLmM4D8D*p}WmOc$6weKeo#ze$hs@agOm!C$exc(Jnsy{Ljdr_+eFai2pwbbif>B#CF?x zOPk(C#qa6%m)(d^HOh4gP|p_lNgXm}#5SDjZe>j?}W=d5Q^ z;Ge%AcRbEW1YMktv&C4{GlLdAfXub}2!Gxm@+sWC?5+ov{ zq7(mF4m933lh8a{*7g3EVd}H_ zv#yX)<+^jZE4$lISi;Ri=%S)4{#h100;v-CFS)|ar9as}9D0WsO5X!l0QZ_P0I(8W z{WRdDY*^dE^7FO)4ExZ;7hWmW5Z2Jx6SIX6CYRLy)3M(D=?`#YU%fUnl1*#@bnNv%Z&n%rm-q^#^dEwT z%g_E;G@}4`4kdsw5!!&w>Vdr6bmr%+CEIJlfMnR7wJsJ?VXjx5ol6lP@T+r{Yeb43{Gu+%T>C6Eo#7PZ1BQl%UUvuWaw zi==bs0&Di}rLkTMFYC)Kh-5)o8vjcR@sjV4Y;6RP7QEPR`Iw>c6u^Zif422cg$Q_> z9_I%y4f6ZsNu=a8@lTC7z$>)CrvN}ktuxHk)xTa(4ff|lAUg;n?CG>MJQAD%>XocU zkF9}JVLYgcils{5r$$nF+3b9{CgEOIq{v)W__BejK)$z{e(jgQE6~};@g(z&m5!tp z(NB{7vojS^9PAHS&mPJ3&m*k{hAss+{Q5v=>>8etUZX5YtD_z~q$Tf6X3BFKGK=4*a_`Tn zeAQq@E@ri{*K}4j#jS+b=q(cq)bv0MzBGwT%)frEA$4VZ+1Br{@nq zia;gJO9&psfyt>Rp1b_C|8jo(z1K0|$XCOINF|pR&9*Q*jdgbhuvM)fM=iOJn z+aX6~$=!d3qbgI47(^u)(dE3j-CyBQ_z+tE@IMCcl9W8n9}gpjRXrdL+9|-s@032| zSYFq=*5Cf|9t$^)RL7GF6&6P&5Be?PpGV12(fO8?JzLZ=dlH#x%V>Ds`#waQg-XN2R-OLwgpD$e-Q#afP|G@vb`}P z!>hx*$)~ZI_P-B40L|$N9}#qfYT26t2zv1^a)J_{jC_KT#vK`(e~n$x1I9l`i&t-a z*9Mr65Ve6jA7ZtrA(cH^eQE%d&-75&LdH6o^nq>|R5gTd&yv)Oj_tQKV)>KSCOF`j zsbJ0KgpY7fg_>@{B3SU=+b|L-^n_NM}HE|=a)V7k7+LuAsm zR>E8keJTvH0fx`?ts02&N}gHvDIA5Ak=Yug0$Nhu^Ek4pJ#-u#MY|kg|9%X$sz0xU zB!RAEYfNW+ejWr_ILD{1{^{HFMM0MICYBLS_3L8_qQTGdRhWmVG%O#Fr}69jyM)vY z_#drTxTDA29d-hfv8`x;S9zwlxwQ!6PQ^dBr~BB@vf3&Ae-VrPypm_DE1jd#Fm8Su zlJ`GN^`(_CCzt+Pqyn7y*GlJWI=@;y+oTU-sj2qPRfAwW(Y`XWCg3JT0*;G=%YSH8 zKBZ2|l;~SFbVoOxhmQr(0rmY~dV!nq_$59BDZyOp&`?n=pM6U=Dz@lu-3&Osw3{O= zC7PDS0k%(Wr^rmRW`WPi`I|220qq}QN}l0Rt(fAxop^oh5{|4-E@1?FsZ|n&bkSqE z#$)-ayULkRTq_Z^4D6;(QIr}Dg0|6H-X!DKYx>p@MEBcGQ2i_Moq^lQf9u1_*Vis< z`b!%Pe(HGF+~H)68YW6!@~l5(hVv_&OI5X!9kbyiWghuZijYqM#3}!ChtddUPSpwu zagGmOJ2GuAX4D8%SIhw0h<99;s63UWpsZC`8Mj9kg}7Qn@&5bY7w_MGP8eYFQ_l$^ z5s<>amap_smN0GQ4Ojt@(Ki@G!nfkerJ5CKP0SWV*Tnc++dltgd;-_pAJf_0Ttku2 zI0#CGYa~QOsl}2!t9D}Tkh{~37_Hlf-gC)HanAam0x;?JHTa@{*_|XByPHIU@l3J- z$N zUo;R4B`-d4CSYKaCI=G9TSp(cs-Ook3%=@_JY_v&=EXAFuJOMQG;q!PG5eU@!~qS7 zkXef{_rfJ(!YKMkAr)6wAT(*-22QCkZHL5+##PnSJ$Bzu*V2v4-{-3|^bZ*Qd!?}w z{Ba#7{fY&xDvaG$ghX)T#@pZH)4NVaUf)lSQfuFLl`dm1+iShJn(p$v5{8-WS6}>F zUjAY+T6jT1Y3Gg;nzus4XB?tOt!ELH@D7|Zd!Q{G7OGaNS(Ff1B&s0L|N9+Y{&lTN z80vaXg@}4KkO;1@=G1IVr&?c5c=NKcta4y&<#UwG!-E?0Oqg=KfpCEbJ)>pCD0GMg zt|0YWSl<3^7wajNnuO=gEaF$Xo$#+2Af|@<<160tI%%mHT{r8=4+=m9^jNI5nLwZSx|PMJGfwW?yG*X-6uI;%FEx8_PdviLl+h@~^r4+u};PejPq@ z>>6^jDVAqR=pB;ZtGYo`aeK!vJOT!7z{XF(>*i>-nSY4gyu#7Yy9pM!8ZrO>emkWK z!4DUpCiGq}&y#V@hLQ7rqazWe^#;5jJcA*vd$&GhjK5ob3XC&8B+(L*x7TZ&{jb8k z?L8yg`;VJl#LStsHfHk@^R4N1=ZgkNB}H-NL4dWTrP};5UH`C?%hJxL?VP<1p?cFd z;J&-{hq+1$a%+^sE!nZhny?TK;|h#so7=t_oSB{dXcbkd&KZ99$B!R*T~bS-IO`w$~HDHOdPR)~wOK(6K z=|H!2;GiHSE4}k{b31d;ET+%(i;$ zrY#)=GVlT?u%D}-S`x6Q>*aS|D5w>a*FVi>A1E(4N%S=Bcv3lH(SdSYqD-pM^Et>J z%A#O4c%LhMHVb)oCh1r~(Zj-ex!9Y@Zv4yB_2n7qA+PZ&m$<_n3$cPTBGQ4apUm(q@{E*Axx$8TEuJgnCKX=hdZn!4-QfLBubXeZs6H8b5ba2eK zW&BjgyepAe`{;PJOyqbqL(xw=pR7rmTzrXfW?)EW={kPW@~bQCE(qg6CREJMo>{Hk zX+W?i+RdV4XNXI$9~mi;?D5)7HKt(DOyFbOcQ{_4oFt?Dnq3+gE^(utiw8>c5X9!2Wt0nTXYxvpUTs%f^UKL8me$em>};?FFWr zImvJ--C!s?DkMd*Zz{y}RnCKuPtP-{SW1m%@~hWNg-wfh-RkQfCN|%AirAd5KUO?B zrWtB^sY-6{<(%5SyD@Pj+(>`tDn!6e&9Gw08XF^UdMGd`7YaMTaQQrBJSu80P?_&a z`k2d4`*Wipsk*J6%y}-e9dN-8+`(=p?VcLe9OmZK)fxDlK+uB)6z9Ji;ihS96ff?R zpK|W4i(hhEF&p#g@;UX@uX6pEJ3~b9$XIr4mKl{L_WXg*DT!h4anleo^(JwD_ zL}Xv8AAoR6s|4q>*PhDoF$GaU&e-G*m<4r4 zp(Tz*0ybrV?=e0{dq*v` zfH?eZu1{P0(7~wBws^q@pbDet^__^#L0!t7BIf{7C-URIXIO1Q4&$u6Jk5{T?GKJg z;|2CrLMbstZ2MwAJ!kx|b8e?XXVlZxOC!541?>;|l1_F3*srEM?)0eq)YF^HI1t_< zESjHrk%YO7|A^;|lu_-=BsPxnwxYUEqK}e@#l-o_N8iu*mVfz64~U0_?%#_EB-|aM1s+5_c}`LeVZ5XMfx|1`8xrun1mExh`|v zdlOy1QV)#{-%{4!EI2I^A3yw9F%yL2a_MyH{l{g~6jQ(T37_ZhyC;ji7k_G(VBFkW z{rrfa9nWdwqpe$=xb{bV0O#||(au|9%h$QcyuPEcBkSIVpX}8EGX8i&7MpGedegM) z_I3q%6r*3CQP)Pi9Jz*EZk0Laya;;IEDFT@i@m%zkT5?VP-Mk?dK#*FMnvtndSOoM4jo!u`f6O*vmj*FQ-R|FVTC(_=ySHl4Ljn zI(Ic(+c>NF?m2#b58%e{sg%9%RZH&=fa?_k(*Am00njOGKWymu)1rikqGJzK{S0V@ zJJ8fkwVQN(4~@mYd#yJKN2taKL%OZ(!gq(T2i1D?v4H9Mpyg495Mko`b-;N&cxXmo z>A+ZJz1F_viiuOo2Z4@ z+-h$)=_1DJQ>8`|{kmvX39+{g5tc9M?m1VK?d@1qK9cD$+$bcwm!XuDj?L@t@BbW9 zFDxS};*bT%TG$1+gnnDZ*6Sj(0kKN=QT<~p%A&#FsuyON!S?b0<(Jg*vF94(KmpGt7mAFFHU#KxNXGdB&vBw9S7 z*B>BvvLH_{3|`pLzO|cVbIfFXOh7w}?BdFJhA`>GaDX zp!r`|kD%bbts&JS?u#*}w+@(e`YPrQU)7`Eo@r6u!5|Aa4}Riag=uJL92QV80H}_G zE&2WZu=-vI;segmVuW`x@!wLHvb&&kL4?26RDOZ+43*j=a7Jr;uS zXUqmZqIO4sd-`B>XX&J*-g`H;^z^yHqYt(`kH=aTHUwH~PQt6fNda3$+rz!y5aAtY zd@g{r-}URxQ}e6Bjc8ilK`RH5Hx=A58|Tv~z8>R~>bydf`jD_KqH=~{t`yoNI;zRLXIm_ zyY(=k3pN!0iNAPx_}ShHhP?}Op`&Ahx46qO!Puu$XSuYykP{w{C>Tc=r`3znKEJjZ zk#=IbR*l8+^WwC%wKb2E96cCHS*G!_y%QnK?|cjf7+g7sf5!mJDt?HLAfdkNJRvF$ z&*=%+z-6r^Ja_IVpI2w@Tt=L|fDLY%;A2wg&PD9;Yp3HOG+?00s7$}*+!6oLaGOmI z*)r=lF<)ywttsHnEZ5se$}{~6OmTWkTPX!E+v|{;T55g#6npI))k9k&zwS{AE3R>g zK%+DZ^0od!nX<+VwZQnmg{)V2-vr4R=rT zRT-nt2Z+|4!n-$f!044eO{>UllN=%Q!oKHtkS2G}LEed~J0HGCQF9QUyZ({iX7FLa zwpswGBD>2Bhnr_X^+J(NP2_dQ{EEZdfQhd;X}5#Kd@QKrkU?kNwCUA{HDj0WzxO*V z1m7eY0h4R{{2y)Wk`j@B^3NAq2NePe*w zJ*C(Au}`QSU-&CeIb~mJ?BGc9Umfa&dI$2=l(i!#D%}#gYaA{BBfwNuJl7N9>nT`01c7!te&aQAI)EWA1%p`fN=ke3_XBTvW@pyeoh7$wM&dwEF?QFC# zOU1X0*jXCi19jySWj_Sd_FV1MI^^C?(AVnSzU5UL7q}$yx{OQH2W_uWa?uqBd&wPG z={9HeK|vu&=6X5S{zrR3&60hRn2*eYUOl!Usg_?kAnnF<09;4mA-~t&<4DZi&z+3o zNO49m3NP8<3Ix`g+Qb+$H2=d*Kz5b{`lWorz$lsniyGJaICcpTwnNXIdpQ+Cm$Rol z|IXf)y+$4P99{5;OlhPw`}kfTDIXE^9S1G!%~hw6p-R!Wp6e%=arOOvo&qH+m(=Qn zI-mho`K*yJE2Kj;Ru_G0U}>l~%=(g;Lhq{xEgRm@7>oIjNI*A{3-Cc=xsplP)O!22 zs8b5O!3pu>m-Y3?pqH{OVo|A3nrw+$EwCHhm@XaB0mD6EWn+xOQs7wzf=i?yJOy%P^{(ZX;&J zKO1yyTVy;g+s-L>EB109s7wefMqd|O}HA#zPG0NG4a zSCQt%Q5G=ufE)EzWbHV!UYS|8-=A@3eEsAry1**;0hXi5M+=JT9%0hK+|(7tjBT5b zSMAOPT=VC6kQa+2FU!4Xba9)A&aY zPwTbDIwf><%O1#n)Tm1xii}p0KbNi!8MaJ=5<%tD3VgWbhz8R?>aQ`<3_UeexIzbu zciDS_<@Y=8!&$T2#xz9iU|Qg}!@#T_;-{v}3yiW1GwW^*0rtMjUZ-`Fe$qP3Qln)1 z%g9q-2+We0qnab1Pnc<;q_6AZY8R9vdX^Z=%emGs=1ys}$m&lSrq4MSX;rG+qJXe4QsC!Es`Q?#HK6erq0` zhE+Ug5fT@lQWlKx*fL(X`$`sR>8IclN-Iltpr?HpsWJP>+-=|7Ji<@n*q;*HXT#&f z=NsD#b|Naba&l`QmTJ1nr(xH;ehkmYFWZ+t^LuZwEQYd;HDF7kF^snr+a9*99a_So z4NF=@8%b^is6P-DUA~HW8|IG(@w9aqe{Hm}v0GdB)3(oo<}>$X>T9muwU#)6tr7UPA>^CgEauY7ebfJm6C>SjaLZSNpu zeAwI&DSj(fp4oxqLLIt<-+DbS+YIGzpI+0uf1eii1RHx8R&Us+QAx#zIsupOz<9!k zj87xREeR(>Y~0^vm|lx2O-a>Ry(YK}Yds;qzrdJQdhKp?MR`UxaZ#VzPZDUjkSn6x zcIsR3DO3Qpu(GmKXYN`r6%4b!0$VtG;HTA{zPJvqhvr0)PP9RXFiou3kJ?TXdE+bp zwc`*XSIM#bHHA<~elrq29bJ)*5tq0d#?pC@hd$f1Q#+iqyk^ZlV4kyj*Q{TrmKoK$ zo>xAxhh9$(BzJN10)Q;yOE8N%d6N4DPpb#f=<)GYMWJUHnku7;4EzzN0{f$aBXq%9 zB<~j$hm8})W0bgox>&ZfXq-O*B=wTXNQobqX;;ywT{C1=A{h@ZhNO?|T9Kw~UV$i^S%A1?cUS}f79L!^`{?i}RiSFaJ+w;?8Xt*qIq z{Z5YsYHf!D;I|%&Z44i!SgwHC@=*q_s_OfW zNEwIG(jkL&TM`0D6*-54-B53L7*VzIm1|ZrYEWwAMM>gTE*(R34rOCvVY4y!XGfP)O(Ee(j`D@E(fGE z&nXwAWLMtezGsp~!*Uaz|7gg(Kz(Q>uSv23hwobJBnS(QK93w zbiW{9R0+g-E6^lN22;^9S^X>^yZ!$5hM6umrW^4gQZdn(EOFX zT{ca2#bbrf2z+}mNbHXdPxJfy;Ny_-zyt>Sftr}VMkRgp3%Xl;H7dj9k+6PRt$}{^ z8q(D|Bb|~`!q=L2z%_@|C6H8LEX&R6B9uxD75;={DX+jdyT20KZnzp(UMvg{*q=L> zxYmy~vF^G$mvl)&mg9#Pj@N8fi9$>)6Cx(P-rPT+TDLGz;RaI*fv>e*vz?Ev^1&Rz zt4AqmU>6zgb)fgALK!_qIpf?PE(rUUcKQCgDzna4uVVc6i?Q=Cn~zjLCE%Cgb>B7V zf=wg3_G(jSN*t=fd(JOlPcf#aHYE%zHz3+M+V`_HO&ef5V;fzNR z77io(E}pexlCkk~=c4HUK9YtI5?3G6lR?K@O~G`_gBCIpmN$bkd~;~3v_w3mF%m!q z7(_<$-nu}S^yvcOi+vXR^o4ABKa(9&QN1JhNc^l_$nNUu3G;OUyRikuzBJoIxR+PF z%$tgmRt&w{Xieku@*OY~sGbW9`^IYV!ueqk2l-|`$ zplOjHIw-F6+GN?Y^lSAv)NEmMq7dowkWcNKrX@lZR*4K5dHqNLy`L;%YXPtR(uzY*5|@M_rg;eL1Fmo z+StfYUCNCf1encH-9!7_xtS2USo3e;{ZH!azH2z0ym{WkzdW+qN;*zCMbItSZ*HEk z*^URXtB^!Q!V>iL%_BtXuP)(n%AU-$no7-odcN)xX{vGNe2*3w9lvcjWr!_feX=yE zHD8mV)XPH2aY{c{5WbSFp6Riko+OQ2%-o4P#fWbhZ9IRK;ldCuHdoOt6e#&fXb|PF z&icg3z_&?s40siMY|N-a2C$`+cQTa=vAgJnRkyK4MliMMcr3|tY1krZyWB?7u^hKv z2N@C&J?X0$_iXgii66IR5!yy4w9&_KJaFwPG8V5u6^%)Mqtc5B5POVo;fWouFZ(!b zcf=LknQNqs1MZmJH%82Lzg%Bqk?B~Sks7jG`CyV1m9&EX&EEc~r|Qya3~VkF*4?F} zk&vo8Tw@Nx{5YTA>r3d_vViMTr^fxI&_=R!Q3x^qkk)97z4vzYgh@XLT2q|xL>g_a z@&YF@4;}FQIr1>c6j#?`iHOap98flca_>QT;qb^snONFc^w9R?dF4U$XpNE$ys>*n zCK?!H=Ul#Y?YH5&2%{8jM!#c6OG6b0;%uWzCRLz^-C^+2+vqj@z`&D@tHtO+C5T>#ZBwdtB9lmErAo~gvp z&_?lLI==#6@56vT+tpq8dQ9By@yi#?tE#GMDP)LmO!yupbk~_&6L7a&u%Wu^bLz+( zdG1`6z&fWpI#*`;-#F>wD20W{7q$TmHSp}LPCkjhI`*Z{JPdDeZi=fS8(VO$*=kg%~ExfxhUJ6CGwr`c*&iV>x@AmC3^k z!z?O-AkZ_-Jn_g_KZ%Wt8~=d|bz7_H=F|9@iV?LRbGP?-J#{C1Lo5*7qYmUGJz+G8 zI2l!QBg-|m#Q1z$HyAmxP8#1USH{(1wOrN&i6vJK#?D!+RL&gZ_g$%&O=C@8*>q%9 z3m2cd$tBk|7b(lID@|%;ZM~Jj<+an(^4ymzU$3%Px7zgj+z+pYBi}(`ruvg{#@giN zFu!Z7!JhVY2hm=F;8ZAoduOfAjkejAp&uaDS-6%zVo6+@mr1V9rbqoK*~ z#P6IZc~*z6TgHX~@~J`~R9rEm7+v{ifnP;`Zfz!&1*$FH7r%3Fn9^?Qy4AVJOane?~-Sx&7WtmdqK^TF=hD?i@Oz9C8hnUbQ^aI>4TvsJK$uWZ z%3zge>Qq?W{&DU|cBW`Gb3_ed_n%$d`Lk(fL<*T`b)AfxTlvsb0h<8+6>L|)r(4>U zxMW^QRr4}WEoSOZ=Zg(|re2FwReR6FA@*b-N-y?4cNB!PN*DryG@lJ;bgfcMPc zOV<=a9rc4#TV_JhE-YL)niviswOQ)7GA|e1yQtpb|0FV9OcfU_4l~Ui%|?^~TZIS<>N$Bq8Gxz26Q`OJ(NyC1MZ_f+n}W zQZ{@9kb7^N8rylS)6|g7633_HetQj}0$?=p=-}8GEaajn5?wbWP-i+aG7_(CzOwUE zUpnnBff}*a&eJi6nVZFS`akO#U&#M6hWqc6w+G;A-Z73%f~tvTiCa72|@D`zs=Z3&$g?uyI`KVk5{tYf2&iu(TpxLF}VQ2y;}nknQ8JS2**}q zKTIIL{Z6yi=)NCz9Ym?yEy^4DybwhT{_U4{ilcoBK)%GpENKaXKF;@3u&~HH+LIXn zuQvolX>rT!D3;q}EH3hF!8dRt8;xc_a87wuDCO{ULi}ErI5xn`y=Ud6xJho8FmwF+-{>=C3*9M zK8E|feLeF$3)}=Gln89AYs(Fn-OpJ`{j<)*@61D5;;(gJ>T0a5D9;Xs?N7KC59CL= z{E~N6A&)Mekum)YCD*kTZ5?y!s$FBkeaDXK4w!Yto&_D_r2YK=C9t~;Fe*Abyv)xd zhlh7=NXq21J-i$Uzd*@J3`ZfD8rlnH852^MI{`;g!#D;E%sxCS9h7xoR9XLT&16C$ z_}vPmf!Q3FVJP7hd)b(#*B>jmHbD{u)w=of%UNDC@p2YB z@TkDgQo?U5+)D+UrK?SO@r{&YOEWtq1RPZibQSFzlN{WL-NX0=oe#+q-xlIsis~_6 z9dSt+(iBEjEKEO^p2uAx{Vzc-!G}2F$fl$;@A>%+tnk1Bu^4|j69muvPwivwsYz+F z?K07=*0hNm)5zqhQS?EbTizguyX^N)rmaR2p)h)%gJ!uG6xt$;wT&VM*n)a;vy!uye^Tt4$9S7X4*|6uF$*DstgU+;Z!Y?4 zU+Jt9v)gnSiekkav>5uGz}M`O^Z)B60C{8M$Ns_cY4-xsDd>WVP52qC(3YFO7XfnJ#s9n$ALkLdS?MlE-l}_C!m(@L_VmLH6 zS5{ZXdrUr4x(xl4QP(AdF zjjKW?ZFKyV+`X2cWh0|l!=6K5khw%JtMmBf%Rx@&?7av{1=8Aa z`oPYGb32un9hs3xy^|HNxSzhK7DYYZ+uuLo*A9*tJDy^JZp*4pA?XUKQ| zJt{_nFje8{4PR8|i&^60<(5$Hx{_Ac56e7k^Zm@mX_y_M)Shoq($tnreOXUj7Km`Q zj5RK|QWKB@=a1j)+wv@(Bu;tG9QA$hxs0MWn)QR(-ftXh>Rb%M$?yQMUp1H%hv!aq zDlN?o7O`jNxc=iAVHx4BtuJgcm|<0R9PS$mhSrbPa;fb-G=#T?EY z)uhmf_df+OsToZ0o{*kk5Hs zNm?>{FE8q!JEsx2BJbIeYYBxU6tqv5(yUrz*OU(Z<)xO86u&p@4}AP97-JEH@yRTT ztn>HG2<4322b%mMcCpartCX(R)ZdZGi#$d+7CbSoUfwI%Zh)R0-JZx}mhm|#6DS23 zJ5F0eIg1J0TLo9akNKM;2wjrte*QP$|F~_2A_KEjse#)SO1&<)(a|~HWbSI&rN`Mz z79O3|UO}w<*aI^2aa8rWtgJSt2|L~?Dc=Inl%9nTqSOI3J1Btx-qGKfbsi6Lpd(#d zD0y0q{E*M{fK1>T=!5PCU%Jc-9g3|g_~D@!Nl6W~j}M1Fv;Nx$`GMjI#QA|)2}ehs zQMEwMQB9GW<$RJ6IUilYlW6BNyC>?8;+rXJ(%ty)I?*{Y(6!Y3b;EAo;b;jcgNh!6(T)M7NA&q2A?u@qfMU``~?TtH#2T?!m!1U21p2yIf^= zaZimrQuMlXV3SJmO-n*IH{76h`WR?KSgkY44qi6#_fj8{9S|gU{us}}jJq3(iWbnE zu4Gt$=pR3Gw^}wUZ7+8)tpv!P#W!qTnzKFY=7xF^%TtzS(^7hMspjuUM4+UZ$k~hQ znfAEtIH*6$*97{*Q}$t^*IP*1&!DQ)RJ+KL9;KhfgSFVcE zyRNGhTMyr@;q9qjPu!igpxHb6ov(e$Z#33#j{Lp*0eNaV){`z9(JFe?J{`|j*4^Db z$Fz3=FWdK^GR>jCY&Z}>#wnYm$LTR>$SM5LhQNlE!IWzQkS4PUT7=)Dk+6_%DMhms&rk0YR@l+0I!!ylUJZe)4#B8(4?oLm*Rqp8}=9U{k-TpXO6g)J4 zBfdk*5VU;agrdRxpnhlkI*qXO&&q)h_-&bf1GhA2pPgBzd`0%=eYq58H!|1Rc5}fb`%~VrLM} zC1^XJt!Zw0DoJYwSLMN4&51UzMwmOC$IbHPHgFiKOct|*y~l+fMV?KhZkD**E5S!x zkhluS2P%Hu(9EsBxDLEvmE(+nDo}JkV6{@;H3aVY$1l$U@`v2P{5vHLY)4<}SD!ae zEbJEJ(8<{&bzY%I+tKFIz=2S1JA1Ys;Ctq>iG0Z703+cJPZnB7=GuUwXE0b^GtJY* zISCWn-%{ujElwylFc7*5MU-$^8Sh@@0*jZsQPn1%`=tnzy*^Y~k~Y1I5ZV zi#gCqo_cC=HIrJQf>omYv#}dJ*Y3+tmB%NKTGX%R^`y(39hl=fd}T}B`MzqzjV|l= zt!#?IdXHD{1Lh9(F`{zyvNlf~Yy~7t4ecczDRW>^9QbyEhd@DS73@=$2WC?uS|WzJ zf-3utEdF+5h_Gs&h@4*D8%gU*-;R+}Uw$jo*34;(TL@+e{E_Rprta{&!P&hubXW(c zu;YjGLu8Xc?x*|U*hH)jH7mBw1a3Z}NYWB+Vr3>A{!ClQM^)h`EG`5#rODiMOdo2# z0n76|qcoslYPuV%?Z7p94BcJ)lJ;r~ngco)9p9g?kStc7eHzLWQ#9+5k@@PViVKc& z$n&P-eTj7Gp00prGMow~=@xXC zV}I@hfq!-IPB2DS-O={6W6dbQzXB2W)mWX(F?BK@UUze^Z$&2Z|93u|6()at#*j$7 zy*L6N3OCnj3ln@kB(IE{bztb@jODh+EhwXc{<{{Rh*1k{GM^3fy(gNo1!Sc^5zmhI@sml%5yOy=W)%)kr|Bt z{lz|oF@jDt%}PLtFiRv#I})k&Bl-4r#N{lP!H=0r0Z|OLhwRgFw{oG^p6z>a&DX=U-$cFQkNdcVE2s`Qz5lWBUx=^WT8X$;F6S>H zr>cUW9%{x7^M*9OutiUodtrF(5B&Edz(~MH&{Yf(&S7a>u3z@?d^KF73O%vz;YSN`EnK9c_Y%4)DFXa0gJ?z4Gx3U%=OPu<@b`%SoDxw-P}%0?t_m=>nBg=H z5cU#XhcnHl%Do2jqK_amI_EpqA2!X$wS759jz8^WVZY(+Q98K(wPBi$CN=*VTd0lC zap(itArCe%li;}zVAr23c^(b65RY6`ui|+LK8~s)BIH^e$37YffLJIjE16UB!PgsZ zzu}Vt4S!%`f#Z>Z1~E~N3^a(N&BV4=W7LH-Q`EP!V97FD9m5E&!ykP6a)3{rh>)>; z9SmpAC~%K_@dJFZZ|93aRXpg;=lsF%axyp{BkaH*;(tEqJ+nt;NQ-<3mg8h@TCMG- z8-8c3tUG_+#O9V~!7t5#ei>q~gh~Mf#YH)zE?5>5#sbAM2p!OxyT*W`JNl>^L=HRP zR|JeYm?z{3Wj_}ksZnA8SGnC7!MfkQfE@h7H}l>)cj_(ZD9Aa+z$zv9{M}2lecZo5 z7s5IFJr%a#Wovg}u$M(%T1B!)8(GDeMX|S*B#yL;ie{yk^p2QG@QIpFE2iVp`}=>- ze7;_BW22=HM?l^xuppvz7@--N-`BAJ+D$vLAk3qE@O3p7yP6~l#$xLR4EDY6LzT!; z!`CK0Mf!qWSBUv;?Q)anA0;gp0#AOG_U;^G-^VGw8u3WLor!GgR@1p;Z0?m_uZ{2O z@3+vK^D(O?#aZ}gUR)oggcB;rimL>c>G;!)?F8@D5%cMv5;i_l)h;~+)6J7Qm-zf& z*LA;!Z1SvGaY?6W#l7M#*X+nN!ROqzRm2-UvA!OJ2~@WHTgpjh)Ax5$30rgbr!%t)30%< z;+44NV1#N#WAr=0ynZ8%D0X8^gq6T%yuWaOgy?bD+3>6gSG8xZM!CKE*cz*}lRiJb zPiKG@doo+MiQMOYwY)FlGIXjh;a`rp={4$Bgzr|Ke3q>`oUKEtsG2HQV9vXcoN(Ny zKuWga&%&<--?BQ_I{Y^K@YgmNwFGp{xNqLoN|#wvDoOTJDy9DHws|z=v@pB8H}Q0% zx{tw4#z<|Jzbt&9)tKa@QZwawNuT4IQ8mG{S+s(u)fn^KN?@>5qkQ<8?pH3B=L{;Y zKTQoLr?j0CT*6UO^@$2Cio#dzHw)t>&;=nL5;l3ooQ>k`(IlI|Bf_a|!*4 zWuI8`BcnTGHF^o>wPsRW$AU?PwNeRRc?NIV&tZFlAm`$Vi^A&1nBnWT?`vwlxqEsh z_Lm>-?HNLWROfbt#Q!>FU5DS_Rf|B}SMKz=A8;#EZs5u`=2!G#U*Wp7JLei0*p%*b zUc5bv+PJzgF!`{#YZ7a3XXoODF09_-cAI+;mk3hpl{G5~0DPNv3ZRF*QSeHx5&4r& ztX=J>nNDyhl9bi1{yJj*I)sI5i|pPpuu2$VooPx$$y~r(8GN6e96{@s?m93e>-Fd7 zr&ikAdssz0u}A$XbTv^V zMG6|4pra&-Y)I;{xux|UpDz(yJ~FxurF9{4RB1&8g&1k(`~AW6PUffJPL0eO;ep>| z;@JAeQgN>sty~p6#+yzh^Lez`dQ~_9F7NiF=YWe%Z(kpgSN~Fo&;}OD!c^5@Q)d7v zNi;{Q;JOk>6-VR0_H9mH_+9ko(Pm6E)x}6N{^wTf9;VHdupog{_JTPRzJq6wN!3<& z&wgpXCExFd6-^Ql602Qf7T4>NKgKNgCy}!57Z9-c>x~h*{FH661nToS()7DIML^m7 z_BWJy8yx>;RXl-P6C>T-4-yg@1un0_dx(C+>%-kx?AD$le2vY%z?Ped>E-HF=X^BL{T7-9?%GUXvhQ<9i9cD({OzZuFHzDTCSilO=t1|5O+89Rl0D7^J zo-og=52e6DxGWZzI*ozmkkl#wFlP>Lb2BQ59I#?}PkF}IqY?9JuZ?hf3T+!tBiBrv z_mYkB0SCUo=_duM2>Z$yho7Mx(t2!HrTP|Lh?hT9+AO8>a+zK2zIxTeWOnl=<@jZS zCq&qhM73pnxxj?{FCdTGf@d;`%-2Day@zr1qEHgp7r8 zxVSQ~(UEB~kNpVpY%jPb|CC?M{j8Z{E}9SW!$?V6g(5XdeFg7N?R7(BahhGyBJExz z{KwnV6$^zm4FN>Y;ehXnhaG8=e0@LUH( zAKf93be{js?ewHQo6^FL+@6RG_%-I#(_R5EW}E}b?%BR0!Uc&Fp`0PN%B5x93sv(i zuuZ0G3b!5l^Vc^34WrhUs~90PW|im{H0hgwHs)kQiFL?^CWw>m6sNFM+d5_(0p>Z{PZ=xQW&2T-yYO1W$ftK*Jq{yegX7gt ze>1RAMLvF7$Jx<6*;aED`J|_jZs4M-s$uRK9(DkCS(=>v@~?*tpUJh3MV)?)H)?+* zeeG&LIVd3aqDEIqg%Zl?x0O_<7E&ydJN z6atC2jXp3<)isIUGJz+#;zF^*j>ma`&xN?M#-sUjzDWLcZC%r!+VqAE6 z6{JkxbJ=u$KZ$HhB8OH4Y`l%5g5!!f>af~ft?7vcdBN14W+u#DA;UVWdu?}Se8S&+ z>w|oXxNAp{0NVtS_DhjqEC7S}gIX z`4T%=A6|n;$#yo_}~YGl~pE<9o@U9%d)&tB(OG5usvlAWjc%6Kq!IXU_mv zB=8StaB{MWTN>SCcfn|!0IYy5oj$Ns-#H zCv|M3eahJExKBX$YaM!7p9~E>vXfDL5R(B4mOo&TaRnD8rq7}O$>HR`yk{5IY@{H?Op-ZT&CyC?0>RCWJlp!BPyCwvY;KQc z$^+>f2J@r=0YtGzLVDt+Wft1U%<8yxm6wG3+X7uU8z`Bin{0n-p}z#m(`?YBrgVA6PB!iqVIC@=AMb?yx5K%tIWY3cfK~ z2N|p5D0*JWrlWPbt)%0-sj-De8AR&ZKKu)!oT8V;55T;np5BOezM0`R$|_SjUWvYu zCcU|#pt^K?24v4xBReY|J(3Gz4N=rpR1~o>cO!aC4@`!}+x5~MIqS&D-U|g4Y80o+ ziSw%qe)LY3m*+=(-`AVtC69O!Haokhw%FtCRcul(&x-N#0*b|>CF-@&4Wxmzhlj_T zV~7_US$VFC^dCs^t|);0NzJP|MBNt|sc#wK&ChI&t#LBN?*un{Dg?wp`thT|ha>hd z+xs#Ey~Nq_S15&qe&=8-fjgnK-gX(m$-)I|cWwnEj<|byp#%JXTN+Sxh=5#YY#XuR zOIw?%-zwrkldd=o1J>hmMJ|Jq2G2&y$nGt+hn9J8;=1A zyy;!WVq){9E?f0*9OB^sl{vL#YSFC&;~NUG(9tKN?AsMZqhYG+D_ODvkFTYzFK!tN zEM@mpJwMKcEUoa0H|RX!(DYWW-=PyOlfEa_yogCIhHXnsE#7wv?xbw9D zd(_W@hi5J|TT{d@wl8Bzy#^6FzqQ~b1)LVF^FbKtNij!$y!tlLHE z8E#b6&D5L`jwQ+d=Jo6*i~G8$_YQtPU?&wTZN32ce+FDPUOeF%v>zBgcBZHz)9tlt z((-&@J-H!_>PQ}?r^+n_g|~ai-dF*uJgMmpbmK+(qmVh`$f-wV5^Sjc{O`7tvCxj|)dliGo|{!Mpi5`2E8Qtmnm9)0KHbfnOJlL|B!6sRwp znWA}~fBWvfM(1=O#7Y#nKQ zZ0ImXU_W>!$f&s)V49|oU`aj{(aV18aYth z?dwuGH8f|l6@V|*`vXwR9r_E`KXX(_=}#w) zZCT9vMO>X9Xm-)Px@kCjg}Mo}hmohC{hlWD4N~M#$^H20U^b;SHDQdE(1zk;pEyT$ z7gG~AweTQ1iX>Rlx+qZFlTxNlAxs7vwY-~PJ|)$Ti!bSrUJ|=)d)~q$3C~_6Ed@YF zLZj9#&0-1HwUcten6Fw&mx}6S@Zuqo*(rV&wuzc7DGLN+Ge5ukDW>6D{xX%C2W8wl zPU>EL0L5TSWkt1HKDGfnRJ|R4hc%`w$#~GeCL@8+(O|*_%W+(Ex_}lS<_x1UGO5tD zUU9FEJG1ylA7<%M=4K0v5ZtqJDW)_pbo|{|IkNcld@UsM>hC!-4n?7sBh&?p18M7g zYrIT+uJM~c;_4Qucfze}IC4NtiVo;Fj+6pL1FY4AVjK&e{o*o-)mGCS~ww zTUpB3CTaBHFwygpR-Bk5bnTKIO!88}m20e}6@Jo-#`v(Sd^l`WjpdPzLn6M=+{XX+ zs}#z&d2SEw4Y!L%&W0^eSW-VWMgv?r%Cid?cMHZ0)1}x^4gQ;jJ!r1<_B%5XvT^3#m?{Wh$})8#_v0$3HUbicx90o5dnF`N z%14}69jAxjP8&hN02Vu()Y!s>1oY&Fw&c~1-o$n9hqOc|iuIRsaprTp^EK0<*GDC$ z@vcX6%FoWEc;n`d|DU}>Y?IlegKLQei@MVufuuBq;YZw>U=_~XsVmEzC37VYaPy60wm_Vo$dImK`Y$7=&l*d@RX8XTF@PRqCn z=Q8&UfgI9@KXbDak0FxhL-^IgA}_U&sj@`@ z9;v%(l-4^-;?E-n^sHzPE#YGX!Vl64kg~{ouFp$nRh^?ruSe{psSbEHc{?~r+4T44 ze6?k*IMzkHh+FS_M_6z!I;JxDAzcRdkh4c8Bdy>s3)j!Q^qEy~zsdDOSMnyR7f{Qp ztR9uam)^a$YV0AG4BIIKk!spKGcb#mOO$|z+2Ttku6FnT?1~In32&<^A8KNe0~w7E z#7T(oxl~V$P@q!vZxpBL-N%pAamtTcgk>$8R1{XyX(`F;H|{e_#Hb#|>=SxYcMH-f z(j3b4D&@n1f+zu$8f70x&MpnWy?uSu{`P z)0;sUY5)aY)a(2EnZPzskexP3{7k~TUk3>q(&^Jcjit?Ue6dozoKXBn?_E0zur3l+ z(X`mw4y4fNn=_UNh#-y;guPi^5lK6uk}g4TFmSA(#?{Vc5_$|R`cvqxR`=^Q@e6e+ z^AID>GEvK!dZZ6hvnYN^jL@=FL$~D^AZmd7pPie&QcxBBer@P(cFNush%p|>ec&&# z9f2+=mW_Pe6zoUuS6be(9nb?xvbmv;jHh7SVMCfwx|ii-*-gNzeEF3aFK#1*a}Iv0 z`8wzrnNXHz5A4lM<`=WmFo?VtoJtxHrdGd(#vN!r+Mn3wF`tz0hzeKC^EI4&$bygn1Y-UhtCpL?QZqFV8dDl+o=@uOkaj&0)f!9X2HUT?X#7K- zIo->Pj^*gL{jlz9;k{ngWs||!*eCQFGn7XSSfzmA&K~YYXJD%i+rz4+d?5jCQX75Z zeRcolAEC5-$D!^o1V;QTQXuk|LaF2IkwY@LszPb`wV{ZJ!{WZh;Zegn z%Xo`U9t}gufv9ocWw4>O#FA#0hJl#>$;NfS&FlSh(2>N=2i4RNF#oduIPj(Van2Z^ zB&S<=a%hS{0;0v*RuoWyMQz=SR3!0o{_0lOYLD}x<}Qk94OxP^XL=t{pI0;z^2`uI10n~f&Sn{Fn+{fXsl zvZ(P)+U!%JT-&hQr&hR;HPnF61CQ)b(DddpE?KHR7@&dbprgw|IPT0q*NpP76_NH^ zk8`V~9Pt1$|H+%sx4r4)!}4MnFT&CNZnmnf9FQS)tq-XlX>O@j?_qY`C9TQ&)x0dO zI1|lJfN@9i5N|ch!@G~S}=b;P^lkJ&a`f=57Bh`!1&C*8i^V(TuZWWkJ zQ6JWo&sD68it;-uR2ZQbH?`BpW|biaa+vP#!B#Rxt6!%pS%1DpTQCQHDVMo#E}lK5 z2seSUGyn|Rg#)AMS(&cDCOrvrBBNtvvz~A5ncxuju)pho#d|~#!H>*A?y4>A+6G#D zKA$26HuI1sXhX)l7`w(v-kfXx`XjI$B zPd9GYGc`U-1#jR1-GH}){{VNX<9Vs4EHdTP&vB~X@`6%y&59+!g=EppQL7WeJy^#X z(uFf>Sk@>5s#ZjuCs;CwZlAgzscvSVm58@C$_;5$OG8)+!Q!tdDyD^sj=bVRT}euS z^?V0B8kyHt0##6nh|>+B@Z0;cUka{=`9obS<1KtiKOZZP7kJdh4;m?gK6clIKJR;^hH~tq6tut9kyO?g%p;5BKfQ zbKntPQxNlQwt&hOixuDS_3ZM{RcfE?J3rUulKBV{m^+|yq&(t=q(H9jFXDqhhv#jh z`57m{fmwyI)^Hhh)yJi;Ntl~J8{QQ#T26hzBGZFA3?jVnVF~3f;rrdraeyjEFUqig zF`5IUtx!!WWy3DPeR!Y)VWUz(I%7RzWCl+`TZd&TgDs@Iy79m?qq+<14PI6S+LzkQ zGJ!=0;5bjWc@I(!m1_4QJ-pQGJ1=VcP3g2}^ptzAjykiE0pBtT5~(bWEz$jLZWWr% z@#zYD7K~Jr|ube*Bt&%$M(jnyqs>ow* zH2x*C?I~&lA`1hsRNf%WHbe+5n^K+_J5uyT5Q7xvK^Z109Pi80POSZ;Y9KZR33tSC zT@Gd@;Xgp^U7hF$HcFq_)om0S_eFk^9R|ib+*Sid;U5%NR;j*G}(#Q`&B>F?#uoX`g**sL?Z_TtErX+$Gl-ULH6Q(ck6me6oMUfJc!<_rXJA;8tH^`ujEZl82Z~Ky#V9 z8$9ng(pK}l$I4QGw-ih;6G+zd7k0+6qhJG9W29AjvG9HQJd?=&;udT_AeN})?jD04 zUvek0M|o~dSf%6Ly~yz+O$dfS{k$kEHT#pO*?%auNJ?%wAS8k)TebM+Z61KhX+%xj z(g)Fmt~w(iRtfLKFsVHap+t?AeejX!4!XY9f-Og0>_jN3>g59Rtt_f`lgdY1q|#TW zT1Q467!z0btOtS&IOL}IP9XGOquE^m=T0BuJm@j3b0c$@dCE?gG0O^{M9yOCsww#P z0BWZV_BjhTsSf@8B$`zDZsA?Hn9Eq+JP@NWg8V5vlEb))^QbN})SD=d-8BXU`jeSK z^`RF-M;K3&(1z9Soz-N`tK+pwUWI&@7pw3U?RiG;Rr`R@YRAiy_=@AIUwc4yH&W)) z6W^V@-z}V|vpZxbjh(jbQ|%iNxzW3BlgfL*=ZNq%$Z zSQK~PtFCT2h7`mdQP|r^!=C6%NYDhtX>l?`#OMr=Q=!iCABYVK1*w8JCnZy!7Nk(O zjcFf@&Hc1($9hYWbLTb#eN>C1!5c=IJN}0XYIi0S_rpnotRWoNS!IQfBX1FkVkf&ZDQyA*~K!S zUBDpv?~((VWxhVb$?!5E=g(r7PDS$8o`#A#s^XHL)=k?kJ;+`{ckD&;y;W-3y3C@d zHiMDYRC1{1zuo!7A|rzb_<7G3cNGWRd6p{tA?Hz5Fkt4PO1>qJtuX{a#Z=sgW%zHg z5$%~#scA#NcB1)^j~;{I`Y13UOXkZ26@lAMQrH;_eIvs&IKBVudAyYXJ_nO}mm$J6 za#YZ(7?8g0nqJ5?Zj<|i+eVeQatAk9ELR%$RzRZPxG;{RQic>?6-B@-0%y=lTz_TG z4r?Y#YIi}f`{8+a8NyW~#ce8YlB^nifI3Igi*&VwdRXDw-cZebtp8bt%SJ?@+!n0W zqvC(h5u96bT8r#CquiF`kVdmO_TuKJw|*T}{#MT2U*i(M&5X`mozmCboxR=NCT!dX z?J4`5ya!(|q^wcf%xesr) zz_cHz=NdkP|<6-8tESDicWkn&=+#OjAmc@qvk?3e6>l8`+; z_D~j=03efxpU=f5Dl8@3wRrUFTyJ;x1n)e2R<1x->5g5puYIidyP}=yPD#dYZsFcP zyT8>YtggH#p$u;hYomC;b#j-y$*^OB3L31$rc39#2Daref0DzR} zR6mI&RZglOLELgU4m zpMctLQO4*}WpU9#`!%b3Eq-i1MR2*TL%qYp@F9F(-&Ew!M}8!DnxFo>+)R5wFM4Er zQau4p^817&RDOSPWy;+tF)Ks(b#jP~9QBl2licL?P57~*>JG(2$8XJ(|BwSdo5l`d z*gkbMnANBXVN=QRv~otcT$mjxB`cfwEY5NcZ+ZAiGut7cL0;~v>nIc>Pt~aghuWvG z<;a42Ca%fq9-fOyU4EwbRzqO=%if!FCHb5cZgxXYs9a@2S^?xTRozZQZQ~P#I*Qx-DSUvBY{3K|Yc)8Zff8ZZQ~zoL#<2 zlQ9iRhQnUI*g+k@;-}^PSt-%g&7G&Y4_;_5K5Y(8s_W|Dzysw=03*>oBUq{60mET0 zbQMS>mJM`FWv6&|@P)AFfA=Cx`K45cMz+0ZenbixB#Cuu`6(Nh(c{xDTpLQ%?>w0e zC|uFH>>i9J(&d3QF! z(wpc}vORg6RQR?(-+YFV5hN@0muG~0ba1cla)V*1ZKPVI?%(oT-VQIpb}Nt;#Ok-? zmsUOPYS2hCyR&StUB0(Hzi2~f-rEU;oGL3Yq~ro?!{1($|Wcs`;t4Gk;aN{!xbM`bgwp|;a} zL1=xrn|AYFhfIY|UGEJvvfA6ezhs(3;k|U>#R($OU^)7|cArg}P~_r_#P-kbX`&Zr zW!N=94fAmb7{Gg{^kFLg%%VL{j}}#DC9&P>;PraU)$?=p`vnndQMl&gj!DU6XEu@W z$G&`5S7s+^(x6VV_QR%Qy50gVh~$J#|1P{%ie;2)C589VzX}%eIU)`- zJhsD0j>-ao_1*}R!uRYKgE0P?$!r?(U{`D1E0*|Ac2%_DYvl-IC08A}ZcMJueePpd zT}fxnr(xQ_+M6_D|BE-en@g1MEkWg$FkqL0`r1^>5UV@sMg`Xqpr*s~RSn@0{ z_dCzIe7A4v7TGZS`e&DANiz$tzjXnDEr*^h$EP`)6dNBjC0iEY<3HV23o6h> zc&Dvz-Wye1+no)f+TtF<9Cm2cXfbY(pH2joStfqPIK(-3-!*7RHBf})YxoMis_;i{`1L-k#OJ?0ug$Z;?JHE=Id{c}jCucxUS06;=M z2jA(uy0C*-DLk<7PC{&cY0$0cuUbhkA0^_E{MyU38z z!kd}B>Y(_l@59orTG{EEnybL7dv?37`ZZpd>lKFzmIz4xK2YH;L~1SmH!4Qexy z@PJwn4~h^z0N~t+nj18>3;>VRm4b>H(7D^;lULkhW9(S&CO6E@KK~R?tGxEv?L)7j zx$CA8sqka6q-Am1OmayFPfA$?U-ZeORQ3y=i9BC(quI-YubN(Ro;&^q{T)2F7zFu# zl%b)XAYv6*noD!B-vJTD!7bmcul_(aV*E?@>~V?*Q=rst#9eEZUI7Z+70u04!WK~X z(v#?YIY48bmFyZRYCH#p&19c|PkW-0_>gNrZ)cPc%*x3wj?JsGt$d&mR~GSj=uli% zY!9u5RMj~W6O)xJZ~ZfObYawY!O_d>PUjo(f#wS!p%3Ai3eCnvcP2?P5JxgQ_sqbh zQF)P#Red{&6*K5`R9e06M0u9}!!rBpVwvR_e9*t&&hR-_Crf}a3bA`Y_JKOv7E64W zDHgn}Fky30I9KzA;_-_k@1bCIiR}vPBQ&K_Kt1ja7~a{6F*R#%`htjNx9q4-Q^DbR zanyG$>R7FgFMfEb7Jg^Rx_!uFdaa~$)gt?A-sDiiuZ}C@!^n;EkwKq=F_t zYZWao0YrYyDt%xnIHE(x?eo*_H$|<%6GER$Z`br2cGn>tyq?yT2kSOpL={D!hDGg& zbZ{zVAd#e;p+h@H-%Qbf>CL&updT+IGtp2voCWG0CKYh(e~;U+Qac=Q&o^T=`MJ2? zt+Klcah)~ecbDkvrYi{u&2ZiK26u)@$*%R3%aK{uJ>1+rL?isjEF-r%tI{YM!}*AI zw$TYolgmhwszV3CX`_etc+fYbbZKElO@Z6h#z2Zq1&4AGJE%%v5{Hmq&i&nEVC5*tORhw~6DfqNCT;|9N?~8ul|yGc%Ppyb7(cdY_>Q7vJCAJp=13X6EMRfaIXOL&x-8`L4M{slNR+xr9?{svin-ytppDxKSUn zBDhYmYHY%cmfC8?iOMQ^BkW)*>5$qjb$=dkhxYv9UE=#0fGv~d@Ey-hufR%8d0esqQBmcj<8=0b zpx2;Y`cS$Qa;H?&%y^?VR%sm83^_a8LIX!WV{D-^w1JwtK`;QHG>Rp^@94YjHn6oH z(`7Ptemv?PnO_8$*1!pLD78|HkIk!pDMJOyR}HQmEbJ4jObc~Y_KLm06`sEEB@gwz zJ8-pKPGD0iJ^vuI?x1qfnZdAn38kLyF~Ll9(G*}T!BQo5@$RjL_Ysv@u`)%C_Mk)* z4F(n>h> z0pS3{s6jd9vb4bBPmNL5BiG{0N|>W?QRk9jPLB?ihMrM_Yu@u zKrI2Occ&%K8J#EO%z!F(o;w=w4730a#vQzyAOlkvZY7WmL|4|17*{A7d6;6Xo@j1) z>eRu*K&C7M5~4=l;g4gv>$GsLhE z^Gt_?m_Mw4Mt`>N!1ecj;`B(bf;W1g^5uR5y*-#lu~UWmJCh2n1uTbSXAb#KAWO?1 z^bdA9@fvFuIyZ_5j*gC6XVv2F(R|ET_8vBW1q5uiT+A21m|yYCWtB^w0JZorQf0u` z0j>Ncmb40}xl-AI&~gYv-@Y@&BD;t)WM&&R@3%v}>AM#?R`nJB{loCUd%NjFuW7xks4KXyt^xp<&*<`k|)4|>@!OL`;B_+boti$V(^nt!5;%Vc<^zT!s9O0rFIT@al^5dCIUrtmsfv6 zKsf9-^u1=4?{_Cl-pfcumnS&|yE3R4+}C22ENY^WzigQmMC0j|*WZDw0tWNlY55b+ z3z1u>#laHUp^xh-Rnfim-7viiK1JbL80ykvAMt#r-2cOGq7?Ly9HTE=Uc*z&tI{ZK z#%NU+Sk8Iuw?R4Swb5*{3YM$7BK6%sGtr7)2U4`$#CR$oSY=zBF*0&3SIjgbn7M zO}Wz73!p8-ch;?oMFuZSg*kqMY6ZjV|XSH+U4%3x+*8+rIyC z;B?>fQS=&b2Kfy3!W{N{ovwFCs5e_lCSp!CJvWB?Zb0(?YVuNH;S{1ONZ^huc-lnp z*Jw6UxK=a0Tpw8HX#@Cw7lzPqMV4fhY>DC!zw$ih)Gz+K(T@?T2d)c{D7xE9uWvk4 zRdX4mC)odYI>DItUi;U?!M4!*cdq}R`_E{nt&_f6a{=$L6eY~Y;vg4sv_y96zU=9k zYu~~n;5^Fm5i9amufE9rF%R!xQf2Nv(fO=;cTWeZ+(iC-wg1G%;>ZlE>JiuCZkNnS z5vMB`G`J^x`7PVg4F$LKF$KIRAL%qNM!*!grY7-TqO+rXnvR6U~ zWgCnoYj)X}F;v=&P=xH+_ZdTs8H}7P*^O<6!IWhT!$@O@!QAh0zQ5Bsea`p3fB)Q% z`+oc$-~KZ*z4mLrp4YYKShFUf^UrL5fTZ4(epSO4*)jP+6q!PyV~(aW-L3Pey&{(WIwB0tJJA zDBQStXolm=vm&y>n+rduGOO`YY%?mB{`g7Z#`mM_wHQVFp7r=SP5Hy9BXI@Blcwj= zxX(-dImusNmbR& zk)t8sWBz(YM|cCTKYHC8FBcg8^l91$H1pi~5f9qdp1le#zN=cd zDQ!xBTjc1@oi}e9Y!BYmTcroHyU-VJjeBjIQ|E65S&mlphn8p`bk6G< zvoTG+;_o!DF+IeLO^x2wVdJ_KGEmZ^G(Op{)1$hX1G4d2l;{C##$+5;*$ApsZ1CE? ztC9e}4TKpQ(>;t~ZT62PR)jWd;Wl(-SG@eB%;cO#nYW#s;9%UA6n7r{UA?Z@{P2|^K38$FHYTokpY{WR|x})>1W0RJS?h$Kk-wFCHUrjQ1&_E+9?eqLkBb^*5-A%p=X1- zMq(%Lq+C(4sBm{|yuzigw&w18)y#BW?pkwo$zxCgug+o^N0*x$aj7fFJSx$nt741e zMN0?JKyccl7I}faQV^p+V@?{rS8M}dN+*~ztXfFm$NJ>`| zv}bni49^a35rb&dJ(At0FoOt~rT0|+w3|R@7%_0(6hT3cJg>D%&HAJTnStct67JDf zZn8JsOsb1xN)S-PCeo(-;`JY{~HHmcjwbav5510$|YsM8fs5- zUGx}XHP95T9J3oOJ+yW`wWhc7Zv@N2@}I_iYMiW-#l?Rp4Z>yaP7yl?y<(M@S=+5=(fnM8 zizj?N!`RyO3#RWCzgip7`XwNW8eCg>>oJ37!n4CA;bqOwvrEp)1opSb9_&OLlkC)| z(_!u!d9^SZW|c~VpJ2leA2*{%B0Z7VP0r*Jt+-vfXo~+KK}A;|w&|`kzQ-k2nT5=J&ZqSX)^0iWvvTDmJxhb=RsZGy+{i{sW^eZ{Q zRY%!k(G;N*rV)j06r`BI=rrF`pG>cu8>7_=wJD86oj&S9Al1bE-Ofq8l4it<*qEHg zO9|3#jvgca(h(Lky6*HR4c+>QJmW-!<$_9sG+Qkwelr5&;tHFKCtwC&dB(mCrG4iS zN^b2+Q6UlG*+f&9aue=Yq9f+YZF-~MqWRUu=bImb)Oq+*9)gL%f!`i_Ss`nhpJNM> zz*XY>JjYj-nqgd4=-hnoV0=L5zgBSht_ zq4o8_Hd0Ab3r9VSPtA!HIvlC=V+H3st~0ZJmln1n>L~3;(n-zTC%c-n;{JH+!EBHp z3wZb`Ut($9F8&2|Dq2a{Rrb_rAjDQoJ` zI0S-`;zzy46Gl^IWZ=TT-1QZ2n(|4=QjGOah*QD)cc`zfMN9vBre)3_wK!h#Jv@1h z@=?UbD(~ zfjMm*ReJUKl1?+EYcv}A9;}!M>;5`*N+Bdu=!*MWbn(O4?+N!>B~_@$nATaV3DT1n zLYQv;S9gtZ}rW6=kob9(b*Z}-R40EkvoMfw&d{qDHlvsA!@4nA2?30!}bBAy-xa4zSB6Etd3E}<8lPrrpaGZ5y_EKFKqAv~E*cxrf8 z0#Izqjofit)g~_r<{nrphk>ahKp>7eSnEi3X7tLUXMyPXUo4o47&On|WLM{PkG9YN zx9AaA^wiVID#wqtpLBWo+x%w7TgBz(AmYUi>xWJmegNPBP}2_~UF+G%Ox66Kn^&zW zBcT#!Ns9XZjAvNu`ha>gEQheUrK^Feoa=A+_O8n1B_Py~VpL*k;l{-#?#>S}dN1Hr znlopcHMsyC;T1U=w|GP2mJd38zx}5c4qv?i+5M`u9>x`f-rUjV?mQhBm)_}d3^b|G zEfz~&{3EeU=}uNzoD*&1P-k`3omkm$`840V!na;@tJE!w5bV@<71YAbmIZGh-x&PU zq#KM;tuKy@T4>4-h>6bn)SCEeQ^wx|U@^mRCJc0xYh?&Upo~@}oxE0s^9YyuoMM?T zesj*qo$?^3%|1#aq6gqEJ%4hSH=(6E*V4~nNLWt^1|_~Hl}WE9Ug8m;V#eZXErBho z`yO)U$MiK>fy7*n^Sj}_UXs*|W9DWGGIN;Y6D?!pzS6RvH=wX&oMj+oUi>f)UC1= z#77ruL`~f(RmO4a$M_ST+U3}?7&IuZzJXS?t*ntl{g5k30vEHm-KoiR( z_@?la{Mm%NZSOq@k+tI5MSz0w#Pj6aSDfKhaLMN9(VQ5D<=BPKR!R76b;mv`dPSf% zIkL*b;)A29tDv&MwKU50iwey+BPa0^DmoB2Z5_%t+fx_mb-;~lXlBIzISA8 zkIePb64AJb*0#bsy&_&?({0DZ@3nfBpAz&PAAG`@l)^ z|B@$m`}c4Kxqh>v8xb0{QG~nmJLRP>aJB3b^4x>m4gz~YwnjcSU(YE*+R5v!BJ)%KiWbGuj zCbY#^2|2QqnM2IXNMD(j5G*?Fr)Tad;BF{%B6otFIbCJ0dnFAk$oWCieQ@@`~$(!DoJJrXo(Y=5RAnN1zMqN znl-HeA=LY*%2Ohj`1^1sf;@iDw(v@UnV;+n!Gg!OfsixXcG*ebnPd0xe3nC0K|ftW z0Vhy?_lP3`OZlN0yBWUQS_YNmgJPf3{H=-g)G1Ycw#~Gb`x}@gX##YRmB-E7PdRA*L{lnAFiV7vN?a;;K*P1E3$H$A@tsND5{gU zg`NBYkc9L5+f;z?u=>3GunS&;CTG{5mbD*PfUI{aIK>=divDove*hs?;dT^r4$k8N zamiJ@!hgA`6kqVdzv^YJ3P0B?;oY%l9+TN^63Bi$IKIiLSve#5Qmx9$HHM4J-L=;= zB9ZUZV1q8#XI@>jojpC#BmXWE^zfp;sNB5BY(dvW=s_M`K;`GGVTAZ~7k3(eEfUv} zX}IwnV(f=?{bug?MkjhI!Dic;R16#)1q$tb{l?q)H|Jm}+*R5yQ9QaJ zY~TKRGZtLo_bfU7CP*3`iv*62fSvqEDmQFy>6@0NyAvk$!(06%sa^?*R9A^rg|>&{ z#qGbk#|GQ8n>#u`MqPNKC#m)he9r|9huuDS9ZUUs%|E? zq&X)}RYn_Q%2(#I%LR`u_m7|TbxL*`>3vBHEyeEkuwe~7O7qmqb6l5!eD{W}D7ZJB zFbZ|9UH?#%I14)fEv}0zEcHea-?7y#^E@+TR->5}E^DW*Kr1FsuM#j-9iQz}4CHT~ z;N@4`y==+U_K|0wpUH)k1H)DzNf#b|u~5MDHz~ivT}BMPZX8;8Vqg`qx^L5+k?}Uk zP<`8Y%C83Bn}x}x@3zQay)ibY-C<(*mMKttZ74XWb*}oc;tFH+6ErPaCqA)E_qb5o zTQRw1EF7#qo2Dm;CrQ_{sEcoyX%2q+ddZ5F`Z&_=Kqb91|DmQ)OqC3j`$ua{ZVLl< z8U)Z`PV3N}>e9GBB>V3>q75cSz^_BU4d<4*4TZUL%FA9$2u-V3iH3hX0cbcUU(T(V zHUxdgueazwEj@dSqnOWc=zR&N%~)$(3j2wfYV-1k^vn|xWY-H^DR8Us_w#1;PW5ZK zm>6v<+>~dl=1er)qIWG1Oe$=G%%OR`%?pC}j?P>ZP+TpnA@nBPm8$P+0b7HObtu7N zYjfNxD1iRO_pND$JS6ZW-c9I5IV~9cbuZoI5s~;w2}7+|7LdMVha0@R8y7A=CBTGQpBa zN76}|($n?KgEFY^xG!)|4adx!omZe0rkUk~fWh~&`c0brb>CBz_JdF%Oj-2Grqik_ z+4`H9vdm$;Z}_rTJ?DZA$kX{@x2OHr7rLGI{bDy`ksW@uN9r#u7(`UHEzEf*xTBD4 z5A34m2h%Bn9$b95Dm5R&ZDTliz zMF53HAeAZ$fO8g3-ZWlBw5|GVBGy7!5M;MNI=6UwiPSL^p&X@EsymM^Y)I&#sjc## zRS8*LwRZbYPt-X~0owCW!o~nvuLw{9_rosi#$N|je7r03nmSI?wkbIgv@|_rEpsBj zQEY6ybRZn{AYqBz+21=ww%l^VfUi-@wE*`ul#z#T6>LVL-Kg8E% z=bV{L6wY1Q<^uP;3nr~v!_aHL#UWz@lR)Rz&)0IMjES9iv?LM;UsUtwQpl#9mLZnbQk8T;QxY{&hsb6OPui%8Cx!-=FuE$>_MP}I| z8;FPe`|tiIdhJO4&=?7XYVuRg7otB%Cr0ve2+^iHVi-904L>W&zL&k(8@}VFPGg z6a`+(5>QzisKkA|p`H!Y4G4}cGdQ@y5Ty=zy$p9+XHLNnDf~)46>Uo*X{VrD`(Ffg z=sI=`E#PGFV<)2dREEHK!keX-OVj+HUK5}U-TCR$_09)?@rbJ#1;}2w^OSHvema{zWoU5e_zXdXJ}sQsqQ!LEz;66 z*f7>QAZ*{N_Y!EI8?<=pZp_BId`{LU&5#~#O_0v!@(hcrf}f(>#0Um2?==do=_!%f z&0Ig0a9Sk8&vm0t70QIf?)`N;cgrr>Ad4G!PxlkZBmP3E#1)wV5f|M&z)ZyUWM=wT zpM0n|lhp8iNPe4t$_-mxjA>WbYIh4RBI-62LiVdo*04YSZR z6ASmk*Zq_JdAKO4diO2p=n`o~@IekyepaU@!;7&pv@lwwg#A9bqD1=++dZ~5n;j)3 zOEplN)EcMrjG5|FO|BfPt+Gslk4^rDB%t4tsYZ{(R}^NF88++RS+{yrSDyrD_TrE6 z5B`}`^S?sfccVMgQI`eZ_laQRsmrq_5^76R{dF@5x@ZrmVCttNvgsMSPWA>O%&GMZAxVp);e23Ji8me;=Q&2Y(!**tp9!Ms zXn-CNe5v(ITcLbniu$a^WG5(;yl{GV(st~@cyu^!F|eWGvIW|;3f&y1a!?$IMjTB4 zwqVpbY2!5}8BzigqAvomB+-Jf9%aqxCrXH>_l=)AF$e+Zt6;T&%#!mqIj|>FiYi)p zxkIz<`&Q9%GwCca{T-OKCgo+7f-nFDIiul}6qz+><(*wG67T)TyQs=$GDyCp2y(Sf z+cZN$j{P3u{E=DTZ^o+-ndwwjI;QF+u<`A1iRrw_5uhYo3aVT4S)w4l1E>QrWD*DV zto@|S8fOZ9A4-6EO6Sc`60g$~Q?XtR+=prs`HY2Ux3$9~JSX{#!@uV7E`@UY1M0@$ z6F~?KA$gftm`nJvWDtI&p$Ee}As6bfJkZ~hEWU4SW|)%rY(-W-EZm|IvunTFD|}bR z)gA~unoPji8$Ya}_Ju-8Dw_v_KtjX6_L7=d@KmNc{EDD&& z+RQ!=*tWgp0NA=t6ZbcPzCdXUWvE$v`1{~a`B;TUFi3kofoJhR5FQK?!(h~Vf~%&f z0D%6e>Ws*OZC%zme! zouon{wh}by-OfTUguLGdYNTU_rp7D~4ZbvAkFe#*lQG7j`*hDq!iitXkkL`BK6gq54L8Y?g8^MXCAV2yZP{Lf7)i%8Ax8R!|NKYpH5c zKlD~avD=mZgg*H*0{Us*KqIvl{>58Mz+SMRM1Iu&eEqhXR`h|;J9_lNRv~cnJ<80` zy1F>K$@|$i7juoU_A6Oy%A*WedaUNI*4J6*_6^M@daj6Z>s=au4H6Vfs8f|c#y|YG zgd?`6#}r<>MFv`aN-}7};lR-sdAMHjmcTcau4C*Ax}8)x#t@dMm74Vt{s{vH2C)5| z$MWlkS4dI~bMHB`NA1*c)Kjt{rK^N|cKS<+X2|lq$<;5x1JdIdhd7WX#b}nc?t_zX zU9J;iP0fsB49yw1l%Q}ilTk(+fjA`t*%I~=&}Pf*Boi?Sxr}fmI=J(s+J<)H?;~7R z?#{H6V{-baaLiOsxGeYElHUim##%4r+*IEc0l$dj3C0W(0lZWsdY>+0v7!SZ15~@N z!{lQTQYT^2?(9s$pD8xR;(V5UkF*z6H}@q9-9g@SO~p*y=nx9|fuwxZ5{%v58i$k6 zC^mZi>+H{beb*1wnQVcSw`}V7>JoU;vy66jN0=s%707v?7UR)WoM&*1P z{x-Tr&UTJtN1LKm%S!A%Sy?AtvaaYP+SqRg_Rc9@@K1xzK-9hsq3`Vl&0j-Etc-8^ z2ahw29yD>}h?|B@l^Ku$7TBw-Kl7?KCYQ|^JH!%>@mE{ct|f+tQ28)~Ud!XL%Imk- z`c<;{N{qtFt;$WW^dKLZpwAB%mtf?Ua*z{c?vhneR>kH}_*O@!V@|_Pi$Hc^g9aWN zof^0P@6S~y;jjxYKc5WW58D03U~Un;$laZz;!=Q91he^8hihyo2ae)MQE=0*Lp<*e zo?#9B+?6Cl$=Ky9To+);o6sJBDS>Uo`1Z(U9wvp4!mTU2fj`&%Q0W78)dq1TZ*h|{ zv5)@RTon)h%BjAMV{Ulpe3=);?o+gmH^k&^_avIm%Ufzw9qPCtuOoaPiNEGY_)Kxr z!greZPU@jTsE008)no1c`#XDDLGyLjSDmgUOCwO8pz=|G$C7-wmR-Ny$#e=t8G-#u z-s-Xf(8uYb8=FM`Byo&Nr1dAM9wibusngy-d0_z|z3l>h!{)WSJwR{^fHB@fzi)N6 zTf>BAgSo@0!3CL1=KvKJ=h3%3N>d35rp6&baW&JYwdJ?zt{phM`gqT(T>p?*Ks@DI zxnBfwe`CT{qF7g%xKX_}|5&xP59VQjx|h<5?x_l{B|ZAVRDKtn3S=oz#d-gQ)bey* z%0xx`WIj?^#WzUD;=UO91Oq(17g@PBz`RFN?zRm6>N$De&@(0)kT~|%pCS&a%$2$Q z$>ncx#MmfKU8X}M108RnXkN*!X(1!C;}{?G40d-H-LHc`(8@!B^@NlP1pnm#p*59o zUN@<1c_@CFt@D`YSt7H<1?Dk}8DY;wzl{=}=;ytMOB%`;82Tuwgsi~AHYzLY8aVvh z>Q~hqR&u}%!bvPLD*SSX7iLXvQK5~}zHjWxz1W2{)5RzLF4+IO>zn!tT;Kf04%(wP z5^~3shD$toCc2xw%SA!%l=!uj%NDD*Nm%zWr`3f3GmW z$-S{rTG>T+t)0s@Gk#7%hd`AA!Y0Z<8DaU!!JUAzmV%1OP2HxjX;}0bb*(1qT%49r zxY&3L++gL{otm9v0Jp``W}J@CZizV*=&r-66gOJPxCWG&x-Q(;Z_oHhXq zW4`Q9tG_}AZ8tf|mD*{kJ#E(^Kc?9$*aZ2MqQKe`mOvY{;1KAT66e!uR$%hx|xx>4V{u7%rs?3*G7ztjScdyyFQ(pKG?P!nij)(gf|t#cTLQGkx>mX%v< z8!ScIH=r>jUETuD2x$P>7M5I|Uk4Jal70GVlc)w}&MF_EECESzzbZ+tdB%H>xB@P1 zaBb&!j%DT(A!(lvsIv9kYCCmg=Cv)Lv@3*~VhAK>B2sXcI|DA?LkaeeN`|jdNjc|Y zlt(W0OSH`?HblihR92t!do2x_@O7mwm2ta~FZL$r0_FHz{00Z{$#mlmnI$NoqSw#7 zS^m?H&eyEzaj)opkA?tUB=ftKFQn#&@{h%o6491=c^m|uEq+=5$BO8Zy?6TWYou-v66T>mz|TDY4{d2sr|9U}0l;6Y7m$1*q}o@&`Rr1&ZelR8#0^OF zk!ffF3b6WCk|4DDhA;ityljIXb8-?iAIAkuY`vo#rl+>{(%Wq_<*NIS&q3H|hwim$ zM0QcXQ%EdAA|xY&1>}^~`};@IW)WzWQGA{+qih^n-x@!+ZQ1JV0kWRN&q>x%dUnyR zF1ChKb18idR)T;>VF03k1>;X-o6+e21IiCA?*ZBVh^p*fx*&sa1MG2O^?$t@+bAw* zu?cEI^!O@wSzh&UFEGR7aHc3hh_!rtw@Pvl;K#r6VEhRMh@8kU2Tjh4NshJ>oKIV1 zuB!nmM>fC_jmqJ}`@6b$MEI^7U;{iBJB2no>k zKp7Lv5R*B!>FK}!s(=*Ny^o?8i4k!9`Yi#)0J(^+>6jaFVeCxZQ!n8Te5!VDo(pRHOzQieagPDj2v*@Y+%veh%x_MZ}w&Ur&I85*;}zQ-;LcbvhD4L3|=YJ@(?J+ zOVIa;sGKR>LZHe6j~msQdadrJ!!4{}TbWIad`cm^Pw(~nbGWJ4z&UYOf>JQ`_9UDA{7aU)v08k=ooGZ|Z^Ajuv$tgFhe9ARz)vKPN}MAU zRifbA;{JOYP|oBcW;3QXTUa*@tId}g5xCHh8Q#12x zSARqRu!8>w@C1MdIdJ%>t6yDywC|HCVKyElpKs>Y$lt{KaP6Xz*Z&iKqN5;ltCj!6 z>GK1WAUyzD(x!V&XpiU}+3v^>e}gJyFSZG=e`9R_2*9ZP>vUg-E0Z9F@!xX(Z}2CF zP<&^CZ_9c~|99vU0O<0S8-0G1@}m?$g&cL=UJLKcEsMWxYeIGI{1m0G@1xhH*^|<40J7xmHu|LHifz!*pC*?3y5K6! z@e9mBNZ`ep0~7#$boh^dK>&q1eSsNF#~sL-l*(;91wdIG*)zU^5A2yubH4r4L9p>9 zZ0>y2)6fRL?!AT!+Y`R-iF|(jFGR=ZKucaIB&a;@Xjj)OD57in&f7ISqo zyu9TnuH}tEX4txrFLgH?9~BWDBQ9VQ3;=fe_OOvQZB&JOHa{m0?eDqHaRAeRQB^@h zV%7HCxLrgE!g=RRYPH_8-t8U-fQ6vj&?de~g?pgC{@-87(m(MgY?FE$l?k*jy}0eW z*@m6|&zFDQ-~XHt089GkAw-uGNW0HG1z?xseh{8?d~Mz5Aap0RZCv?+@Xo#V@Z_f-j9VEw$v9 z0B;>RLpJM`&azi;eHp{EBk!lI+40_+P#K6gSst*vau4?l_&f+y+q!f6kf9ki9FU7~p;*-yUYaxA4YK zLVvV@E12^rLT*j!(OlnK`&}CLi4d3H@n0ToUf}so#cukk@Qb3Fmc5A;(P&Rl{Pj5=j3}EBMqsWC2*vi{rgWo0oT!a znm*!RhtE=E57Cd@Eu--_#{YRYgul3q_lt2HI!-@m?y%6@ZBE7_f7y|uUi^c~It9B!IDvXDlrgyhm# zrPI29Ki?OnyWh!v*|K463de_Sj(dZS2W>q|nrIbVs>;dnr{Zf56(y-aR+@u;$f2sKK>@pfNx zI96u71t<~w04^yytwh?y29M%M*Rb=G?V={`mu#?qK+0lO!rtFTZ#@ShYwd{=;GL*TR2)aOy)Goj{QO29x?jp~`CMTW4k0M5~;`Xo&`UVQhPQ zX+276O?SDCHb(Yp0~(H)A7-9_O3@VMCBRtIa-kAqByO&1Xxy#re0)J&CLv_KLY4OD z(mx((h2VX7E+L5CMO(55!u43^$re2vJ6;qD{Vqz1R$ZIoUKK^U^6*#KerlnVvQ$LE zI?)Z-kCuDCs-h%a#wQ#Dz`T_*N)FGE^)q_kup0x2JLCk+xpfTHPOgWv+|2H zA1X2e4&8UAI|}F%mFiTl^UA6=AbDEs#ynkelSqMpAdPQ3WJvE( zov8}~law}JQ953na@s1x=eISD&4}fBp2<5q$&M!nZ}G5d+V5OsH|a^)nC|PC*bHUu ze$dj<;kZnZUaBSu$XvtBFS0HxEGWsMDh%-2h6f?`7ek#64(?*282o&7x zQJNlD)L5*$`tef#pzz?^X7AlkpZuK!UpMcdzi(8ZxUpu3o{|cejZMAg;^Q|36!Dib zIQYE-i27WyE+{!y?Kx&qj(ulRVyc0+3Xex~z0C;hC}X_}ppBmN(u#lK*1Z?(c?vSR z7sv1ACmnuVEU&Q+!h$xeDaGYZw9vFjsn(10!@hT_y%3vTPEkoVOJSyM=V#h#mh!<} zSD->-ftTv@WxPbR#wtu|sY#q@Liq`+ldzk2j`(~Axn4NU`r~-R(fXAT{z_}^ z$3%}p!fWmLf~H-Mvc(lLLgr3Z_N?EwwzZ2&RfUpbl4c(mrY4qI%d2r~AV{hpQhcIt zlmO4Q>NFe=pQ%sg>KN;OUEN56({Zwd8GN}J@NsfKdl@KfcwHTaduxM zJJ=o+Y~?f2R*(!9Z+rPB=T7pQv~uuTcVL6fLxvWRvR&;#bb^PLg96WIvVgRVWJUsk zP4ydlMUrR2!dTV3mn@wV@l}M|Vsfp|Jt29-py3UmML<^wlm)nZu5^U`n9OYMfvhLb zOti*~vMP)7FU2UGKK2i`b7}WGhAK1KD@33_9!n+Pxo z*?%yJM2=33&PRd!4FJ#U^rZH@b?!L#vLe{eD2IeGgENGEur1G+fM`l!B(dDlF< zt~zdE*6LV08Nli2^|$%fnEfHN~F&9dpVjAs#qlXH+ET)&r-imBtK?(wXur7 zYxAzVA=R-XQA?_hpo~q3&nvkEGw7ZrpJ2eqA@#|LWw`F3XcZ zMntHL1Et)VPVT~mL$^Ad#(<29SX8C&jJ_{ZRH6ZB?RgaP4ai!LceR0b`SS<|^IrnH z50IA^S>^8u7xS{nK({=SL=xN-rwP9%`Nw|gs5<_ z+4MlpCSnhUKq$AhA1H&wQiMQ2pGrWeP0ox=*mb@`l1;7ULei|l=TTUza=4l5R!vCw zVud2@(cxdo$ofa)?xls4IFR5nbya_P95TrkjH#`FfYWv3oQ_p$3@sDCxXd`i@_m~8 zLfy|zjJXjRf1#Cm5x~l1GXFS(ixf$>xEHh4vb3~9HF9@8OJ^xDN3(IiPX!PGwtK*< zKKr-NeN<$ufu`>Z%8*sMqW{X!pfF|-ROR43x-;u%z4FcEK)xB# z|3bo??^uBB>3omzq#MHt@06!EgRENhy>9gv^t@~cTVTkMr7*SG!IcPy zYM>d_#7XE-0pXpTM+k7otbFINhb^%a9+}=`gE~gL%If%Wuhp@@-5Nmjwnj8K4um1t zozw0aRNM?5jU)isREO09zrD`dMJ*PN0-n#Ve>|V4olaeOS!-98t-)}B$n)nD_KKZY zp^fo}mj%nqeDum>5+)6Jl~E^vROK+(aOI5gTadNO$;BMiW=#eEbo+BfmdH07$*P;3 zzPqSEX={Fi5ja|NWq}AP$6EX=lQp%q)qMhB>e86h{hgT1jpgHHs&Cku_|?k@v`7Y! zxAFs>(vdA^J_;*N1B$V1E>y&q5}j`bjJ4>3JS|sN!<#yU!n0SdT$C_B6?pyPPuI|n z*N4mxUB;WbE3Xr{oyPF7iY;|g2%DnXH<%EY23C(xz{`nfr~JEmQbsVV3(}VH?sHg7)aI9Sf2F!= z{`g^k7mBx3eEQR&6SW z1lWz7q=mlntnt;h)iX9l$%m$TKgGO~Y?u(c`Zbuf#nEZs8V%`j7%0S%-4YHMjfayq z5U{k>ael80E40B{gV4d&7y+eh230MFtq^R|R*$!k8pywC8}7`RnVazj)QPVNd~ME3 zBQ{lz(9xad|F9bzR6be)sG!THCTw;J$Jwe0}sX)BEv0y0WO^_PDyeBMXa|qSt zlW>xL{L9a?t&_X#;x&G)$<%%bQ2svh&J0}L4mNq8gZTpMa z{s*@vTMb^e#0Wg_)Q}Br`s_?8|B8E^^{K9g{=jc;Kp_OM9J1NE3ejgQgt&kcU}3~V zX3}@!hRVQWgBjBupm!c7;Qx3U92}jWe`^&;x$<|>_`ms~CczY8ZL~Z-e@-(x5`cY9 zNy-6nmZMw(s(n_LeQ|jm*)$bM>R#8V;TjjC0k4m>xEE2pwI%I+=j#(fyf&H&A0Sl8 zVL!Nljy~Qv>VjfQrj2?~_?#d8YH$6TIG{P-=~UnPBKt=t&A-&B z|ILQp1FlT6j)B+F5}o_=yKqmQ3Ek>Ql#uTLe+X3xS#EpK)u3ike*wtHwcbdB(83AI z2*U-%x+7L$jObm&+v`D}o>PSI<0tBub;JWrv{fLCXrR)!;MLRVG6ADMD1)XRb&nsD zrBE^mTjt6i_TIf;Y;jk6gv+kR{v4M-@0zk^>J&mGVWAi$PqQH$=S;_-L?d1@@{haUK?21v6 zar{dqjD9Gw@kRaWx6vxF0iE$FR*k31li0YS1k{i)+6WwlX0D;&M|)3~L-jpmu7@t@ zp)>-=nb(I|X84cT>XIjUM}Z0yFX0VqF_aX3uc-X4cPFKUl>KVfGM-1DNC@&Re1oXGwRY9E%tteaa0=OmOIJWx9Kfb3af?B_1ad*Jf9{igz* z+kdis+rCu>!Kgp8s9JO!ke{#&on5&vCIqit%OqFd%cICrXUf1}m(=z8 zIVzA)hoOiGY#xBr4YtnDyb;Nee)b0z$~+2|(9er&KO-Qt4mHDUCV+Rm?HH-rTD*>` z=vxixSO{ze9gT#?-Ti8;?bIV0mhgJ8$W6YIbwm9*&n1xPV7^r=%r3UR<$iO!tka`m z6+GZ|GV7Nvus`Gs`}@hfuT^>v*51yWq}M6YfxLIo3A>s{17_;Sd%B|qUg4L@NOZNu^+09TOR^B6PCUC9{06A!9-Q z;|8rsLT>qLFBL`AMK)_9R5!CD}!MwCyx=UcNlN=SVYi*O41Hj@-DVp>cRRe^2(=&jOD%>vo*=S%8=X znt=QRtuQM^B%^uMc*yvsDTq|KIloFbr=oI6V1JVZzwr@L$O^agS!=b1W#ixBH7eK` zw<9F#9)qP6p^b0mvnPVTX?+4E3`o|`!&liNA$1dC!Nfrs!3yNt%^0r!@f?S3@1p-Tt5|7G_2%B;@Y0uOgFxuqs+f-k6W$qiW zJ@?yRSEcu3?_l8Z%`qs^K{ zh`u`w%Zmi*Du;Cpd8}D!-4TeIpu=OcevI6jiu_WQIGJ6NpC3>V-QTV8?Cj1p|9~BVd<;z$B@k2hyzVoJH%LYwpipQo_ zr%P1U2W=-G3#nc!|H>$jjQ(&)*8Qm)Hq2@rd+Jq4#6oIAI2d+kiNt76zLuee{Gk#> zJnMOYXmG}^U9Dl$YStSmr6my)9|}|rH1Z%;f3OXD3O5_3qjj+Z*Z2j4%2zFH>XyGR zuY*x#67IJy7lHN*d|pN<2eV$N#+f!OXDS!Vr^nV?Yns=SZgkGiZ>^d=RzK2e2Cd^_ z2JXS=vx7X&qP98p6ZEJ(59gOeK#)~h*P`GQhZ4GQ#0+OA_L6&1@_ss!aBIhsnly4?ft& zMn@b;E!X-G+2J>Bn}fY&R(h8}YHNBav!oL9+1RF9T#gUXfMPUFBW}8zsmjS^JTVU4 zAk^k$nB6!UTfaWywQQ{xlU`HCf}G%wY|STa8rc+!Wm^h1#Dq=mot3rwMBl)MOe~s2 z^%U8hLa_WgmuAgiy3ppWb4aa@$9F6-W#V%k_oAZ(2x4ch-SvKjKERXnbLX^rFLlK; z8&xH6y>RT-bFYHvKTfKkY&?mfO;vV3 zB?+`aU+tr|YLdOL)brY&lj|MNy!N=h!TH{cXtT(}nf)V9F8-g(%a1SFM2xfkY zEJ&+9TP^SsjYiLnq6)*$6NdT)$R>v*l%jD%uE$ zNI;n!QADJXA;=UVj$w$17$87YRAx{S0TJjH0URHST{nu1A zR&e-ZY9?^*?}yk7H@ng;0j2!RA(`P}ULrZ~X(MK0N`G{iE&nazMCpMxcH&XBG&;>D zIK0@SwEPt@MK}~HQ0azeH`$VIz#YTv>#Pi=l@AXpfmL5KstXAx4@`1Z1;7xsMX$(! zC5IjJ2zSClXDk7ZDk{}$yJ1u)%YxP;jyQD46YI1T{D(ECA#`vCvCUkAk-7TK>3GG* zzTci~vK9A=ok?vob97mSs@^?ryc!QkZ;o}Xpw~{{z9P(_4(;W4udlWt1G)+h7}JJR zCx>3hCnR0xGK}NZSgO8^%g3<}1hZzuyT{7l;0@(nnDKW1hl#)f!0aPm-~2WNI)D_t zAsFV>gfL)aMoBn&5zetGCN>d4>}vP2Rib9wA)h5UGb1gq07;dX#8R4UHNeR5iHn5z zUsk0~sepll?E9~HYQP-aR)AIG7>%Nnh$?ff6JhP1J&2^O!Tn`sIkR2kFYtH6B)AIW zZH6kdADy#FO3K*12onC&YL{OW8wDyKRJ=3f2peRzW<~95hUap!yX}U75reJCfzlBE zi!lS@pO0%-x=xy`LYt6Qn(PSB2s)p`jlXhZuXW8ObECs+vG#sF6ERr}`cb}B6{_!k znHJA{f_1Vz(ts245TVq#wrJrgBs_mE!96`SU1NV$4h;gk`;y< zMd9<~%Lv}#c>MS%iq}|!FWIL4QrGS0mMy(nQ4rBmaHpTCi>4W)W`av`s&EUQ?8RPV zWM1C!a`iW@6v_aD=)yLhanyn^o*e}Seq3@64g5WEc__YUQgHK21#tGaN>0{^2wbR+ z;Yu}eD2W`GHd@uZ0piB}qH8nJa{pkJ2X!g_W#}VPs{J<2r}-S`Vt&+zZmKUSeOWvzI@Y)>BY&87~Z9 znEXq?BrFoiQtLK`!!5A={h#bHFA)s)czB_8G{b!(AH)73F3?v2bA+v19*KxZ_elBe6YztA36ftIj17)HMQJxLak9t$0 zHoIjzVn#!-x09(BPiXZzkjj+y;}m_64uTZIWSZsNj3c*1`73FQ0sIj@fof}lujRcM z?OUm8nj1ZuH9LSm73r12$~4Q#bUS}@Z$RnU=Tlo_H$wcX9ERS{nyz&4)orvz?&0NJ z?3t@A?XR91>=Aq!ZRy(LmQ4zFhMn7Su4i;4>R{|!6SW0OUskjX!sCR6};s@Wt(Mtb`#+Hc7}-E8}4j+O`P zq$wH~0#NekZOYoMc%wtaFsbMX-=lClFh^GWk+?1slGd7We{Sa|T&Eeb)_9mNH1Zm# zFS{zOY0Q}xzlP#ovk{fV3ROn88z)oheUFuZ^G@gQi;ptUap7Ugk>;`|v(i%N-ra#( zG$TXq8m#Z_z@2n$2!pNVyNM()+X`8PE22&0+XRpj5-5;3{cp%;`Gb{DqSfX_3Z zvSK^F`?V3O8sjF8SO#9QHM%ESOifvEf(!i{XrI_+>M;E^N=8x)o3rO zL2ruqrOxcpZ^I$%sOkd6ZrAa=}VLNV4BeW z4__dZ9q&@8$5A;e9SW_#YM{vszAPs>pRku(8SlyZ*b+W{6VKqF(Vml$aloUrWR_-;@!ssljw+!nA!u}ha2M3S4%Tm6NoTN z-1uczsUL5@rQw0sfvBM@nHO6vxb>7iw1p)2H7HT+M@(9}8!L62MfmEvWsstpAs14m zZryZ$j{Akx`d7P+FEh3iq(|SE(271X$6%TC^cx4g&av*-5cL<8r$z=`G)k%MrIZ&J zOGcJVod%qfCt%94F+UEPx+fyeyfMC)vZgZ%Z4 zxs9YiRSd&_Z3do|SLId;N%s?iAzWrFC;f@D#_cG65-W?Q+$-j4F|tEv_Ya z13Hpak{%Z6T=^7uZ7XW>XkOnkWVnCAVorE(h$!oP(HLPOs%Xq{B3^3fM>!sqRJxVl zABU4_qG&nRxHOt5Wmbe2*b?1?SHs5QAiRXExkz1Q3M{|2!!6*Z7DvAkhA(O@vO(MN zvRdM;_8WRN7+BNeww})m@fmnUXp=Ry@~7b2cegCRucTs0_55Qs9Skye`G$cMZHV8Z zLzA5q11h~^-j|&AUVLOTYGF3q;1_x@5b&-7+mgeiL z%YPcTc5Ud+_@}C9*N==~6fMdzI%J^1;>IzlBvPl66wd=nDoVp>2<-L)kyg4pNbxfB za131vUhD(UH0NlKYM1s`?%jfvo^_1evTfztVAY- zAb`ElM@80@j4GCT-uZ=@afZQJm2-71g3UUwCg@>)%dI#=k%n^}V>21Y`05Zy(9rHL zq47&Yf&A!|_HxjYiub9Q$2ZQ3k8L8m5kuzFS`Oo*AA64bL_ zpjrYQ{7m;Bx@-R8Q6k%+2a&u=kHP#HmS%1&>xtDmp{Bo7*(Gu)D=VvY&l;ZK!_hcm zh!+Au?c2ON%BQ)pFIFcde(A+*GZmk@QhHC<%JP$YR{Lq+H*Xa|$7U08_6zGG7|-1O zmG*!tY`#te2(up5gou<}$8Y&|6Ws%VdxwOLs+%usOGGs@|b_^dZ;ofFC9!r%9;l!g|gwKqwBBFMM zHN$L**?oZ5Hjc;VP$nn6WIHdcAs^E);c?W+_bA|sZ}TDb(@qp%KI;lQW7j|wREa;- zJDYC$!ILhVPN?{)G;gj`93f9K-=vAT9ivJGyTm$Lb}JchvEZ2^ zB|y5C98X?oFSRbvk_c8lrePmCRB?I1yk5Tt_$t69GBJc{rHgrqg)gQ>$WM-7fP_+W z+*2m{5ZmPudcvBg*|fNFpLCG-cyX*J5UH<^ank#H!rw&$v1V+<_7Xgv!vg3naAsfQ z$lPw>lySBhvJJL4ETtEG%YxH!_hhUIIKhPV)tUy_M`M$HSxU{<`El(FC}69>CD(Pg zq?C!wL+pOF&HCzV#G1tws#wDy!c@v1Apj#&)P`i|k@LcKVNQKmI$&L`n-ib~9?YUI zigne12bZ=OFUddlZPHR)sB<)fn!+*d*NWI`CSy^`ojF0d%s|xN|3VUEUdmXHO|AJC z_KSX38EiH*m&3xtEUkBPg5fiRaG*9#%XdL8+}XLl$8NOKvrdTAoKhouXoLh$zTEZ| z2QCnGH6akoFU^wjGgVfQ+(_0%#Y4QaPq%JumzZVGknp3IU4F>4{oz2m1a4YvSp$4A z1<`XU*O|AK@h>gr5QiSu}5Z?VAcw=mNaX) zP~%d=foRw}>Ps`=>u5kC?KY&MvB%N6sx5knWf3dNn?a3n`f5M#tjD5fZd`Y$Qwe=z zEohi;U~Zm}k>L6Wo1`^#jo7ghgu5n|85*b``W(L&j2h;~H17q@%d7Q+2Lv=ke27(X zQ&US5dfPculZltXbvb6gh`ylW%N(52q*+r?Tv#+y4|NawOr}Wsa3!PGym(1@440ed zN|_9Z_SdhPu8kS{2KxIpyyq?C&bm~6`QnI;q6^(j#cd(jVkqI+Mln*y@Pn2<*{8QKaHJaz%Sm!_7!;Lvu^f0R}?_?)To=hXh6T6ypRX(X~K(Lq4 zhS5R19_=v{ZfDdi7M+@mYu=yTUq2TfT{|GdNZ6EF8%e5kaXQ4kkE#6rt8pv_I>cPa zZCU^>(IiU2gfx+ZNz|;ro8|dw`;5|B3@aVuJQYQ^fF5^V8J{K<`U@Pje)3y;SQE<1 z3L8%>L0tB0Vcy=b4zeycykr2?j)Hb{?9m>#y4qE&r!u7d{6T8{myVusHdQ>(z)H8r zI(P(FrC3~8@zqaW+f_Q9wFu~(QW4E=fWIhlsu?O5wXUjcD$ZIE_#X&qyNc# zp0$S=6QzB&t;843F@<_{BLYeXgpcKK>oJT^MpIJ3tUs)((0%8QfDX(^NQ^c{>3PM7 ztP!dm1KeDpE}jnexFHk#(s=@aHt#l1z$ zKGEvfFu6rvyD|#x@!}okv5nhS(lSZJR@s+Z1TheWl`DhmQ_<3sneLC;Y{Kp80!3{3 zRpIrX9!jr1>>av{z_v8?E<~`1Qr^r&9_PB$M}{CY*}7*^Pfbar^ivVjPF*&Y?Vg&x z6QM)F=>8u{dYO81hD~7_N9qe78P~&V61kr3dcxF*=z-XLn;oRrscl3_&lNcp zCuuJkXuzXjW$F;xmE$6AQ?t!DVN?P&^p1F&pZX-#n{Lf}NW`sMC&%pD1#xp|2-}l; zT*{Ome^~%4HS75f`>X=Bgal#vlXe;R6Ovm5!{KOC!knm{9yZ%;%ZySR*&Lscd$c^#*wrhEPZ_ zz1`29uHjkp8`<>x^x@@!=F_JM-(~A-bz&#Gej)|IFIC|c`efmmW^xC1_0mjb$O&XEo&-VfG%M(PMEv;tVl$_5JdB>J1t&`E$Ey*2A7@|}I3YmnEhw`0wDOmU zkh8gcmC#q$GsV4n4{Y)09w9&&U#tiU!>RBvG_I1hU}}zG2syvaLQcvOd5D3(Klcr81QV z4t$Mqe_uHfd_#CmLA2B}f$`|eF1NtD5jTq|a*P9p`czP{URsUa6*47rgETWq;XQ$;NnVfk8?~OedBq7vAl9-!Q>ISmJaU; z5!GHw4qg(t>1*Kp+wIUjK#{DVd+|`B8g?~H?TZ>_mO+~`4!&pYM|wk-=d9IZp&GxS zdT8tMFRyx)BTNYu!9x?+9LyK=I1%1LlJ%kyq+#3tXfd?$F>1Aq+sO_Lcfuak@y1OJBa)Y!w`uR!HSBSj>IWdP zuX}MneymkOfq4oaM~R>Tq~)oz9!IQ*9$|np{X<_PrNS6BuD;G@RDKj|snb2%HI!jk z%x{?bI4j&C`7nK5jOKPY3uD&@ahRmOZPt8Rk8Drbuf=jhG+ppU@@@h_NVRZx{FZW@ zb?;K^SYb0%q2u)=T-L3n*5-)unwrbOK&PU{iYZN2s1}>I6axl%+$Rl0|IIG zNzhZhrXztZqa%qpbthe!7cBSU=s`C2sPGuHr$2D9Pf6ocibEFNv0w9+wL_980>8DL_C=yAfK%kvHc$+0s@3^ zrRQ^*5h1)vyzn`H_zIj2Z#i2s;AaI04f%u6aB3Z@KX=E==^`G2(PDI5ZpC?);S!Q3 zB6Y{l<)@Ua(-H-HoZX2U-$Eg2OGoy3t%m^A?JYEW+$v}~B(Ip?I5tK3m`4aW%}Uo) z*z*EG%VhnhdO|9m+d{PYf{Kf${DQX&ms{&BF!o(vas7rgKrwZ^=g(M*)v z7&Mj#qX%{ahp%}yA~nnU02Tj>TnBQ)c@>wCn3Or3!IZBtinECF5hfwe(t&kAw{xV% zHEQy4$6UhZ-jk<4=M9q^s}s_fs;YMng+(pjJpNH?L_B?XDX?bcRany40qh93?zldQ z$B&ii@nk{4ZABvDL}&9Du1;ssy-X21O-p=3{v0LlZa6LEdPRCgiK7MmbDY$Oyq=GF z4V$N`-XZBVjs581+J$b&wHM0oxuO6_qwLt4fc`Zqq9e~+pBR#$=jZ=Jd5t5sj9`4@2s@iq~hnS#P~ zazcujrN+m<3p2H25<^*BC=()oEOx?vZYcTm9nc72_BLAEo24+uok!^}1az0B`t?&s2G7UeY zGzpvU^fB| z6*YdZ@-n=OL!J%Fvl5hk8UxHGqUQ*)PVlJm=QG?^iIaYZPZ;er85tQwWJ}*OYh$Wc zePt!KHP<~lotsm}AI2crsX+ce(nf}QT`kV7Y#O<)B+O5f**o#bfM0BU>M{3D1>5hK zJ6IpczKEQ%GExBtFDn%Vk5_Nf(}}&EywfWEfNj+F(g=~@{ZUv-^C*0#DO` z*?7XvhdQ0*a*;Zv>9&{~E7)Nvb2sJ8WEk3<=WzW!ig)$)mIG0v=;tnsD6KL}98#u4 z)+|;3?k6fo7KXs7RDwm0Bu_}qq5zd%Mil-SZY9VfnoD{Fd9>IeJ611I z{zkCmhEU(oKI5_nwc5b@aj~)%KK5|n$PpF3OiK|-&iFex8U_B^IEPAU_oU!9h{I<+ zGc%6{Z-H27%gbOe4zMo=*F!AXGFd;~>T}8N`P>VN_`b#+2XIfpQ&wL*!D?NQ2lL#g zSUYfvg}c#r47||1Y!q0i+LGqC#Wwnx&ACY^rr_b8F#nJEI#uhO7+|HntGYPb#ejXBDMyiA4ye z5dzN?Sl!CLF3bZu`cTzKZ46K6d)xtebUH<;0e<$7$1*2RNln;jl1d>(d4Zl><f+Xb{;G!rHadIT+wuKN=>U%W<=qy!1ot(+d+`**>y#5a^1GKHVw|5$7fV)oqo-_0x zMM6NPLu3DP?&?)TfKS4jx3ZX>jfP3TezUO4y&3r%ztJ*=2V0(g4>rghlL73S&w)B` z?w&+$)KVjB{P5tjBzERey+>w20}la8=dgxna8e6?@efpiB`1dS1 zV%er2SVO#%`8VPb^!fIjgLn#V;r6H$at@OE%M-ThZ-dsoUp~;W)IZ^?e|7Jhm$Cwc*tl4x;qchNjs z(H&v9Q$+R-CoSWLwu-zfq5BrFptHXQ-9%{fG!z&G_|sDm$lRRc^GtR&tJ_RjC7g6&qrx_~9aC?zC0w5y zVgw8{SV@1`YhsdZvjzlmTg%?WB%Em|N)kLMKx$~s#J$_Ckm_&uyPIu46E!F)Iceju z?>L%i!e(F<#FeA9i)P=7er@P=``3@E`4<&-_WQQsBib1BVX9*)Q(JLj0PKfI#kvVQhEY`#8b<(B8jdm%c?kldZfYSrpq-V85*6lgbj%waqmB_iQlJ>P&{4|4mE3 z-N}kLWKDtoe5-LAwDXSI{2nx1ov&td|7Xypuog5a47Z-Wfk}W%t)J;*O%HsZ#i|Ot zh8e%0eQCfjBscmuO;5@GwIGlPc%%S!a|4FI+C{v#%0u_N2)vhreOr9MiO*5&<49ui zyuf|}t#{(kDrit$nn^2~T&^HczI9){**4m)1lVfr0yYbrC>#3Rtx^K$N@kdmJFYiQ z5XbQ`CN6Z%pc##R6SMaU@FwDBS4}fkIyhMKEI45SC@D8g#hqQO%2vwAw-p%fcWu|T z?NKl%`<&|5Lk>6t0$BW!|3UDo5|LI z!A4#0!e64_E&~0&gQI0gYi~i<$pm4XTqBye1^s835tRbeB7T>3{wl};Ei(}mIXPT! zCatIs?x|jD10uKHvrs2OdV0{^NJ&dboh-%(v~L;1>QsYSqTm9@$AI>nl6|93jXlM% z@@eRsfE5@YbNXQh^H)O-V1_=!2u@2<<_sEZE%$rH>`B#VT@Oz(E|jXVM;HXJh9WHV z!YpwNOJ9<5toQqDzTS2#`8WBhdXEoJb;+}{(+EF5sA=_FGY0@3N@1tH&5kjfePE!W z*37Z1|I(!vbPerOAzy8`?H0s$oju#zWE<%IA+BBR@vb7 z#@65)Mr@!|IDxgyALt&@+{+r5kMt{g+-fO!&LEsxXRUR(KQAaqzQ26>&Vlc*NrHTz&3|tHM>3%QFO?uyFsJ7D zu?DajVBeL(cBaD3=$GxI73Srys50I;kc$#R%b#AIq~4_~14;2Qg$*WEk9mhvC=_oL zJ?CG44Vmi0x>w=-GqhI%R>fD?*6B(s_5{7QTr`jlRd8&eHTxCD{*K>(^b&NkSpMY> zSV(ItnJWgadCUU=aBZS7JZddKx8ahrkuSCowE?cqRbid$1A*-|Bp)#B1p`az!pPiZ z>w(7JD_FSGSFHF{cssCrAkZt6XL|A6xpOn*pMjy>sQBqmIh{8AX|uBJPwQ#&6Hg>n z`wkmvknL9aF1fRF#h>eqy#6%fnr?q^W97yp3v=}@Hr@Kg=b_n{2I!4*fI7j4Z5lxG z1WWg84devX0{dTVR6w9F1_}_ml-tS-{(NOG&Nu(jz&7Z~&QY!i4UnnIip{ClxF*@1 z2cGwP|D3UfQXch)1c}+1YOqRRsuU?9qh za^q_~Yx^9h5;j^iw^rBQJxA~}aJAf~Wpo$lMx?0%NKZHhD06-bYbaLDw>3RO((B?< zoNp!zNXh0eR8T#7(ozBQC@J3sh)kHq3G^ryQ4iKK#@WRKbW^%y@WzR zHI~vL1x~jUM=sTJ` z1~?1WH`E6lIs^b5I`{$h`vJEA$B!O6e(dP+Th( z=i}z(6%>&W6BH5`=HQtOu>S$Tb@FigF~E^SmjH*k4jth-wEqbp{7cMZzr_7B965COK;9E453umFfWwE5 z96oa7#EFx~PaZvf_Q~>pqI>=p5nY zyN*EKz3ku^pFzqT^?dw(Q9}9F9|bj6^p*+_mgrJoim|_w)`P#YZncUb_BnQAu|Mw%-RhbwGHS>j)P> z2e5YakjbB1|4rnWq6tUDy*G#HaeB*_?!63pMq5_2dig^BbEB*NjdF2`Qh~#BQ3>Dg zalHTk5?Bs+?B>lEz`wTFA?ypj!ep5N2u!^sKKTm6*3HRpS%B`XTwMfV51 zMlO?FyAc+mIcI=-h4kG7or`KIATnAFNocpPHTQ{x$^Df=DVNOM%&sKJX{R} zOx@0DcP?p}4RsFjDlWaf1t_UeD8^E+XFGr2VEH7zUbJ@bH4v2)t<`vF&Jrvan z&W1CdOdAUN_C?~hJ!1U$~SIf4&%A&*<#5ass72U-)q5%=>vxdY6h6#P6lqJcz8G?Dz}*=8VW&R<^By z(nQ+qb(MxbTS*!~lgx_Pl^AtERFc`|J!~cOA8Mw8(xoOnmaffUxG&18+xeD{PJj<1RANPYNxcvgn7o4)*WTXa1f;axUW1D7PqMpSPxe1TWZFp33_LB`G8Yzr z?cw+V_U9p-bzL;+YlOy4mp+BQWPgYbYH`W~D~+e1bl>86;# zYCT6ef80Dm)v~{2bssQOHmo+#lJIyr5SL|JUp9I9!@^t$*7#f0A5A8`$9SfGEz+h> zOb+Qcy>G0z@?kEJhpf*%L_(?2H0u*W^|ceX_I^C?I@@b%wvLyovxol?`^S|_2i^I< zHxzkB@j6aVC7M~4(WW61duWG+-`Ng{VgtGN0l2-9q@g;U{?L;DDMW~>x(`4bbM)|~ zoV$`4Z!Ep_w@G#6^?XbUMb8cv1H8pWjr~3N%v$J=bS9UrsW~qpyU`KxI5y(Q7xs+L znwiZHOL?S_|ETjJmv&>BQzeW~r!yz7&{ryAJmxu+UZ?`|^ zY>0h0V+Y{672UOM>78jkyXKbtRZg&})!b~Oy(|*!ZsCRbSV2|ub;tL}R|bmLu4$>< zBrj9@Tw6`Zoa$NhEL2almDO#q5~jv`23+}jUA-zs)T?yD^yizY@@xG>|Ml2g%x!zO zjA<4l8Z^|uNP~jJHJRWj{9^H*XGYsCv9_i@m{MK7UrtDkmVMRa|J*MRu<axA_Vy_)S>Kqu)kFiKM_ypx(4O#logqTJ*zC*uerXYTAF*KFH;5`TYR$>^N6 z7(&ucn4QC4&UiB@E!R1#!oby=8Q<3q+!QJz=n(iE-3@jHQ)AUvr;#KW@OpFr_&S)3 zij`J=pEMJEQtw~e_r(3;bb2VaF4jMK#Q7$4< zi%PpmWEv-oz#@IxnWse|6uT#OEiJApX!yFJH}!ft7z7b5Y3oMQEY_uN_>YzGFL9lf zLc@5WI-Dshx+*2xEam!yaijvM8p|Qcm+j-(tRd6Si6XR?)|5pNGBJZl7#++_;*r){ zLAQlm@7A!(JYCBWsdzuvMGg;!8&_xLt4Q5!4Rhk-?uWs0D%mP>^CH#I0_&Aoqr>R# zwbf|dZsK;on>)-)5(*n>%DN8CP4hW78RaqCK2l}6TH4;dt)(9>(?11cR3vPr1gu15 zPU~s4lPs49`o6d~=fA1jRHk8}%$ul8Z<%P@NHV(;&@8{ED06O<2o>7t1IuGoXJx&Q zP)ar-OlO;>p#WR@C%8$3J4s|O@CnTOd`-QbyeYckitA)Qr|6SJhu~~$x|Z#&U_o=g zu;Fr=<@EGwst9#@T}3dJ>S^#BgT`+?AcKj>KlWflt0XJQqQ+vpZ7nq&>rG~DYIGL< zgq8T3^A=8mW74pjUk7fv6qCdGRav!zng&{y`Wpqi#qEr?%VcaYl&^=IwG%_Cv(WT} z1iszOvo2M>Gh-*!cRE?Eez}aa*@vfs$+vYu1ogscs3~q0o|3p{J^b6vUQ`eMhy1ip zRkW2&C@c!?jvG^uB&gQShXW%uP=jiqE95d*y_rDsKEOaecUu4zpp=VquHWoi%;{y( z;f`8h(jTzV%#7kmA^(-hHoR!DeIPa)r8`qI5#Gi4eU7f1ks|T*>2rDGBzFwIDX8{Kxn40phykvxH{4C!ePez1#@*fxv8BFBeHu?rA)JCpoX%e*R1i;?;MPlLoN%i`5Chc9i@qVKYXZkaIW^QCGS z>fY1!nf9n;ifUHDbG;Sgfe)VnhrY9hUyD5gUL+61ejpt307hKOW`Fo!PU>feqq3>L zhb7CrdjS`h=;#0Xq41+I}M8xcmW;@|G1xrX1@W_sojApB&-(} z)>YVSRATNmh%}1}g2Y}O*p+r%4l1p-ECQf^lwV7?gS{ntXIii*_NuJHND|KnJPLcC zFS*0RI@+Fma2(OL-HYsWFzc3e9;$C82iX`2 zp`{EPDbt4Xsl^=uH`jJ;0}o zPOxl`nMua#k%c8wCOJK=doCYm2`1U3GqLUAfha~^cAF}Z!9X>Frd+xFLMFddsn3UH zTbV`8mRPe0K#QX6{uE!=NGp$|e)*EA5514sW)dP8>d?$$7VTr~#Q0N9m15NzKgg@j zdEU4@CpV7I$b9w+0XSIz5trJac~^Qb%1%2aY4$VcTTsH*da~^h*>7Nu1Wf@pdD{cA zE=>0j56e~o7mZuwR(7ui!fjO;wd^cDF4|DFR!-g$k7^(ZFCJ&{tH;nCDa!fS90-iy zIvaA&J+U#c=b{_U(%MD_17gwC5HW*v&&T}=2I>2N3)dfU2IeRM`#Wh`1trN8L;K23 zQ+(2fM^u`QsVF(ScRFFFa<@*MI|a|3wUjV)Zu>^l@ziRxQXdZmoSCe#_A*0K%Vj zC2HeMBU^>g_VUL0*_>}=(^14aezSBKk2Dl$k|K(TpJJ{iJ_K~npcaT(J>Q0`vk|7l zm(!(PVi~fM43D58z6;7XG9?6Ru7}}C5_o2lb2i{6)>rpO^!JtFkJ;14@fLA2r8{6& zDG53gT{t6QTj(ftizy-OR4em|?GdL;4#-ueoni{6A1B=2RM(}x$ zKbnH4_N3gW1MX++!p1k^0ODgETd2^&r=qc)%-zh}fY?}rY4l#FaDa}H@%qaPK`*$L zrgr6Y`fmLP923&>_3j{6Fqn+-a?O|Oq``$8)a)xN6pCL6SpO~4i@$S>#8`Jwpl~Ye zY8V_(mN%4`zOQxAwZQ`Am{zN6Wr|_x5(NBWrysWSIGlcBH!XD^(A+jJ1Laha9%vX(sj^0=rOi%cck4cls&Y^6E<)`9@1|(yTZgBmlWbw1W}kW(-MU`f~Mt{2@Q;4{*xx2R@RSf+C0xVBTqy1uK=+2+ZI>c2)8 zw-n+H@9l_u_Z_x-dgdj2fPk-m4!VDhaEiIe&~V~Wc`~Ln!IvY8nat!iQ25YzVl2$9 z-Jh`@uRg4@CBt0JHUD4<5NhsEg?|)j{S;_C%C(=VO z>&BW;A+3e!wcgWBNy*VtR3nF#qDh^KDGE;4YZB@u4L}RGR_vi`42#s; zqP?CE*vIwgn3J#Qn2-pqXtnaY7#}HlBF39c zOf7=mwFI5Jg;^Quoe{Otka^aMSe;2&!~TGiDr=I#V0QO$$x?f&&eP25o6tm@RWTh# zS93>0k(f?+1X)r*O{zgod8G}zR@^8uZ>53ih{E)nra17Oq{1Om6^;s?HRB6197fIv z&+tnAmW40)b0MBzO1FnqW`zu{2hrC5SSGIHS8hRE$+`WXT(KUo+I8iHCkxRnj6vCX zy;bj!mU^G-q!Z&x_6LJ63GejjX>xJL<=rzKvyDCa*TN8Y@OKl=8L?dgCKRnX3Lw(K z9#LJtjUVlcvw-~^>(ujqLNR{Ym$ND_FGYLZlSP#tw61eVo3tjGyIdtpWiL;>jT`VO zt4ODH#XL3UL2u@p;601Dd$ff@Sevrmef?`robwSX;UJRuZ8ov2WMR_dWGwS78xi zy)$K_q^Stjmud%U9`ZhY%>#yZHDVh}0^Xcp$o-IWQOfasK*)Lx^YtbQrrgefdi%n{ ze!59#gDq^%k9S?nmf1}T0?Cx+4S(i4UN~lZ8tH4KaQ0L2{K%NEcFAfgo}-Z2>V{!1 z@9L%&cxHn~1%nGRrrhn57Opo(IdtO4Vfz68*P$pgr0QC}tJkhAnJ8gl!xmT#{g1^9tTG_SE}3`6}H`p{NRxiFtL{Tapaw0W$44 z%{br>N@JrDrnc>JrNHHG9VsK*GUS4z{xmf9rJ;Voicf=v!x_*-AS-^=d;D<+;Oj^s z+1H@3`1BSJZ=#lNPI_$`j$qoU-0F_(7Os9Z5Bv8BE);sx$_o=V|o6|jiRyoqx*p6U^;7o zn8b1wf7Y6No?PP*(HvMbzTCVK82j6?uOoRlV7)qdRl4F5IF4!M8Mh=n+muh!ZHD=DF%!s(1!DlDmQy5=;o7BWgSABr-kgPM%bw0k47 zD@t!V20XKqC+ma82^@708~CVxN^!RGdht}#)Y6aD6;Pi|u8fH$2;#-cq-&t!X-nyp{UC&0!o4+mhjWj99OOg=;$a1btPm zlo|4-I%fU>S+!J@f}s}@?Q}1s01oU9e&GX?Je*aTH>HseW;T=kI-+X9~tB*#~m!{#rAH_&({}$$+8Q*x}uU+yh&c$0rt`jho4dA&H_1316 z%lwa)DYqN894hIRhx}xRo~$1GRuCZ6&i5*Dy~@;BG+z85Aw1RrEL}+y_jGg=3J~J6 z7O-j`dN=_(-P~DT?tTAWpHB#}-0Fc_@d1lwYad%4Y!fN?_gTnkZXDUDy=7=s<&7G! zGX8P)IIr7k;4z1rHwF3+7*z1O-3mCoXj#cQ#Sy{#Lr=J!jSIP;^C&3mSx$>fC_u{# z02nL3)xRqPWtqj2!Gdkh<49OcA{M3j+fu^Bu$2BaNYR$Z8u9s9pLq9hV^vwXT*Fv{ zU8&LI+T~%%<@`3^ogrnF`QfSl=?@~YPTQemjC$<%V3W@v_JdT-zVIiMrA917fN^K2P&xIdp7J(QAKbNtB$qfCpS zG2KIdu=9j+dV-rSgCvT9n!QN;%5OSx#zJ=Kan4%VoolnnIf5<=WV0^4?r1EKrR-{H z1iC1S^K7v8)SN5^F(S%#3X{x{`I0i*xVNN}Oe)Yb-Cq(61)*Idz9xO?$xk!L-aJcy z$nRQp4da}Q4C(H$C;I?Jg=oIgI%}lin6C=IQ=09Y04)W4NK1EgF4c@RqradrKj0Nv zPcslIIMd?aEWBKKyfYTF8c-%-V`I&FlBQ21nUSpm@PpFQQk6@1L`Hm2zoA4+VQH$u zo3Ar?y0)>jvGF`A>0Lh$r*5D0XrJ1MB)r9T+I}n%p7qwZJ53Tb-&Hw5viHR13^YBU z13^xv>Qq%E4yq78$=G`zvyGU45}AcL-LyP9Viu7#G2PougR+x8sUX6H9TI1q(+agH zo#mRN3{)4j)P&}_Ny9LTIw{3LYZRT4larc{gmV@WzylX~lXml9T zja;g)LL1DSciVCOEpTP zgBk%nTqV&5tyKPIq^ofxi*mWs=FFRaolF0l(#v3*xKGX!MrdA_^wM1ZSbtm0)t*oLHVKA#%B!f9Lz$kubc<7(Dq$U{oiI`R|3^r z|6mq|y-7Tng@%{QJlBZN0G{64=hMwfh)qTL`s?0KD080;q;n5GYnhd!3x>s}>lMWr zUXJ4~=OgQe53&cwqxq!bA$I=T3W}8qGh1kL|YgUNduQI~YY?@pyJ(Gw5~_MUNqAfzpWGrviIT$A^JQj{zY@jZW_EZ{@`Jfil3ufE zK7MhmbTsHYE9qb#4SjW}sn6$xZUM{L&Z^cVyz5jpVu$43sGJOOt2M?n1*M+O{?8qy z^7t@)m8_=Q&F<&t8UG2sreI0HDlZxmuYb%PvkR6hI~Zl5pHzE>A#Qu)`v9&)+D{7n zKBtr5_B4GTFi<@zjQx>Sybti$2RsX?e$MKBY*}Bmg$>?!*+F9rB`gX3;Leq}23kHg;p01hYBkN$0IhaOz;)z01}Yf^U7oCN?4%W2EO zq^8iu^+zvGg-Y)OB>Vma(|mwAIo%aCq^<+M3^?R6$*-Wc_eTEc>B%m!LJEE#5I=w9 z0{Xw(6kWqh8NJ>-Y3cI)T!^8MB?FwdW$7P{aD8W%&TJ~}1HwWf z0)P{PdeY3dtCpNPAe}J7V>Z9fowRI`}%5Z znf29%gOT?#Xj0iFDooY!KY9p8(AUgPKR4%mzW@usbN~Qw<}!J}?y$v6*n+|%*&7v9 z-8YE;JWc+RI;tmdurip&noTGTE50p>BBkAo4lUs5hJoe#&ju|X&D%>#RkV{Sk(#^` z;j?V(>87TbRLAo|nSEXE^@D_|?~_!OdfUe84CWsn&i#e$LG*oYmugXq7vn@Z-e16c z$s=PCYj2h!rP`$ZKnNSqRH*no+BRPEd*2}u!|W@);pV24qr3Hb3x0|po7zG)7p6|! zL?_+nw=)ivWv5Y!!pp`uQrTG2(U_Zl<5b~?xtoOc0%__?`+CG0V%;BWe zM+xq?ajSeY)qR$sV>-L%S<>jBAj2H*_CRytr`( zrXVdCY*(n9R1_B!1L}X8oZX^hz6HBh(w9eygW)mLj`J(<$tay}T&lct z>g(JeyHBnq#0&NNamqjK(~z?ZX9rixysm5Ppy|82Hz3BNg0rT&;zi!ILB?QBMUnd7 z0Q`!7@1K)X+MGM?19?f{L-`!=)K+34e5-t(KX`DBjdn@XeeEkmFT0G1PeWS zUl-&4#aO1NFc_RyY-^fsR)}e7Iv>$gJC(fZK+!Rs7SvP6=TdbGi&;_P0!H1P6D_Lm z`_^B*>P~reeOO}N${cmCW;AL6XcCU`$cIQnlj1xgf`x3%9yBdcHw}&i`SttGwP}^s zLqOqV4cl(JGH#?QqL3^NzE*=D1k;Ol6<*6H?!BaAHf*1PiJ4+$Tm$=nQL4l~pwG>_ z=s^hyR*5=Gha#z$XW%fXiE)-WGUYn^wiVL_1N1d7TAeAa7ZTAcRbk3#CQ`0kNk&6^ z7y*fgBgjx6pJms-6|!Kgo!H{{O8u$OmJ!Mqv^v8@)|c;(<&&bff*~t0_tux!WVgNw z4L5b49;yuR#RHk?CtxJ9sj&;J)jx=R0HeIVHVw;}pum8JE^Ztiu{!2z@)U75TXUQ6 ztXaFSti1X8?_|N8_^^tegX;VHPi#4H20&~{94NkZo}Wc<*Qhs3Cs_np9LAIQ+g5hV zBnG!^I4L2Ta^EENr{_$Rlpz>sq>7c1IgX?lo`pyj$yMJBmsT?@W;N&U;-fBHUGSE{ z20rhgs+GO?g99nsEL{i$Q>C}-Q5_u9%NEN4?fN6*UebV1Pg%^F2~ zu~C43^MhDp0|ld2(KI+B){((cX2gThMWS0LCXi!C3kHo9;w&dNt9>u13yP$7%BV*O z5;8_wK9-tAG~4C{^=M}~4vuqJ`J2;Cf$p_9&za5ONiqGL!Y-w4t6&2|3Qh>s8-Ba^ z3E8N6awSd8b8F~5v8aILE!agNQQNba-ecg?NOGfW#pGS%wQ87kErV`L1o8k|jT}`= z8}s}O@6`shM3hlnXAq;T{@4~sL#t(kM?GtpT98D~3f*xqeUE|&c};7Y5v3%7SXB=r zofN|$MdoVZyIgxC;{v4cG%bM7r`u~lU;(_0q^qN zz=h{csXR(awY8Slaq71p8!kUt`nr2dY-S&@^HLMBo`%=igR*p%|CY2^@efVaHa^i= zJrtX`B1>1VuOfL11v)QKFAr-+a%k?HYIbeI_+CA5%SpQ}tSXl}e>I>7&M4TH2|8%EZJsIao7uh2L1$)d%#v)#LCF`Z2~Wwd2!m*OAh^&-#;3 z3l7Ot2klm!o0MBMYFhVN82i3dOqF|wUyyD{b&pa>155jpY*3wK7_6AaPEa-V!s=6q zGb>hWq}&c_Ut0mMil`xx|ypqVzhI-JOip`&787ATKQ`+u()>w5nUecpH-#; z&@AisB<_A|rB2%d1*D+MI>zhMkNQiYL#+X6_d(zab8tiox@XxJq@d!fG!)<hr-FQ1pNK$#$>fM&(;B1%=X z%*l_Hi&o5}iLxq-4I%m^Q-Tn>pG6l1F%-0+RyiyWT35aM>a7IN@JIYKwX36DVaBYubf+B2ikWJs!h|JYFdE-0%_7!=&WWvnrElsIr+XX5*?YUleT1A! zD2xR(qZ&@P84b%tsIFLV=9#XP@681+ieBp*8o(+sT)(1=it#QOlsdQEIQt8lYHKDc z670$q5lE6Y^6cxh@K+uTpTClyGeb-#duChPJeZj(*{w!YimWTX z_cusH_sQ4U7@2nQCQ=Y|7y|__$~T3@4Y9~D&y=01h7cilsLj==m^vT0`CMhN?h_Jw zUk)KtV^cY3W9@5<>pPzF>R6fY9Vr{MxU1!jE}V`6=Sae_tN(3D9& zQ`30F&IVtRFsY?^CwI-YacID{8MT~~M>ztc>!!k1?%*F(b^EupB)aHGO*0ux5XPr4 z8`_=g;6$li(Qp^U04GmNZ>in+v1dFtgWft3*TtlL1@qinN0RlEQ~ej}fbq5|jup!n zrf>XZaQuqi!ThOh%XbB?yX|`J13E4-gF-CqwH244y)B*d-h;O{(t#e9YuUf8HlOZ7 z7o)r@F^NiY3&2`UK?3jHmDq!=Tk!HzN>_Src?-Rx#He*uS|ZBtvd7a{mxS7cVF5pF zW{5m?M$_>}q8WPyu!hV5ZgTeLvO@c8Vk4X$*}SFDDx&Fr@BjvDQ0xB zxpCD=H>J&x63SL=`shTIz@-(Trv-6DZ*A5DT__tOHNyU`MTGI;!VYQT2h|PCS#)xMW;&!u9wLCkHgl6F;Bb1n(~`kxVgc zbo$mGdQK)<+_;2b;EvX(QDGoTl^QI?fuUU4{BltHB|X3En)*Iqa7P=WopBdbd~rFL z51VU?YJqzaPIe7K$!fW-Sb?XRKYrJM$k(@yBIjkNOlVSb9^_-fxs$?CwND92*YC|f zb8RprdtU%dy8OMrPv!(I9GtJ>*#}I$mj0#OALza+`+%d7VqOW|T3oP~3mKH_cdSmY z?iGKFjQpFPf0hpb01=+pcV&$z_@=*pl-bQM*Z}4nue(<%D=Ys5OM>h@hjQvT%>&U- z4BT`H6^?>eO<*ZGngNbi`a@*DK0t5lIDccVhjbMDuBe`{iZSvABPL}X6eHZkwq5>- zZU?PS!Lo)=6I7jgBlMi~@ncf0DVH;uaqh-)0Xtl2A1QOYq{qg++xqpF?|xrQuuO&u zU3h6xr{_<-lxdoqd;K(Bg)Ol1)ECYxZ$EVF5>%m?BTz1)e#-YaD8yrOR(x5;8et2$ z#UprN%}}nGNQH+K$2(6kLo?vNxEd8=$v9mw7=Z>!%_ZC^%?nB^PhN6tjg~|ryuQuI z`nSqx7Q}?oe-`NcA~!gmR0hf_ z+$dLF-mx2IsQw^D8Onthn_}Rd{@JAO!lkHs^@gg912eV76wGT-1uU%6(txSm)vx_i z%tz1pOO-OB`X2Niji?98v>5VSD%~4~6Ky}wT+2$vTuYpRM~2>YdY-1&dM!nQtq!bc zfzHi};e&0e73RAG6U^foVX1x|lut$@Egnm1YN|N@NaT3Ht*7yzjGx=vsc4+1Z8x02 zz~ITMC5AN+6~3+@j_jd96L&9HEc3xa2@5NBq-rnsV2d@pI=m4()3$qHy@MQhCR^q< zgHpIYes9p@ipZ^~STZ$(wvAyfH)K@PKGzQ=^N87L64#rCpO zT~tc{r&9l6QKJuWsnk#~_Xv1-^ekl{3Zs;$x%#u9^xss>UrXOkfjVcMy%FrNuu>LJ zK*dvGP!e2Z*gX?RD=1vi1QLgX?bZ%urfiTywez zU0*cf@ILhl-HFAKR-7dR*~EBwD_aJg!leS=Peit5oX$neF4zJhbe6XYdR@pyiP2RqAjj(fpW1TtL?t zG&wow;VXWh4Hiuv_FEOwNTKZp^i-$}c|M~GZsh8ldr0-da*jposz{5rt;gX9Rszzp zdEp;^iDp6qnm%$6rwt~4c=@txq50d1{D&vs{KEX3aLPtMGT*I;6ZjHi;?`Zpj|b+w z3re|P3RcLqqb3{5zDnf9(+r`8^%)lM$l4|4&JMbfKy1)8d3yGsDM2Q}oeH&akifTG zF26_uL&~+Qa^mftS1(I%$uDSL+!6mw*dmm6`?RV>^!As-q@rE#+FhWC2LD`JXZ+U_+ zU(~0N=0|S-!1(`z_V;c7B42Bcpoq|6`M;K|;ZN*2JbWo_qdE3u=*i^+6LL_=;?u>L zAt&z-#s3gb^B=j2Uk^Tz`;#d4AKB(7{+p~@GLzldcrEn?to;r40bsWJw9VvMg{SEu z?|)7&;?$W`t3l;=-{O96je1~&x&-3jVk}J^KK{i1@AfBoxu*f4R*N^R|4oPeAAyd1 zl!4N6GvjL%?R`OKpb&!k*$38%x>W{{s)raSZ@2(11FYh$yyDVKqfi?i(qD1_(U&_+ zX@okP=H!nrDW&Rdp7(1TrMDtSbgw4BKJw&OK8|+0Y!CGo`C?^!e9wy{nGk7HA!Lj> zVi!VIfAEpF??o8wJ8m4TAYgNZ%op`W$s34x+@J{2#+kc9k}dmnZA@>YYDCWR<-D5$ zr<$J2cz-+L?7!TMI3`qc(U%MMfe*kQjoSw2T1}b3(xp7>A2@cW3)0U0CVC5S;q8xu zyO1~gPM9ITSIh-m^)7VQkNaeb!-A2vP6rpY!pC3M#EhQ<7gT+LdwiTaUH{n7MbFVe z-zep*@G0zoJGYbcy`Y!;+G`yfz;A3csdqMNOIix1r;Wr#%PT^8t0+Uo?4w6GnK8cZkI^KLcB-bk;01PZ|IR);f0Z@o{u%(r-(yUBWix#{s9~g7l1`^ z2#DCuRUM6rIWR{9w1VC<^uVW$^V1Lf^t%_g8rkcqFr?_c{te#8DV6hWjVdpcoEY7#$iYK!ZC6vgb zMk8HH-fs?NSInO#I-wQ_E#P+q8bvVKxz*1T(!efV^*mw9CX?yi+oXOkZ|0+3bEJkk zYg$uNiJ~f1oTlsl{lmcHn{Y;%rd*y(dBT`Mbg7gk1Druou0%t~X#;K_{yB=}ZBEg! zPfK;(q*w1tS-%pxb132=;8d)uarJ_Ypro_G~$3jp?A;WsRBBrZAJP&?jz-5w3QMv~A~Z+Jx%g;fgnu z#Sf&hE7)oHJN)+XPeE#dMt>3V2ISd#t2R}^_tF$YqB-6DUPa&Yytvt=!;5@307W9x zyKW!(cxx+l=sz#W0}i#WS6j2Z0+PJtWPUGX$qXR~Qq25p{jO4}1+ZFFaS=Y1Pw)Iq z-+tT9Y2PFSsOocjVPSPO$-aR?)2D^y7_rSGy4{zqYFj&GQP;JLDs9PmvBqX0aCdel zsRlZf6Y2RF7ULL>?|~<<7CRrPE+@z%KYKsuwpDjz;0XUh zV8(WdMg0SVYM-Zto$Y~!>Ak~N%$UR+mJj}iYL%CKk}LQ4Dc^e;opEkyR}$eWjIG^b z$88ON+E9Ha)x;Fc{cTdZ6=o6!Gpr~D%dDmPduHhnnv-c;5tS4tIozaye!i+|E91-C zo%h%LFxUF<*LxI$-eb2t`!HJ#jZm6>Zb!-c`75t|DV;1SlgFL|3)487L4t(9NXeMy zX29VhpJ7?$F7$f9>6!)KL`ABISzk2Kzw4dXbY0^EmM^0!^HQ?K#IAmu<)-O-3hC>N zASV&0PYm!iKaWJve|phbtd%k~xc7y0{xP|tE%CTHIsBa0pk1MRmkb&t7kEp>(>ITa zko0sOgDO&NN*c~6x@z(61hl&D=7>oSMs~ZpVdUg6f^@elwNXGfbB1)+KvKekgaOb{ zlm8Lg`%Pa)_B3?TD2G1gX{N#>rSEO+Y!Dq0A?=hYRkU?Cz1t^oTr@Sh&$w~5usL}a zi{j%;+7oTGjM1DD$XD=Ht!QJW!AKH0C(>hY9RmoDCaX(O(+jCuIAglyQ;K8YqNkWT%j+WMK@wap4 zO$^bFnb7!gk}b28h)Gd{QFSxL^>may>kOop>mMW;ceZ-8jhAiB8s&SfascI(Pp`A9 ztmH6Ts$VNj8=P_cB7q;yZr{NVSWOR0m3cF&s1Dis3w`4V0(-I8YmiFaihlSRyicBJK){0&DSvY!-=_NVeMq=qZEceM zFZ#ouH96#HI|lN$8fqaQJduHOoUZ`choiqa{uf`#;FfB_-?8{Sb6^>~{2%9GCJuaN z`vBv$z2lb8Lp#NCb>E%Q=?cvin-1Lva4w5A2JQn?s-5B&i<&j<_KNv@zPPMhOG$m` znOON`j=VdFr46;B`S?JYlQ6mw*aQE4tEg|HDaOuVIe+#zrg-(*Y%+IVS215xi7f`_ z#Ed2e3wbu5Ag2(XHvVvu1!2s)6WEi|RZ!tMp;N0s2KNk{(s})pIcTGRe&iDa>)f}V zZ$seZeUc_bYYy~<5v5sq1L-hV4DWJeTtfX)ISrC$p;*p_HdARfV$!+_iFMKAU|%TD z_o*h8vrq0$nliJOOsh<-P?oL!TOvL8ypqcZGd8*tbnwQtvJ_&=ZyWXU*9ZCO# z!oQU3&!xtaFlQ^6xS4|^_1E9T=d0D}dqk0&AR`lO%SRf1c-`BBI@?)0_dUtzj&}n` zcjkk+r|LADvjR(?1sB=bo;uO?WTSIa^?==^! za}>O2Ux!RDWoBpp2rSA=+1lcK`)8LPXg(7k`T?g9fJ67TSN9Ya9LM8-Fn{iysyLaw zLNzna2@EwmzjGUeElV&%%1iZc-lDXc!1_8@tD;heE6r#3PA=_r`BcN^C{4<=j}deS zyKprt7Ea$TDPoXsDk!O~e8A_yPu~e!bZotNsLttOvlxA2bDJMl8=D&Q;*3>=AJl$C z5#faCd{ps*<*NL%sfEzD$*Xm)cHBXeq`Z#xa>mk5JPZ|_CiZ;u*@W_9tH<{qm6+7x zDecJI6x@}Rl%&T{V}YQuW#KA`lA?$q2hW9N=Wq8~0*vXz9hly@_^bI7SPE%Iu`u?2 zxj{CWM_wyB69e;Ky)sdn|A<*z7LnpQDbZUs?7THV0b|CDD$V2GMyF0I^wylIs6Oc7Ec=skfR=kW-m9@494Cx2<{E(pC)E?<>QpX%_fEh@AIwtu!`tS@}}^}!22mZ$+DWB zmIfyN3XSlJxAOHhHUJyI;jg}wwd$kRe>dViEIAkzDoDp{DkQJmeDs(k)=m4$7B~4*{mw_N zpVMi!Y{GX3tRh?$WD<>UI-Rq-rNh6s^eM5utzM^s?VO>#?B%NkX@+9|8Nrdm3`e>0 z*bCJB+Rp9+OmRj5ARZ8mpqAryb&z|#0@U2q$k=N5ms|r5@>Fe$iOQo$oE#18L3h*7 zr0@u+-Bh`JaEj*pW}>^q>h-1AAHs&gr*<5+Kjc#3L<iq9nBFNuo^wr<7ka)H*cP@G?>D3 zik=2El@67n+ZT?!2CJo*q76vCu&|l?ttd@6xzFn@!hjDv>QWE)i5H$vTzygr2@#^0gzMc~Xv~v<~q;3o0N? zcUt-qJ4(~~YHlfoy8_+f8|1H}0_?%-TnWM)Rz;lX%l5athl=rJ%a)}l!kMI6ta_LK ztp}x87)Dui6J5``esJK5@~6fcjeII-`yQMw>|i*^tTR_ zOJ23QF9@)@L}zrHsnpawu-Qkp27Baw6z#2|4D4J`VBB-w)1r8CU`Gk$iqcExF4&>x z0)MlxAec2S^raa%?f25TCSU}(_;U}SdB@)#-fzaFWzFd6*@sY>JAr5|%DAlMu<4yx z!px=*F^eiC;6$$OMI^RH=GOESN`+BaEBXUAG1u7h#Xg+`11M* zSW@(PVn&Rn3Srf5z4$kW`t_iHF6}bmo?SiSgpt z8NJWhs(Safo`Io?+&8@59YORv+O=wEgwvB#M&~Ax`}IAo=mxDR7164PG%7pl7(-!|J^5KN;PT4{s;A#t)XGqzLU&nl>+#(G1K#hOZN{?6`nwm; zo0!(T>&JbhN>aL>pKY(uk%00ru=&9KBqkQ`rt08aJ?C95t7SJ+Up-rmGF1Wpk z@+A{Z;Gg#p8s7zCwEUB#wI~#;= z(Yv&1s(Bf}A!W4?){s6>t)`dlsuN&5+WCSOADGu&SZ!G!0HP-wvZys6HaY8s;m9nj zkngHV56%Ly5i@zbRf-*QeVozdM`t7MJx5%d*6ETJQeCPf@Ox$2OpyhU z3_Q{xXdGCqe*|K`JdQ5*!{(9+cJ?vI!7au%-06B$OFmWfYu3n8IZo{UNbC5l7QQVAv~p^t9I#$%b-L z@^6fUA91J^CE?zzw%A>zG3MsDktzskHO8_0!=T7hIz0Or!FA+*k6X;^UteRkA3Pqa zuY^#pG%JHN&p8Zl?r=?AXr34LEtA`2dh<)(cH6Rm_&2W*_i{DO@6>dl#|Ld6!*33v zbHyQy+YTFd4Alk_@BZ(z$3WV$$EP?^^YF$3u@4irEU$hBORDZq(aV?Kp?Q8Oj$x7vQE7@FmueZlcxOT9DktU z%0LM#xO!prBVr&IbnPQm zl5yV>U69)_UBT8CXtETpae}JgH6SEkkf}9dn3hRsh`)~-xKvII-e{1@Pbs?qHCL#9 zwqf^~oNw}+kTC?u2Bs#UdZv0jy-EwbMH*c_0+R>Q^mfdJF3GlkF1DDh&Rh^qM{1dJ zb}6usb;i^}2&4QSp@u6yhFj^?4Qqj*82Pm|?lrN& zy&$*8DqBz?qw3<~j&bh`h*gW=YzAcm$;;xq8bk5>8gMS>mmJ|3!T8+Fh!lkc7HsRm z)eRVtNyBr~y&e(MX?UYwKPrH|!|^F?Pi6L?iW=QS770-HCxGPQyRIe3 zr_s^Hfw{K)?s!G_IYL`{{8fGyb{)=Xl#J!=M;F*0eL}EdQl(UR8M3)UCO2m? zAyZcQ@VH9^R8OyT1J}JgAT+Qx|LVsc2B{)|`xNxnlskq+<%i)py$!srjG_8+PUe{B z6*rTw`g&3OeOB{q2yR0NrtQa**(tv6ZBI>&IOL$CwaivH$%%ujhJZ$-N)d}apg52R z*eU^bJCDA=UQ^mR?=)M+2cPK-#O~pb-U@x2xw{;T@ON zIf1WJ0TTB|7UO#J?C%sNS=GOj{>}XBqyB6S-YNO25XDOepF=;DgJ%$(J6hl;G1$~>q-vsm3N%_S}xun*enotl;LV`B2L>eVqAd-l@p{B zSt1*LgCFDXb}cSqH;rNaBgTOR6$MyveGpsFB7q=AvyTHlznv4qRc%>eVh4QIaL|wG z>rxVB_59_^1!;{O)5wDk9xAlf_czZ;+CMc_4O>tya`|*;M*WIiRfOtu8;o1b<^nVl zjZMq+e5BqC@HVZeK45;s!~7h%@{+4f?%R?_y(+6Yxh3uO?Vn=ixF9=bjk{;Gl_qFh z&~6EnoT;4WG3+1VU=#=)XJ%X|09r)vcnj~9xHRse#A|9)fv*rcEbD^G73~}Fxm9%? zRd!ZYKI#GAk(B4plr#1*^>mX@rCy@<6g>KthqlP*N%7O%8^i3P)^1aFyMaiB*di1#@FFnHH|j1=M{TLe{G5hNY$X zLwY)7AV%fsfDjohKNetR(Ws-Qi`Pdi+=>R5ro*bks6dw9U{dUUnrJo$&GlUCxhNqs zHu}n9GqZ>m_64F!kpT|3qdL6_8xWvd1;PDw!ui7sQX1`sYNwL|OZXD|gO8ffy0^61 zD-;xEMBvdtuS{?bj|oDd(?l?rfozbu;FprzAkv(k6RIK?Pd)+fP&}|&h%6n!$er5H zVjLJ6MU(FHis)T#NwM&VU!)A?EYY-mGk$H>SO#Xeoc^l-J!#pnuP&k&w8T5v3%1CW z?KgcoHJKn#8xVU;x;1+}uzEV(a1tx2ktE>0qU`(dCqTwzh?y*L(1*E%)l)v;XN-pg z#NUnIy4f<)ROmoyZC`^ewHas3_M|rZtRt`Ei!Y!;77OHvA@hZVx|v1s6X7P6DR1sY(xpKIdKa0|*3sRTQvH00C3hCXfv~6gka}wGirj$Z~qGWWOm^%DN-fSIWu1_g%f>Fc|J(Wyw4RRf`*mZC$CruX4ZSX5 zYW}RxW=NsGH{RqO&cQ*os8Ec1DqEpcoVOnZGmLLw^<#}|hGhvuxNa9`882&}9-k5~ zQgg)4^WjhV&yt*3^5e?&W*{HaH+#d^nV%uVwzl)Zad?Zq02{E2wMSvSpd zAwOj8itgbQ_-4^o-$}OC8q=Qu+jy4m^(R34ig9bmzh>1~vmH&_gXPsMc9ZZ3e&gY8 zym>c6F0X$ehuIw$<=Hj%r4euD!=mnJ0T+5`WOY3 z9b2E#RMiU@k9om%KgE8(N{dR9xF7 zSe}@Q;%PT0aI1ySCk0#a+n-0T{{&#cY{#E0?2QacyS*Hf{(b&TtN28E^6jh_C7POJ z!bg|Zf&C7sd2jvXM#R99QU+P(JP?)YN7PeP0iPdIhMk+}s;y)daNt5=b?3ZXfy|m(O#sSwDKstObenGUfhn0-S-)jF!RvNU=xf3_A(1lcX9Ruob}DGdhUCJLp0AdKSvO8k zmvFP2%C`eCJ656>!*L@Fvf7Yp>zMb9zlejR_XtY@>#bcoHYPe$(oN38ONWRJX zH*sugk3qJa!DM}G8l__A*rrA>!Mw}?YAUs=o66s2R@F(Ujg~@=rMhUKN>}pG!H9aW zVONFBX6t6NRoVP>Ge;Rq;Fsi1r~^OG-VN2!11iff8clhq;xWljn5!$Ps$vrX`2ct=#iw=*p2gJmoWQZ-E1&Kx1!v*( zDgAK^&cG3^2Ha9vamj*E+opykSP`h2d~4`xvt2~mjfCl`O^(J1erV?wq+g$uOj4Ou z>M5!r`)b_6F)|y_IOrtI104S*#ZIirvst47Z%<*T5_L?jc-MWz=jLE}J^v<`MDe+6{@UBv+e_rzmXv;H zrrFzB_{TmXH9l5&lBL`BCToM}dh=Sm)!4*`0KV&L*PZ&4RVkAO2#fA``0630LSSjP z=CPWhxQX67T@64^XaqFEOix<$(Wt8ypQ7w3ofIcVl$~mXB&(0EbtUP`o#4GB@%?)T z96((%;EaPO6aXz)P8FgmRND16~BA5Mx{u~vcJFoCjr1-HDMMZ9_5WF39R9nbU)G3`JOH-v#tTV6>Kex zWuSSpp~IB{ae3v8(QsuxVWhOj)xJ=L0Nq>pE6vFPD{e$6PE}7U^VXP>R~KD-aafSX zeccA00*3`fSzw9MqQi&k!JJ8TKe%)rvfIV)Z9z#p>L+VLTEOoZpUB8I zf#vk-##tT+|Hz-r0`pvtxvTU%lKul9u|w2QBKB@LSM7gvPa3g!v|~8lXQ3-Zj_l;v z^EUv!jLwCrE;p+PLvUGw`fKNl{Dwbz`EB*f<8VfCdi5SkCl5JwqSwWXcoMKK6KVDF zHvq3J(7Tp0iXl>tEDcPzn}BmtW*x+;nQUQx4T?SO(#^imb3ri)d`Yw;-JLV=xn0fx z{&ift8?^tq#Y(bIViHZ@#OI#?L>9K;_NNTz8gL-gHQUPSEi(~}4GS^=;xs-O+f;d) zn@zWP$Za@w6v(JSWc2NP%AUlgRp_`!#Pn{)vBHKA(>n$l@HK%Vv|D-(s5Ywr!*;(< zMawGOjTeN@Z8dJ!xQZ=2eBz`VGtJy|qPllX_XRBXj1iH{w9i#^bEo)wLF)l8+?NF! zY*Yi}y;I2xrI`gCVOSv6ahB2oT_83x)GK!#9LmEv=sC#@{{&GaoiF}qS<(IWZ3;Eq?oR_m!v zTOA)HGqs!T z^425`6S$^mr2#Du9Td=-=9S&~lx8r0YJYuTo-r7w&Enn6b&GF4QM4AgO7eC0H8!AN zhw3jJLM=x5VNXS&F+~YQGi;a*#pyzyp8zMu+kOIUK;2x(!75h3Dd_f|-E$cSGX9C9Mc8+#dcSG_ymQ%@ zn7m$HzV&S80g2MDsNo4FAffYVzFlc*#nvZ=Uzisvo>@t%OUFidj8dh>KHT%}+wK@~ zEGsF;$;#)&k=UcD3M|!xLi1>nWA7#lLS0=-*p)7cd#e9#lYg8ZP8Fxyo;w zV7UR#uA_IM+%>FBVg&_nJ5dXplSs6Cd3h4r#(0IQR;ymws^vGGNBb?TY)s$_jwWQ5 zBWP^m%|gM z5MlbEaCsYfsqN)l%(mhg%;LJni3e|sP{ZekT2V<~(8@Ly*=m-Nk*Z2(E7JFzYf-Gp z!lYTWNfhYI#ZO#AeZiBFtneP6(qk%x;CgJJ6-4l4kC17VQ?G1ed4JTmN-G-fXlQMY zjbXo7+PkCj=d{j7l^kp;O`eqlcOukz07`dBuIl}QbD ztt|c6RjH5kdv6CjnyCf`SDVplb5FIUA?f-Toajuu=AsIVLVwjG*nNY2mCF2R@{_<4 z(Jy|9Z*R_Q9(`Uz@U@tWg?XKFEmg2H;Eb|#;uz1pbM&iW`m{O>>QzZ0=}Ma=k95 zq677hN-$pMG^LgmYQqY(uwW=M&tQMO9bTMyjw;1omWbd%4i8%D^cQlm$d15fp}xRS z#;{LBZaNv|Re+xBE~4Uea+sY_?mF^A6|iPVf%!xl*X=^tGDk<)i`2bnPayZ=DJR&h z#D`HtpilksP!H5x|G3l0BX5IN|KQ10Ls^o+@Mfy2vI6&M#;D*DWEnzG7$Lo`dEsDU z**2ct)DMM!ZykP_{<{wU!9P_w*o_U0 z>Na^_;$&LhVq&w!c8irr1h7X#zXCoPu6IVyZ4jwfP_A-Fi^aPx89Ebp&6hZpY!1_o zS&B0sjFBCt+U)wM+;j{hJLfi?@K*St4h5M1Di`eeB`MkKy!!HL^5-S`PGB$h%=*L= zurC}x6bF*+>@A!G@24I;YCwV_^aEcCku8}DX<`b`yrn8k5Z6W9K)s77Qo54io{&54 z=9Vp`BPZ3&hdb^q=RP@Io;!{eyVpz&oQu(vWC>Au(R#>u0_eHgBZKqikxlM-?O2Jc+nr6e5nT1ff-6G^Zxtzqi z0%9w1A9UueqRQ>e-k)1~_JFTZf7>zK<3x9%{ZD{8O(u|~vGStBOGf#Jtl8NF40&93 zIbvEaGH7H@>D#`SZ`Q=w?4yCqml_T9-!wMdf|VC_eg&CFzPi;EELwHs+i4V(fANcJ$mVQ;St)AR2CH+~kcPvRO=fqC^SBK&@_54o$o1K=_-=~>+d#z(fr>n)~ z!aZUD#F>BQ=>J~l|8~kb_<6XHaohcrP+JT0i|+c4(=(ygM*yDqA3QVM7?muYQZC?E zALk=JtOWdTrIm{d-IIvbYiQK^aUyBtn)6GREYa=~^GIDy-)wg&uv4d(HlW?Oqe#0@ z|I~wfb&&(Jsz!T=shC-#sOwGH_Byf(or$oe3?vnDo=$W*N2XZEp;I|8f~2-v=~XH5 zQ})P;A7)@~A@SMA*kY>Zo5{u=A@S&3r0np>L)i!Od7MT$giG37q2F(HH@iIU*s*nP ztoA)F#FHqo4@w7P@ZO&Rs~>i7f8XYxPJhPOYuEoAbUhFX`0j8xC3wNznA!OLW+ip0Npru9!Q;OB$-wdjvoUD_L-WRT!ky$&uF79Ye zz%zS1^GCnbM7Qlg?!nxP>QCLj6Ac~l@>sEjm;mQ zL6r#}S6aSr2kUV%NLkm*4PA^k%myOWG(1&TN$@=sW$3HpMiRUNYQh=pkd^ zvPnx^g7Ex~D#(SXY}OH5XDcvx^9EN!;erws)>9m<{3Ay_JaQ$o2E>e2>QWp?>eT~P z9p_6TPso1cUAr@5nV3&Rrr0OoJx#{bAO%=|3tU~8L8$91?XRI`7E&;wIfJ)YHMRG{ zIVWoshWo1%*hReborR2@zm9jm*VKQQ{)1Q^XU`WdE!B~%T{?v(3}7D|lQ7I}w`cv* zvI|NDHapvRAthY_edrb;m%g3rV0y$#@mPNTjT{H%kEnC{-JA3%DF~_>q6Q_a)I~bE z)*CgNhS#9g?OGZ%hL@TTS!@PisRvj#X)i%y3elRz$dvR594TS0JcBFvcagj!hzfJ6 z?(Y7{Zc)j|>3*1FV;6-#fr^BH`%`A~wmnkCDqwnNHa#HXm~_0@kvY}X`jngGAyq7o z9=RYsR;(m`u;)Zth5d!9s3eOPUA(0g7`{$dgpUJZ5grfbNAaSSvGGB@n(p?C_Yll6 zGODwT{MsW>{Gf!xjfss-$;O4%QQHl!%B~p;B7@L?A-3QS59G;FZk1Lk^IfTkQZ>9HcTKc|BdQOXxXnbgsHRre+$vcY&oJD~c^d?;Wi z%DR@B+F+?)Zf#utW4`B6Xa%-7vskhs-fenjSxoLHfU$HvLz*xGjRMlhgr%JEVWUgIufgfWh|m4NjC&&}uNx|dT`*LiXltcYWlA_mYp zGpE+U4SVtSY^AWRm`JsOs0)JPAiG3~vaLu(fq_U%eZ4X;79341F!F@fgCL`4N`@L? z@mDt_?7i zUDk%9O(BFf=gf)4l;@+MGihrTb$f_~NLNP^p(jd`Xq<+u;BWz6h^8Zze;gc`RU}vs zbbIHibP;O63R5^ad3SyJ6q?L@ugQFOi$%B7#;vAql!-_Ux~XtkT+jh8_Ti<_56Tj{ zfd*z%owcs-;H3%Mf__9`uKGgt`9;~2aj^)nwk}ERbkPXHQc|LxZpf)GlXxM&)VzO< zf{TIV=VJx$EpXt(FK|2KTFS?qQanCv4P&AkvI&fg>4{Ff(m146HYIr(e{9kVW)?S* zRQG9#@e|+&qI0UIWa)r}mfGj^B=_OuhWFq^+xj!;Mao=KFJhe?xBF#8lw)}{c?n&X zZejMx4;ty7-;u>#IuBSEZ?EU_$6Hdj%GKmU|WN2~b&+H6Mf z^cMh9I?te$rA#{I;#dYmHWwU7;maeMu_gB^%$gS9_C56pur^VaR=ii}Zb)R`?{(3Q zzcki{tSh$X&Cm%5Mna|au=veUzV6czDW*S!PO&7*ZbevvCg@cq?nU(~8*h~ZP4h^4 z`PVp3houhM)3ot%3zk<%-uvj#H_v^NMVOar<=XeA%nqfMm(fi4{p7vX4m{Iec5?_U zzK~-vducU-ExtXUq^oYW>~QJ2BSEjJz_}$#L^z!98$z60Vv|SWWi8pYBKInamDuKL zUoo({_XFEoz^>nh&;LX7{`2ZTaeKbo6!+aW>J3RJs#)^*n;_Y&~NW1e|I?0n@4r3ovy?m9b>q<+@DRzP{7t3S@Jj(1; z+SGYeG~=0xCbBLhcxNt&PSp>bn@gVQDIbAQvc@vvVsp>Mh=ecdr(gI00PH9D9w7Q2 zN6U_S8;qYW`f*VTR+T)EmxxCn;Ze^Dc_^c5Rn!33r@5Wri(|805Uz3LWlZ%mY!13Y)$?#V_ze%cap~6-BY+Mv(rqr+$lADe>Gj zRI0e{roM1Tw|D3EkYS`LUGRmz>tSCTGxv4jZ*Zqwn1Es!P{Ul3CuXxa%obsXQuoCV z`HQ#u!A66f)q`k-nu=poyJiX9z)b>4jNox7QYlxrXTN!4^u~ zcG609q~kQn+)FRaC#}=481lB^cpqMy`MAqPg?VgD=Yruhu?F)w`(f$`E?sYLQ@1@@ z_!XhMv(Raf`zgn!z6txSVZ+YQzhy zG6DD*Y(7#yUFmRC;DxRai2sYM(d{#vkmxXW`I>H77wHv3Yg~Lb8rW^mPLir*->(+{ z(hj(|svodY;Hbq)GUA=bu*_tM(q)UyLA+&f-mZu-YBg{LZ}VVSMx_O0JXl;NOOnbp zuvp~%^0cHJpD~*h)-w43%Z?~zbn9xlMdf7jYzi#5Lj1RagJSe2wmW-XptkgJL;a{2 z(LI$5Nm{M|3F1_7h_O+QlH$H58^d$JZ5q{;n zRn8^*Xm>fS^8^oNCAWEr1-`8xpoHX^&#^YZ|0&l>;>Z@I&n zMMxUSm?~V|=BW$2;KzpF3(~h9(krbvE#dU}ie6*;UZX>|Vgscr0&|?7^6ZI)cc|fr(zghT?P2^@t zO(Ra03So(bL6KAwino&>Mdv2Zffb3y9$_GQ4wrkJI|>$0ubb}8EZ?p3ptm@{gd%cy zW$RHxwc67Sn{0Q_Qk>GD#TYz6b;T+juOjcb7O0=?YNwT2(2r1-uPlq@GPE~$9g?m3 z2|!5~$F>bd=dl^$eVp{VNM^>>KTxc#&D`9wG~`w!D1@Vn_z%-)WL!9$F-b*daIBC{a4UKIC6Drm4taa&Q(h=+W^BYa^kMlli18z zF9r0O0Yi!>mY=g92c3fXH)GOqr31H*s_ck9AaKQ|b%~cE;;%A~^@afBZ1A$=<0rp9 zw;f`_`IwjpX4AfW-)@YQVz_*mj$(B*jvBDKKz4){TC*rcUJQHaCW{QSSub!vmCw5% z&%&~cM*f0xGzNQacIwNmK0A3Za8Al{Q&33ICRd*rDvblDN|blrzs6$PL^N3XyN(Sw zR{^Aimp(n)#oco=y!OBNz3JMh5%dCMcsJ?pm3a&sdEdfkyB3fwrDLJvj{OY8@*7)) z>+Qva;=p)whY&I$Z=wG=v>)aqV8o9WmZkTr6)(9W+kzFo*vAZWNZQ4TMZpn#7gdJ4 z`g;i8$8}XsmS#)nfFcKdsE%5sL{jVjiGq(s|Q2nX3_{uTmxfoqH$M&oi}gRT5bR$CdK(( zy46uBRs9uC^^+MzJok6M{Q6{BKM8ZirxY9Lojfww$OGwpYP2m()=XJGA{feZg&a3I z!}j*#f5jjI>~O-LfEow}Prf^#;=Ug9_XVs`#0pEo2OudS-p%`A{Jk8&ye_uN?e7aj zJl8t_V7C~LY+l*J6aIcXTLpv1|5G7cly*7;@C`Pfw>_q%mY&?b8N0Ysy8}?cQXl%t zJQ@AQNfd>Db*CMX1O5kQ;_uP#p#XqP@&IoO8V%#lVZ^@_xOjbF1V%p85qHkXNkNkF zcA|LCTmB{#!_F`;QH0;(OGRE*PVviOHdABMY>0G|=jqG>%WiTVY--O0e`V^8u;UUm zPqAzQl(c7Tb4S`Ct5wulL(f-UR4_1MRkU5;BvPbsfNnMzmB;i-GE{#ma0GQ*y4{yO z&fmFT%Q7oz<&GPxMgag8okRcThfwM_}=iqHzh&Uv-vb5NH{!6h7Ygn;G$tW4vo7)>r7}bPfIhR+_&7DrX%S z$HLl+8{^HLtu{p1u8jHLR$tZNs=*qNrY>E+lnKuLRB+8hxf18K92cyVGs^$enMrh{ zI6#{U?0Ysf_{}F79P-}wrd~IPJs><17IcvF9MBOXx|9`vKnGWLjIDJXOd?@ZZVrKs z@me6)Oh(8LSrww;Y4JKn(70B>go5{RTLNP>byd)03xV#iG^->+2n1NYS89_&$`N%;Fm=b3bs)70Yz`?ge{KeltK$HD> zuRWdSXtDP)W0wbF!+9itMJ6aQaani6EStMFf*OxL0luzy^zCo7?rm)7w?7V1jq*=1 z-~PUC!}0GoOgxiBM;5xfatOZ;+DCi^izGoL%9rD;`B|yoez~3x|M7y1TWmXVD>pvacMx9Rb=?2`4*r*PhraLh$LWqg zD&Xw=&T!Z_x*U;?&{wUljA$7bZB3_~_3reSRQj>Y&r1LIL>1ZFxcMO&$O zZj;z--?Dy_LQmjE&pIW_ z;=okdnHGy$jMducs~S)Dx3nb`I$t*ULeUZl;^TUyTj2muS@Heg_=GYQ*j{+N5#fPrR8N&iv>uF0T&#C;)0~7eX4l!y42|Hup0H~ zbo=1krVlAED9TY)bOr5^OL&b(jEAH7qv3eic>>KP#2F`vo@?^7xtlFsVhj9|Ng_c@ z7I>p`-gXoQ!-kb**Ro@H91cAh0P8i0H8s6rW%p~7NpuN;DMI!P7~!u~f_%+KY8yIV zO!6G-^n^9?rwPCwG%dc#O0uFYsl>+Dw4pYYUA>}V?@Rhqf!rz>Tv?O&pUY&wHVM`mHgeV4WFXWD@` zr}`Jy-~~Y1s}R&#qqzHJtWab88_?49BL(5s!Tz9lR6neA0poqR2{Y4*L63rulA?*+ zr48pX0wF&0)z6wp3wv*DK@Fjl%)Ut3_S;!VOx~1o%rG|9bf0Qm)}*?LhgB(=tHv*IMz)^5dR_igRdZD#mE zktnVqw~{sf-B$0buX_}oaFlfAUNA0I3uX|Ew4vJ zqI=N(8@ngLJbyr7{c@1Mpo+4vSS(osQ1XC9e188`<_Y;;13BVFM{=Iw9V$L901r>W=IZY6@!c79vx}Q~$UUYQLAZl`$K8 zLpT1%laf)XD#&o-N8kwb%?6{IREzTk?}+~Kc#uJk<=p|8 z6EK+|TZJ)|z2!m(ysZ`tqk`9Y;|M+ren;}NOVin9WiANzCn{~&rBp2GY-=Qx;yewg z)h*q!+=SK??&V|jBhd;UyJ&oTbFh$kTmQbdgG2O+DgxM`inKu)m8Fay^k?d?cy@Kd zug$J2Ct4^9B=wp&L_Q_7nBEtXm_EMU%C4XE=4OT0^gZM2MBCQwe$VK(Cp}~x7MQnm zUWa^DSy}JWw3qR6G2`q&QXG%_piRaD}@QSW9N_M@lu9DV|j4laAF zXvUJ10-`4_7%VOV8y2Z=@`l7X|%j*HBvWl#uWR7Q7Eau6?6%7X+n9rjeX=|%)&9bdKR$Ply_>-?ta`pHbkd8)OJ za{T@Gq=x)L^-o&RROQG#C1w0=q@6L`$&rJs1TzD%u-cd{qw=!$8!1D|Qi+$eu=N(*Ks|kzR?%!eh|9#}!$+)K z&CjdnU&}56O$lC1IKiYF;{=Po;=pn4b?~vsf!?IKH)#VG!-SNolL+7=BnzGw=PwyY zxVr~4KDMFQcIFpK%=xqvT>UGm&A}=!J`anuY?RN&Ug|!^Wl*2{D4B{RL z-;6^yt8X7W|6>MVA36!%Elzi{bYEPNztPtfcGWe)Z#MmsLwx62NYAc}$fnsqO($VP z{k|ZLiY4)AYA zN0w%K9`HVsf&*GE#kcY)#yB226AvTx%AuW@0aSC#AqiuvU9&15M%BTnwC+~J&gwm7 z77Qv$!vY5^^UAC5HbH6|xp1gWAG_?!XbRcgHM)F+~IiI)Gg` zj=H%;E{`XMyLed9uFGB-e9C_&RR~ybsfBV1nx1-fNv6|vzijx;w$q`%spZq&Vv>qr zNn8`%FhO_vCqVa?tllX`^~bAmIpYRgsd_qRBbqU3TsF@|Sm@f#7nG3OUu^qt$T;bH ziCd^*le5_)FuA1qddNzo|6GfQSOsHVM#R9>w}&EPtR`ep6p^4hE|(5B$Xj+`7FMH- zzwNH6xm~y*6Fo^O6hjI`$TH8Gh-pJyp=&7tGU>|)YCz6 zNLAvmd-9gSWoNXgWE9JH285=FCLvdD43CA;^A_feiG-G}v?fV(+#Fv_bU-8}>5X4H zi=|<@76yaHncRpA!enjS#6-+Zbq9AipZ+yawDVv70?~gt5Z;MeA32cqFXlb}H%mbK zo40r85{~`OulxO`CRs&ZlW%nS7V@u(M!(=i>K9(lpO3Q(@j@|sm2dEd&jUAnrfq9}iX`#;V7HiQg2qF-?OsMRb%T5& zuVxH()kT4xFiX*YDKj(NR+7LyXHK&Mu40e1-WM~J&5qQUx}P>r?wX>ZAs$Kr3k~VE zKg>60BGs&whOzaau0jd0@qOUHM?ulzoRRd0v(rDWJX=${N^;fdpEC8HpQU>)A$w7n zNnlK_Jx`c4=t517v+Ro00a7LZYF<4_)f1-^7x5NTx_PdN9C@sPLt@<0SYRn|ZKYJQ z%F?(WtyyL8qHT7hWuo6&Dt$J!S{bpb*KLAr#8`Y}Widz*#X&VyRdRdk)8;1I@yK=e zEmLu=)edaSKDeD!{?j6L z<432aYQRWRV6;8Sv116T+NyM}EE<$k0#SsSLk)9z z_;uCidRiqRgz67%6~ZOKmy0$bHR+!Cf!e8rbX%7#M=L`I@x&N^%L@JU^RAwF^TFBT zuDJnYEQatQRq%#d@P0WRjbk(D0x9hq>M8!Tr-gC7j5br`hR^2vy2R@Neh; zCjtn6d4j*tWBQ*!$NuHH{$}C-|KNV9n7`0H3jJl}?+gF`gZq7b{CVzA*6E)&`8P{` z`^71wKV<y z;;hH~3@Xv)HMNri?oK}Twh6#FZ)Yb8&@GCNdb~rpK}r7NuE1mc9Vd4xo~cO0$Hm1! zZ`ue0FuQ(p;J@Mn{ATrUiryx;WT_J5Nt2LyZqMEquhe^mTg8~gOw&-#zQM5zDK zzW*6gvx9p>9;TXlu||VE;U!B{^84>E!8PAk{(k!3@gw~H9RFeNKT*^W^fxHf z@feg`XccDfAOnpdO6U}xr}MRP7uIj4T_Jj(qs*l#DyHQ^4De#vx!y8GYSxD@`QOyo zJ@IRe{3V0yudn)lX3X)|$Nu}J-@f)`A|H}ceeC?WV%ZsDPxpthk-Wan2d5tFHgVi+ zx-Qc!%0A@M^n@quE8626vrPO^F+Gs*0u%?2gwZ9#A^CUIZJ?&@IS5W8&=(iOkVE!| zDwjn($wnTQaSGPQMBJ7Ta*IXcyMDgO29e*3r@EG@z9J2 z4xxAr$A{6inBqE*=$vsb4ZM0T$_<9+H>#B!exfrNIWEYrZprsL@8Ylb z@u!BbG`PnKx~#2xD8GrmiL(0G!B3Y@S$B;&*FLXkEo7y~z|(a~2ein*^wba(n`>Vm z7@_Z}Se;2y^`>3Wy=;#bK!G{WDQd_oM=LWpR2#;OE1{4H##(}+(6GoEUj>DIorD^> z)^qRGlvshkre!wsw!#BjxX~yBN@2JyiV$PM+%|rWYwR#R|C@ z6159bg0b%u(DR`m6ZdUNV2xwcNG~JSj`*!}T%dmtRN0bxdHDL?Shw-{!X0(7#Z`d+ z`l?5!28wZd*XkKLv*im{4*T!D_qM53Us6E(*w<^hx%Yv!29}pgj^?6pGzxKX9pJ~4 zGYxzjrwq+4okp>c>aWJ0G@dwSChzCo=1|-tv339&1&jTrTyMXz4qP7eA;fA5z9dN0YB~$M_TiH1dTNMvrSR=^Vgq zeD-)LJau>%W3sL`W?w#0`MCYb3!!`4c;@uOn=`$^ac`gRZO`~1qez3lev;@mq*~2; z0TB3DoyTh1d&%t6@dTL zt-SYgR=Lh~H#aV&YRCJGj|Y`qF$wzepHr)(KLgfJ`e~?c9ev~ftfwp5%z;%${ddMz z$I8(SQZHY61j>JWtT8CeJ}uf~Dgw+|YllHE28ODO)2oJ=GTC0ZGbu*G9}e%2Zs=Q% zXnfgc9ObZUja4m=g(cO2P1mnA!N+5mtXjV_8j$lzzvW8zKre>4%r+Am^aTbR!sr9Z z(AFcupAG{`Nisu6c~H#(6MJ9WtD8{<9-%Tl4u=fs(EUA~1ct;V~q9^(7 z20xbT$j87<@9g?O5(^7uJqA@mqfqxd)GAU{PbFt>CEIkR+C$Z5NdaJS{gqMa9g7z~ zqGHtdit(fbL>)kpy94813V*ol?(2%?U6ks~dC{g2dw^{H@p)YBI-d~@L(Ch0ES#M0 zQ;azUw74+_4=OJNKFF&?Z=FY`LHh@Kz)stU4TUL|w7ya$- zvwskk{iWq%Df#aV;|BtYvUx^Lbqt3?48sd2Zl72OQh04u@tw#gb!A0o``8$+ z?X}~5x>#v0D@U%!sRIY3eof1YwG{p2x;b-xV}t3&%a1ns=I5-`b4z1mXe#+EqbzvK zyY9{ARG?gGj%e7NR$D8Be7VV)K)%prrQ3Creq??OJZv^Yf;radWbnK|TkndqPZw)5 zGi2XwF;7(jPKQs~)boaiku@T(*>#I%P9IMX52MWDDHqqQUiN;)lRSf9hQ#Pwt4g*_ z^E#$GTZV^m?Vq`PJDvq*Ubs6sy|KYyVWz~EOL}Ybr6wMBi(_Lr6gKf@kKnnJRlGp2 z9BpM;`BtZFbsoP4HG6Jk=r?uzuC5;%?JYg*!Lo|K9`vLY_$` diff --git a/lib/client.js b/lib/client.js index c5cf0b0..ec02abc 100644 --- a/lib/client.js +++ b/lib/client.js @@ -40,7 +40,7 @@ __export(index_exports, { name: () => name }); module.exports = __toCommonJS(index_exports); -var React4 = __toESM(require("react"), 1); +var React5 = __toESM(require("react"), 1); // node_modules/@xmanrui/dsh-dingtalk/plugin-src/client/api.js var DINGTALK_RPC_CHANNEL = "/dingtalk"; @@ -50,9 +50,7 @@ var DINGTALK_ENDPOINTS = Object.freeze({ pollProvisioning: "provision.poll", cancelProvisioning: "provision.cancel", reconnectBot: "bot.reconnect", - deleteBot: "bot.delete", - approveSender: "bot.sender.approve", - revokeSender: "bot.sender.revoke" + deleteBot: "bot.delete" }); var ACCOUNT_STATES = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]); var SNAPSHOT_STATES = /* @__PURE__ */ new Set(["disconnected", "offline", "provisioning", "connected", "degraded"]); @@ -157,54 +155,6 @@ function normalizeProvisioning(value, now = Date.now()) { if (error) result.error = error; return result; } -function normalizeConversationType(value) { - if (value === "group" || value === "2") return "group"; - return "direct"; -} -function normalizePendingSender(value) { - if (!isRecord(value)) return void 0; - const requestId = opaqueId(value.requestId); - if (!requestId) return void 0; - return { - requestId, - displayName: optionalString(value.displayName, 80) ?? "\u5F85\u6279\u51C6\u4F7F\u7528\u8005", - senderIdMasked: optionalString(value.senderIdMasked, 120) ?? "\u8EAB\u4EFD\u5DF2\u9690\u85CF", - conversationType: normalizeConversationType(value.conversationType), - requestedAt: timestamp(value.requestedAt) - }; -} -function normalizeApprovedSender(value) { - if (!isRecord(value)) return void 0; - const senderKey = opaqueId(value.senderKey); - if (!senderKey) return void 0; - return { - senderKey, - displayName: optionalString(value.displayName, 80) ?? "\u5DF2\u6279\u51C6\u4F7F\u7528\u8005", - senderIdMasked: optionalString(value.senderIdMasked, 120) ?? "\u8EAB\u4EFD\u5DF2\u9690\u85CF", - approvedAt: timestamp(value.approvedAt) - }; -} -function uniqueBy(items, key) { - const seen = /* @__PURE__ */ new Set(); - return items.filter((item) => { - if (!item || seen.has(item[key])) return false; - seen.add(item[key]); - return true; - }); -} -function normalizeSenders(value) { - const source = isRecord(value) ? value : {}; - return { - pending: uniqueBy( - (Array.isArray(source.pending) ? source.pending : []).map(normalizePendingSender), - "requestId" - ), - approved: uniqueBy( - (Array.isArray(source.approved) ? source.approved : []).map(normalizeApprovedSender), - "senderKey" - ) - }; -} function normalizeBot(value) { if (!isRecord(value)) return void 0; const botId = opaqueId(value.botId); @@ -215,7 +165,6 @@ function normalizeBot(value) { const state = connected ? "connected" : reportedState === "connected" ? "connecting" : reportedState; const health = isRecord(value.health) ? value.health : {}; const stats = isRecord(value.stats) ? value.stats : {}; - const senderSource = value.senders ?? value.senderPolicy ?? value.access; return { botId, state, @@ -235,7 +184,6 @@ function normalizeBot(value) { messagesReceived: nonNegativeInteger(stats.messagesReceived), messagesReplied: nonNegativeInteger(stats.messagesReplied) }, - senders: normalizeSenders(senderSource), error: normalizeError(value.error, "DINGTALK_ACCOUNT_ERROR", "\u9489\u9489\u8FDE\u63A5\u5C1A\u672A\u5C31\u7EEA") ?? null }; } @@ -257,8 +205,7 @@ function normalizeSnapshot(value) { bots, totals: { configured: bots.length, - connected: bots.filter((bot) => bot.connected).length, - pendingApproval: bots.reduce((total, bot) => total + bot.senders.pending.length, 0) + connected: bots.filter((bot) => bot.connected).length }, provisioning: source.provisioning ? normalizeProvisioning(source.provisioning) : null }; @@ -307,7 +254,6 @@ var CSS = String.raw` .ddt-tools, .ddt-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; } .ddt-tools { justify-content: flex-end; } .ddt-badge { min-height: 30px; display: inline-flex; align-items: center; gap: 7px; padding: 0 11px; border-radius: 999px; color: var(--dsw-alias-label-secondary, #646a73); background: var(--dsw-alias-fill-secondary, #f2f3f5); font-size: 12px; white-space: nowrap; } -.ddt-badge[data-tone="attention"] { color: #ad6800; background: #fff7e6; } .ddt-dot { width: 8px; height: 8px; flex: none; border-radius: 50%; background: #aeb3bb; } .ddt-dot[data-tone="success"] { background: var(--ddt-success); box-shadow: 0 0 0 3px color-mix(in srgb, var(--ddt-success) 14%, transparent); } .ddt-dot[data-tone="warning"] { background: var(--ddt-warning); } @@ -368,23 +314,10 @@ var CSS = String.raw` .ddt-accountIdentity h3 { overflow: hidden; font-size: 15px; text-overflow: ellipsis; white-space: nowrap; } .ddt-accountIdentity p { margin-top: 4px; color: var(--dsw-alias-label-secondary, #646a73); font: 12px ui-monospace, SFMono-Regular, monospace; } .ddt-health { display: inline-flex; align-items: center; gap: 7px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; white-space: nowrap; } -.ddt-metrics { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; margin: 20px 0; } -.ddt-metric { padding: 12px 14px; border-radius: 9px; background: var(--dsw-alias-fill-tertiary, #f7f8fa); } +.ddt-metrics { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; margin: 20px 0; } +.ddt-metric { min-width: 0; padding: 12px; border-radius: 9px; background: var(--dsw-alias-fill-tertiary, #f7f8fa); } .ddt-metric dt { color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; } .ddt-metric dd { overflow: hidden; margin: 5px 0 0; font-size: 13px; text-overflow: ellipsis; white-space: nowrap; } -.ddt-access { margin-top: 18px; padding: 15px 16px; border: 1px solid var(--dsw-alias-line-divider, #eef0f3); border-radius: 11px; background: color-mix(in srgb, var(--ddt-accent-wash) 35%, var(--dsw-alias-bg-body, #fff)); } -.ddt-accessHeader { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; } -.ddt-accessHeader h4 { font-size: 13px; } -.ddt-accessHeader p { margin-top: 4px; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; line-height: 1.5; } -.ddt-accessCount { flex: none; padding: 3px 8px; border-radius: 99px; color: var(--dsw-alias-label-secondary, #646a73); background: var(--dsw-alias-fill-secondary, #f2f3f5); font-size: 11px; } -.ddt-accessCount[data-pending="true"] { color: #ad6800; background: #fff1d6; } -.ddt-senderList { display: grid; gap: 8px; margin: 12px 0 0; padding: 0; list-style: none; } -.ddt-sender { min-height: 42px; display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 8px 10px; border-radius: 8px; background: var(--dsw-alias-bg-body, #fff); } -.ddt-senderIdentity { min-width: 0; } -.ddt-senderIdentity strong { display: block; overflow: hidden; font-size: 12px; font-weight: 600; text-overflow: ellipsis; white-space: nowrap; } -.ddt-senderIdentity span { display: block; overflow: hidden; margin-top: 3px; color: var(--dsw-alias-label-tertiary, #8f959e); font: 10px ui-monospace, SFMono-Regular, monospace; text-overflow: ellipsis; white-space: nowrap; } -.ddt-approvedLabel { margin-top: 13px; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; font-weight: 600; } -.ddt-noSenders { margin-top: 11px !important; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; } .ddt-accountFooter { display: flex; align-items: center; justify-content: space-between; gap: 15px; padding-top: 16px; border-top: 1px solid var(--dsw-alias-line-divider, #eef0f3); } .ddt-summary { color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; } .ddt-confirm { padding: 18px 24px; border-top: 1px solid color-mix(in srgb, var(--ddt-error) 25%, transparent); background: color-mix(in srgb, var(--ddt-error) 5%, transparent); } @@ -394,12 +327,17 @@ var CSS = String.raw` .ddt-visuallyHidden { position: absolute !important; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; } @keyframes ddt-spin { to { transform: rotate(360deg); } } @container (max-width: 680px) { + .ddt-heading { flex-direction: column; align-items: stretch; } + .ddt-tools { width: 100%; flex-wrap: nowrap; gap: 6px; } + .ddt-tools .ddt-badge { min-height: 34px; padding-inline: 8px; } + .ddt-tools .ddt-button { flex: none; padding-inline: 10px; white-space: nowrap; } .ddt-empty { grid-template-columns: minmax(0, 1fr); } .ddt-brandMark { display: none; } .ddt-qrLayout { grid-template-columns: minmax(0, 1fr); justify-items: center; gap: 24px; } .ddt-qrColumn { width: 100%; min-width: 0; } .ddt-qrCopy { width: 100%; } - .ddt-metrics { grid-template-columns: minmax(0, 1fr); } + .ddt-metrics { gap: 8px; } + .ddt-metric { padding: 10px; } } @media (max-width: 720px) { .ddt-heading, .ddt-accountTop, .ddt-accountFooter { flex-direction: column; align-items: stretch; } @@ -409,9 +347,7 @@ var CSS = String.raw` .ddt-brandMark { display: none; } .ddt-qrLayout { grid-template-columns: minmax(0, 1fr); justify-items: center; } .ddt-qrCopy { width: 100%; } - .ddt-metrics { grid-template-columns: minmax(0, 1fr); } .ddt-cardBody { padding: 20px; } - .ddt-sender { align-items: flex-start; } } @media (prefers-reduced-motion: reduce) { .ddt-page *, .ddt-page *::before, .ddt-page *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; } @@ -480,11 +416,6 @@ function Heading({ totals, adding, busy, onAdd, addButtonRef }) { }), h("span", null, `${totals.connected} / ${totals.configured} \u5728\u7EBF`) ) : null, - totals.pendingApproval > 0 ? h( - "div", - { className: "ddt-badge", "data-tone": "attention" }, - `${totals.pendingApproval} \u4E2A\u5F85\u6279\u51C6` - ) : null, h("div", { className: "ddt-badge", title: "\u5E94\u7528\u5BC6\u94A5\u53EA\u5199\u5165 Harness Host \u51ED\u636E\u670D\u52A1\uFF0C\u4E0D\u4F1A\u53D1\u9001\u5230\u6D4F\u89C8\u5668" @@ -670,70 +601,6 @@ function checkedTime(value) { return "\u521A\u521A"; } } -function SenderAccess({ account, busy, onApprove, onRevoke }) { - const pending = account.senders.pending; - const approved = account.senders.approved; - return h( - "section", - { className: "ddt-access", "aria-label": `${account.bot.name}\u4F7F\u7528\u8005\u6743\u9650` }, - h( - "div", - { className: "ddt-accessHeader" }, - h( - "div", - null, - h("h4", null, "\u5141\u8BB8\u4F7F\u7528\u673A\u5668\u4EBA\u7684\u9489\u9489\u8D26\u53F7"), - h("p", null, "\u65B0\u4F7F\u7528\u8005\u9996\u6B21\u53D1\u6D88\u606F\u540E\u4F1A\u51FA\u73B0\u5728\u8FD9\u91CC\uFF0C\u987B\u5728\u672C\u673A\u660E\u786E\u6279\u51C6\u3002") - ), - h("span", { - className: "ddt-accessCount", - "data-pending": pending.length > 0 ? "true" : void 0 - }, pending.length > 0 ? `${pending.length} \u4E2A\u5F85\u6279\u51C6` : `${approved.length} \u4E2A\u5DF2\u6279\u51C6`) - ), - pending.length > 0 ? h("ul", { className: "ddt-senderList" }, pending.map((sender) => h( - "li", - { - className: "ddt-sender", - key: sender.requestId - }, - h( - "div", - { className: "ddt-senderIdentity" }, - h("strong", null, sender.displayName), - h("span", null, `${sender.senderIdMasked} \xB7 ${sender.conversationType === "group" ? "\u7FA4\u804A" : "\u5355\u804A"}`) - ), - h(Button, { - kind: "primary", - onClick: () => onApprove(sender), - disabled: Boolean(busy) - }, busy === `approve:${sender.requestId}` ? "\u6279\u51C6\u4E2D\u2026" : "\u6279\u51C6\u4F7F\u7528") - ))) : null, - approved.length > 0 ? h( - React.Fragment, - null, - h("div", { className: "ddt-approvedLabel" }, "\u5DF2\u6279\u51C6"), - h("ul", { className: "ddt-senderList" }, approved.map((sender) => h( - "li", - { - className: "ddt-sender", - key: sender.senderKey - }, - h( - "div", - { className: "ddt-senderIdentity" }, - h("strong", null, sender.displayName), - h("span", null, sender.senderIdMasked) - ), - h(Button, { - kind: "quiet", - onClick: () => onRevoke(sender), - disabled: Boolean(busy) - }, busy === `revoke:${sender.senderKey}` ? "\u64A4\u9500\u4E2D\u2026" : "\u64A4\u9500") - ))) - ) : null, - pending.length === 0 && approved.length === 0 ? h("p", { className: "ddt-noSenders" }, "\u5C1A\u65E0\u4F7F\u7528\u8BF7\u6C42\u3002\u673A\u5668\u4EBA\u6536\u5230\u7B2C\u4E00\u6761\u6D88\u606F\u540E\uFF0C\u53EF\u5728\u6B64\u6279\u51C6\u53D1\u9001\u8005\u3002") : null - ); -} function RemoveConfirmation({ account, busy, onConfirm, onCancel }) { const cancelRef = React.useRef(null); React.useEffect(() => cancelRef.current?.focus(), []); @@ -748,7 +615,7 @@ function RemoveConfirmation({ account, busy, onConfirm, onCancel }) { } }, h("strong", null, `\u4ECE DeepSeek Harness \u79FB\u9664\u201C${account.bot.name}\u201D\uFF1F`), - h("p", null, "\u8FD9\u4F1A\u505C\u6B62\u6D88\u606F\u8FDE\u63A5\uFF0C\u5E76\u5220\u9664\u672C\u673A\u4FDD\u5B58\u7684\u5E94\u7528\u51ED\u636E\u3001\u673A\u5668\u4EBA\u914D\u7F6E\u3001\u4F1A\u8BDD\u6620\u5C04\u53CA\u4F7F\u7528\u8005\u6279\u51C6\u8BB0\u5F55\u3002\u9489\u9489\u5F00\u653E\u5E73\u53F0\u4E2D\u7684\u673A\u5668\u4EBA\u4E0D\u4F1A\u88AB\u81EA\u52A8\u5220\u9664\u3002"), + h("p", null, "\u8FD9\u4F1A\u505C\u6B62\u6D88\u606F\u8FDE\u63A5\uFF0C\u5E76\u5220\u9664\u672C\u673A\u4FDD\u5B58\u7684\u5E94\u7528\u51ED\u636E\u3001\u673A\u5668\u4EBA\u914D\u7F6E\u53CA\u4F1A\u8BDD\u6620\u5C04\u3002\u9489\u9489\u5F00\u653E\u5E73\u53F0\u4E2D\u7684\u673A\u5668\u4EBA\u4E0D\u4F1A\u88AB\u81EA\u52A8\u5220\u9664\u3002"), h( "div", { className: "ddt-actions" }, @@ -768,9 +635,7 @@ function AccountCard({ onReconnect, onRequestRemove, onConfirmRemove, - onCancelRemove, - onApprove, - onRevoke + onCancelRemove }) { const state = busy === "reconnect" ? "connecting" : account.state; const tone = account.connected ? "success" : state === "error" ? "error" : "warning"; @@ -824,7 +689,6 @@ function AccountCard({ h("dd", null, checkedTime(account.health.lastCheckedAt)) ) ), - h(SenderAccess, { account, busy, onApprove, onRevoke }), h( "div", { className: "ddt-accountFooter" }, @@ -873,14 +737,12 @@ function AccountList(props) { onReconnect: () => props.onReconnect(account), onRequestRemove: () => props.onRequestRemove(account), onConfirmRemove: () => props.onConfirmRemove(account), - onCancelRemove: props.onCancelRemove, - onApprove: (sender) => props.onApprove(account, sender), - onRevoke: (sender) => props.onRevoke(account, sender) + onCancelRemove: props.onCancelRemove }) ))) ); } -var EMPTY_TOTALS = Object.freeze({ configured: 0, connected: 0, pendingApproval: 0 }); +var EMPTY_TOTALS = Object.freeze({ configured: 0, connected: 0 }); function DingtalkSettingsTab({ rpcCall }) { const [model, setModel] = React.useState({ phase: "loading", @@ -896,19 +758,67 @@ function DingtalkSettingsTab({ rpcCall }) { const [notice, setNotice] = React.useState(""); const [now, setNow] = React.useState(() => Date.now()); const addButtonRef = React.useRef(null); + const mountedRef = React.useRef(true); + const statusRequestRef = React.useRef(0); + const noticeFrameRef = React.useRef(null); + const focusFrameRef = React.useRef(null); + React.useEffect(() => { + mountedRef.current = true; + return () => { + mountedRef.current = false; + statusRequestRef.current += 1; + if (noticeFrameRef.current !== null) { + window.cancelAnimationFrame(noticeFrameRef.current); + noticeFrameRef.current = null; + } + if (focusFrameRef.current !== null) { + window.cancelAnimationFrame(focusFrameRef.current); + focusFrameRef.current = null; + } + }; + }, []); React.useEffect(() => installDingtalkStyles(), []); const announce = React.useCallback((message) => { + if (!mountedRef.current) return; + if (noticeFrameRef.current !== null) { + window.cancelAnimationFrame(noticeFrameRef.current); + noticeFrameRef.current = null; + } setNotice(""); - if (message) window.requestAnimationFrame(() => setNotice(message)); + if (message) { + noticeFrameRef.current = window.requestAnimationFrame(() => { + noticeFrameRef.current = null; + if (mountedRef.current) setNotice(message); + }); + } + }, []); + const focusAddButton = React.useCallback(() => { + if (!mountedRef.current) return; + if (focusFrameRef.current !== null) window.cancelAnimationFrame(focusFrameRef.current); + focusFrameRef.current = window.requestAnimationFrame(() => { + focusFrameRef.current = null; + if (mountedRef.current) addButtonRef.current?.focus(); + }); }, []); const invoke = React.useCallback(async (endpoint, payload = {}, signal) => { if (typeof rpcCall !== "function") throw new TypeError("\u9489\u9489\u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5"); return unwrapRpcResult(await rpcCall(endpoint, payload, signal)); }, [rpcCall]); - const loadStatus = React.useCallback(async ({ signal, silent = false } = {}) => { - if (!silent) setModel((current) => ({ ...current, phase: "loading", error: null })); + const loadStatus = React.useCallback(async ({ + signal, + silent = false, + restoreProvisioning = false + } = {}) => { + if (!mountedRef.current || signal?.aborted) return void 0; + const requestId = statusRequestRef.current + 1; + statusRequestRef.current = requestId; + const canCommit = () => mountedRef.current && !signal?.aborted && statusRequestRef.current === requestId; + if (!silent && canCommit()) { + setModel((current) => ({ ...current, phase: "loading", error: null })); + } try { const snapshot = normalizeSnapshot(await invoke(DINGTALK_ENDPOINTS.status, {}, signal)); + if (!canCommit()) return void 0; setModel({ phase: "ready", bots: snapshot.bots, @@ -916,7 +826,7 @@ function DingtalkSettingsTab({ rpcCall }) { revision: snapshot.revision, error: null }); - if (snapshot.provisioning) { + if (restoreProvisioning && snapshot.provisioning) { setProvision((current) => !current || current.attemptId === snapshot.provisioning.attemptId ? { ...current, ...snapshot.provisioning, @@ -925,7 +835,7 @@ function DingtalkSettingsTab({ rpcCall }) { } return snapshot; } catch (error) { - if (error?.name === "AbortError") return void 0; + if (error?.name === "AbortError" || !canCommit()) return void 0; setModel((current) => ({ ...current, phase: silent && current.phase === "ready" ? "ready" : "error", @@ -936,7 +846,7 @@ function DingtalkSettingsTab({ rpcCall }) { }, [invoke]); React.useEffect(() => { const controller = new AbortController(); - void loadStatus({ signal: controller.signal }); + void loadStatus({ signal: controller.signal, restoreProvisioning: true }); return () => controller.abort(); }, [loadStatus]); React.useEffect(() => { @@ -944,9 +854,13 @@ function DingtalkSettingsTab({ rpcCall }) { const controller = new AbortController(); let running = false; const timer = window.setInterval(async () => { - if (running) return; + if (running || controller.signal.aborted || !mountedRef.current) return; running = true; - await loadStatus({ signal: controller.signal, silent: true }); + await loadStatus({ + signal: controller.signal, + silent: true, + restoreProvisioning: false + }); running = false; }, 15e3); return () => { @@ -956,22 +870,27 @@ function DingtalkSettingsTab({ rpcCall }) { }, [loadStatus, model.phase]); React.useEffect(() => { if (!provision || !ACTIVE_PROVISION_STATES.has(provision.status)) return void 0; - const timer = window.setInterval(() => setNow(Date.now()), 1e3); + const timer = window.setInterval(() => { + if (mountedRef.current) setNow(Date.now()); + }, 1e3); return () => window.clearInterval(timer); }, [provision?.attemptId, provision?.status]); const startProvisioning = React.useCallback(async ({ replace = false } = {}) => { + if (!mountedRef.current) return; setBusy(true); try { if (replace && provision?.attemptId) { await invoke(DINGTALK_ENDPOINTS.cancelProvisioning, { attemptId: provision.attemptId }); + if (!mountedRef.current) return; } setProvision({ status: "starting" }); const started = normalizeProvisioning(await invoke( DINGTALK_ENDPOINTS.beginProvisioning, { locale: "zh-CN" } )); + if (!mountedRef.current) return; if (!started.qrCodeDataUrl) { throw new Error("\u9489\u9489\u626B\u7801\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u5B89\u5168\u7684\u4E8C\u7EF4\u7801"); } @@ -982,70 +901,95 @@ function DingtalkSettingsTab({ rpcCall }) { }); announce("\u9489\u9489\u4E8C\u7EF4\u7801\u5DF2\u751F\u6210\uFF0C\u8BF7\u4F7F\u7528\u9489\u9489 App \u626B\u63CF\u3002"); } catch (error) { + if (!mountedRef.current) return; setProvision({ attemptId: provision?.attemptId, status: "failed", error: presentError(error) }); } finally { - setBusy(false); + if (mountedRef.current) setBusy(false); } }, [announce, invoke, provision?.attemptId]); const cancelProvisioning = React.useCallback(async () => { + if (!mountedRef.current) return; setBusy(true); try { if (provision?.attemptId && !["failed", "expired", "cancelled"].includes(provision.status)) { await invoke(DINGTALK_ENDPOINTS.cancelProvisioning, { attemptId: provision.attemptId }); + if (!mountedRef.current) return; } setProvision(null); announce("\u5DF2\u53D6\u6D88\u9489\u9489\u673A\u5668\u4EBA\u63A5\u5165\u3002"); - window.requestAnimationFrame(() => addButtonRef.current?.focus()); + focusAddButton(); } catch (error) { + if (!mountedRef.current) return; setProvision((current) => ({ ...current, status: "failed", error: presentError(error) })); } finally { - setBusy(false); + if (mountedRef.current) setBusy(false); } - }, [announce, invoke, provision?.attemptId, provision?.status]); + }, [announce, focusAddButton, invoke, provision?.attemptId, provision?.status]); React.useEffect(() => { const attemptId = provision?.attemptId; if (!attemptId || !ACTIVE_PROVISION_STATES.has(provision.status)) return void 0; const controller = new AbortController(); - let timer; + let disposed = false; + let timer = null; + const canCommit = () => !disposed && !controller.signal.aborted && mountedRef.current; + const schedule = (delay) => { + if (!canCommit()) return; + if (timer !== null) window.clearTimeout(timer); + timer = window.setTimeout(() => { + timer = null; + if (canCommit()) void poll(); + }, delay); + }; const poll = async () => { try { - const result = normalizeProvisioning(await invoke( + const response = await invoke( DINGTALK_ENDPOINTS.pollProvisioning, { attemptId }, controller.signal - )); + ); + if (!canCommit()) return; + const result = normalizeProvisioning(response); if (result.status === "connected") { - const snapshot = await loadStatus({ signal: controller.signal, silent: true }); + const snapshot = await loadStatus({ + signal: controller.signal, + silent: true, + restoreProvisioning: false + }); + if (!canCommit()) return; const account = result.botId ? snapshot?.bots.find((bot) => bot.botId === result.botId) : snapshot?.bots.find((bot) => bot.connected); if (!account?.connected) { setProvision((current) => current?.attemptId === attemptId ? { ...current, ...result, status: "connecting" } : current); - timer = window.setTimeout(poll, result.pollIntervalMs); + schedule(result.pollIntervalMs); return; } setProvision(null); announce(result.alreadyConnected ? "\u8FD9\u4E2A\u9489\u9489\u673A\u5668\u4EBA\u5DF2\u7ECF\u63A5\u5165\u5E76\u4FDD\u6301\u5728\u7EBF\u3002" : "\u9489\u9489\u673A\u5668\u4EBA\u5DF2\u63A5\u5165\uFF0C\u53EF\u4EE5\u5F00\u59CB\u53D1\u9001\u6D88\u606F\u3002"); return; } + if (!canCommit()) return; setProvision((current) => current?.attemptId === attemptId ? { ...current, ...result, durationMs: current.durationMs } : current); if (ACTIVE_PROVISION_STATES.has(result.status)) { - timer = window.setTimeout(poll, result.pollIntervalMs); + schedule(result.pollIntervalMs); } } catch (error) { - if (error?.name === "AbortError") return; + if (error?.name === "AbortError" || !canCommit()) return; setProvision((current) => current?.attemptId === attemptId ? { ...current, status: "failed", error: presentError(error) } : current); } }; - timer = window.setTimeout(poll, provision.pollIntervalMs ?? 3e3); + schedule(provision.pollIntervalMs ?? 3e3); return () => { + disposed = true; controller.abort(); - window.clearTimeout(timer); + if (timer !== null) window.clearTimeout(timer); + timer = null; }; }, [announce, invoke, loadStatus, provision?.attemptId, provision?.pollIntervalMs, provision?.status]); const setBotBusy = React.useCallback((botId, operation) => { + if (!mountedRef.current) return; setBusyByBot((current) => { const next = { ...current }; if (operation) next[botId] = operation; @@ -1054,17 +998,21 @@ function DingtalkSettingsTab({ rpcCall }) { }); }, []); const runBotAction = React.useCallback(async ({ account, operation, endpoint, payload, success }) => { + if (!mountedRef.current) return void 0; setBotBusy(account.botId, operation); try { await invoke(endpoint, payload); - const snapshot = await loadStatus({ silent: true }); + if (!mountedRef.current) return void 0; + const snapshot = await loadStatus({ silent: true, restoreProvisioning: false }); + if (!mountedRef.current) return void 0; announce(typeof success === "function" ? success(snapshot) : success); return snapshot; } catch (error) { + if (!mountedRef.current) return void 0; announce(`\u64CD\u4F5C\u5931\u8D25\uFF1A${presentError(error).message}`); return void 0; } finally { - setBotBusy(account.botId, null); + if (mountedRef.current) setBotBusy(account.botId, null); } }, [announce, invoke, loadStatus, setBotBusy]); const reconnect = React.useCallback((account) => runBotAction({ @@ -1082,22 +1030,8 @@ function DingtalkSettingsTab({ rpcCall }) { payload: { botId: account.botId, confirm: true }, success: "\u9489\u9489\u673A\u5668\u4EBA\u53CA\u672C\u673A\u51ED\u636E\u5DF2\u79FB\u9664\u3002" }); - if (snapshot) setRemoveTarget(null); + if (snapshot && mountedRef.current) setRemoveTarget(null); }, [runBotAction]); - const approve = React.useCallback((account, sender) => runBotAction({ - account, - operation: `approve:${sender.requestId}`, - endpoint: DINGTALK_ENDPOINTS.approveSender, - payload: { botId: account.botId, requestId: sender.requestId, confirm: true }, - success: `\u5DF2\u6279\u51C6 ${sender.displayName} \u4F7F\u7528\u8FD9\u4E2A\u673A\u5668\u4EBA\u3002` - }), [runBotAction]); - const revoke = React.useCallback((account, sender) => runBotAction({ - account, - operation: `revoke:${sender.senderKey}`, - endpoint: DINGTALK_ENDPOINTS.revokeSender, - payload: { botId: account.botId, senderKey: sender.senderKey, confirm: true }, - success: `\u5DF2\u64A4\u9500 ${sender.displayName} \u7684\u4F7F\u7528\u6743\u9650\u3002` - }), [runBotAction]); let provisionView = null; if (provision?.status === "starting") { provisionView = h( @@ -1162,16 +1096,14 @@ function DingtalkSettingsTab({ rpcCall }) { onReconnect: (account) => void reconnect(account), onRequestRemove: (account) => setRemoveTarget(account.botId), onConfirmRemove: (account) => void remove(account), - onCancelRemove: () => setRemoveTarget(null), - onApprove: (account, sender) => void approve(account, sender), - onRevoke: (account, sender) => void revoke(account, sender) + onCancelRemove: () => setRemoveTarget(null) }) : null ) ); } // plugin-src/client/channels/feishu/index.js -var React2 = __toESM(require("react"), 1); +var React3 = __toESM(require("react"), 1); // plugin-src/client/channels/feishu/api.js var FEISHU_RPC_CHANNEL = "/feishu"; @@ -1387,6 +1319,87 @@ function formatRemaining2(milliseconds) { return `${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`; } +// plugin-src/client/lifecycle.js +var React2 = __toESM(require("react"), 1); +function createPollScheduler({ setTimeoutFn, clearTimeoutFn }) { + let disposed = false; + let timer; + return { + get disposed() { + return disposed; + }, + schedule(callback, delayMs) { + if (disposed) return false; + if (timer !== void 0) clearTimeoutFn(timer); + timer = setTimeoutFn(() => { + timer = void 0; + if (!disposed) void callback(); + }, delayMs); + return true; + }, + dispose() { + if (disposed) return; + disposed = true; + if (timer !== void 0) clearTimeoutFn(timer); + timer = void 0; + } + }; +} +function createAnimationFrameScheduler({ requestFrame, cancelFrame }) { + let disposed = false; + const frames = /* @__PURE__ */ new Set(); + const keyedFrames = /* @__PURE__ */ new Map(); + return { + schedule(callback, key) { + if (disposed) return false; + const previous = key === void 0 ? void 0 : keyedFrames.get(key); + if (previous !== void 0) { + keyedFrames.delete(key); + frames.delete(previous); + cancelFrame(previous); + } + let frame; + let completed = false; + frame = requestFrame(() => { + completed = true; + if (frame !== void 0) frames.delete(frame); + if (key !== void 0 && keyedFrames.get(key) === frame) keyedFrames.delete(key); + if (!disposed) callback(); + }); + if (!completed) { + frames.add(frame); + if (key !== void 0) keyedFrames.set(key, frame); + } + return true; + }, + dispose() { + if (disposed) return; + disposed = true; + for (const frame of frames) cancelFrame(frame); + frames.clear(); + keyedFrames.clear(); + } + }; +} +function useAnimationFrameScheduler() { + const schedulerRef = React2.useRef(null); + React2.useEffect(() => { + const scheduler = createAnimationFrameScheduler({ + requestFrame: (callback) => window.requestAnimationFrame(callback), + cancelFrame: (frame) => window.cancelAnimationFrame(frame) + }); + schedulerRef.current = scheduler; + return () => { + scheduler.dispose(); + if (schedulerRef.current === scheduler) schedulerRef.current = null; + }; + }, []); + return React2.useCallback( + (callback, key) => schedulerRef.current?.schedule(callback, key) ?? false, + [] + ); +} + // plugin-src/client/channels/feishu/styles.js var FEISHU_STYLE_ID = "beihuixinghe-dsh-feishu-settings"; var CSS2 = String.raw` @@ -1914,7 +1927,7 @@ function installFeishuStyles() { } // plugin-src/client/channels/feishu/index.js -var h2 = React2.createElement; +var h2 = React3.createElement; function SvgIcon({ children, size = 18, className, viewBox = "0 0 24 24" }) { return h2("svg", { width: size, @@ -2033,7 +2046,7 @@ function QrIcon({ size = 58 }) { fill: "currentColor" })); } -var Button3 = React2.forwardRef(function Button4({ children, kind = "secondary", size, icon, className = "", ...props }, ref) { +var Button3 = React3.forwardRef(function Button4({ children, kind = "secondary", size, icon, className = "", ...props }, ref) { return h2("button", { ...props, ref, @@ -2147,13 +2160,13 @@ function safeQrSource2(value) { return /^data:image\/(?:png|webp|svg\+xml)(?:;charset=[^;,]+)?;base64,/i.test(value) ? value : void 0; } function QrPane({ provision, now, onRefresh, onCancel, busy }) { - const [imageFailed, setImageFailed] = React2.useState(false); + const [imageFailed, setImageFailed] = React3.useState(false); const qrSource = safeQrSource2(provision.qrCodeDataUrl); const href = safeVerificationHref(provision.verificationUrl); const remaining = Math.max(0, provision.expiresAt - now); const expired = provision.expired === true || remaining === 0; const progress = Math.min(1, remaining / Math.max(1, provision.durationMs ?? remaining)); - React2.useEffect(() => setImageFailed(false), [qrSource]); + React3.useEffect(() => setImageFailed(false), [qrSource]); return h2( "div", { className: "bxf-card bxf-provisionCard" }, @@ -2318,11 +2331,11 @@ function formatCheckedTime(timestamp3) { } } function RemoveConfirmation2({ bot, busy, onConfirm, onCancel }) { - const cancelRef = React2.useRef(null); + const cancelRef = React3.useRef(null); const idPart = bot.botId.replace(/[^a-zA-Z0-9_-]/g, "-"); const titleId = `bxf-remove-title-${idPart}`; const descriptionId = `bxf-remove-description-${idPart}`; - React2.useEffect(() => cancelRef.current?.focus(), []); + React3.useEffect(() => cancelRef.current?.focus(), []); return h2( "div", { @@ -2528,8 +2541,30 @@ function PageError({ error, onRetry, busy }) { ); } var EMPTY_TOTALS2 = Object.freeze({ configured: 0, connected: 0 }); +function mergeFeishuSnapshotState(current, snapshot, { restoreProvisioning = false, now = Date.now() } = {}) { + if (snapshot.revision > 0 && current.revision > snapshot.revision) return current; + let provisioning = current.provisioning; + if (!provisioning && restoreProvisioning && snapshot.provisioning) { + provisioning = { + phase: snapshot.state === "connecting" ? "connecting" : "qr", + ...snapshot.provisioning, + durationMs: Math.max(1, snapshot.provisioning.expiresAt - now), + expired: snapshot.provisioning.expiresAt <= now + }; + } + return { + ...current, + phase: "ready", + revision: snapshot.revision, + bots: snapshot.bots, + totals: snapshot.totals, + provisioning, + pageError: null, + statusError: null + }; +} function FeishuSettingsTab({ rpcCall }) { - const [model, setModel] = React2.useState({ + const [model, setModel] = React3.useState({ phase: "loading", revision: 0, bots: [], @@ -2538,77 +2573,68 @@ function FeishuSettingsTab({ rpcCall }) { pageError: null, statusError: null }); - const [pageBusy, setPageBusy] = React2.useState(false); - const [provisionBusy, setProvisionBusy] = React2.useState(false); - const [busyByBot, setBusyByBot] = React2.useState({}); - const [errorsByBot, setErrorsByBot] = React2.useState({}); - const [removeTargetId, setRemoveTargetId] = React2.useState(null); - const [announcement, setAnnouncement] = React2.useState(""); - const [now, setNow] = React2.useState(() => Date.now()); - const [focusBotId, setFocusBotId] = React2.useState(null); - const cardRefs = React2.useRef(/* @__PURE__ */ new Map()); - const removeButtonRefs = React2.useRef(/* @__PURE__ */ new Map()); - const addButtonRef = React2.useRef(null); - const announce = React2.useCallback((message) => { + const [pageBusy, setPageBusy] = React3.useState(false); + const [provisionBusy, setProvisionBusy] = React3.useState(false); + const [busyByBot, setBusyByBot] = React3.useState({}); + const [errorsByBot, setErrorsByBot] = React3.useState({}); + const [removeTargetId, setRemoveTargetId] = React3.useState(null); + const [announcement, setAnnouncement] = React3.useState(""); + const [now, setNow] = React3.useState(() => Date.now()); + const [focusBotId, setFocusBotId] = React3.useState(null); + const cardRefs = React3.useRef(/* @__PURE__ */ new Map()); + const removeButtonRefs = React3.useRef(/* @__PURE__ */ new Map()); + const addButtonRef = React3.useRef(null); + const scheduleAnimationFrame = useAnimationFrameScheduler(); + const announce = React3.useCallback((message) => { setAnnouncement(""); - if (!message) return; - window.requestAnimationFrame(() => setAnnouncement(message)); - }, []); - const invoke = React2.useCallback(async (endpoint, payload = {}, signal) => { + scheduleAnimationFrame(() => { + if (message) setAnnouncement(message); + }, "announcement"); + }, [scheduleAnimationFrame]); + const invoke = React3.useCallback(async (endpoint, payload = {}, signal) => { return unwrapRpcResult2(await rpcCall(endpoint, payload, signal)); }, [rpcCall]); - const mergeSnapshot = React2.useCallback((snapshot, { restoreProvisioning = true } = {}) => { - setModel((current) => { - if (snapshot.revision > 0 && current.revision > snapshot.revision) return current; - let provisioning = current.provisioning; - if (!provisioning && restoreProvisioning && snapshot.provisioning) { - provisioning = { - phase: snapshot.state === "connecting" ? "connecting" : "qr", - ...snapshot.provisioning, - durationMs: Math.max(1, snapshot.provisioning.expiresAt - Date.now()), - expired: snapshot.provisioning.expiresAt <= Date.now() - }; - } - return { - ...current, - phase: "ready", - revision: snapshot.revision, - bots: snapshot.bots, - totals: snapshot.totals, - provisioning, - pageError: null, - statusError: null - }; - }); + const mergeSnapshot = React3.useCallback((snapshot, { restoreProvisioning = false } = {}) => { + const now2 = Date.now(); + setModel((current) => mergeFeishuSnapshotState( + current, + snapshot, + { restoreProvisioning, now: now2 } + )); }, []); - const loadStatus = React2.useCallback(async ({ signal, silent = false, restoreProvisioning = true } = {}) => { + const loadStatus = React3.useCallback(async ({ signal, silent = false, restoreProvisioning = false } = {}) => { if (!silent) setPageBusy(true); try { const snapshot = normalizeBotsSnapshot(await invoke(FEISHU_ENDPOINTS.status, {}, signal)); + if (signal?.aborted) return void 0; mergeSnapshot(snapshot, { restoreProvisioning }); return snapshot; } catch (error) { - if (error?.name === "AbortError") return void 0; + if (signal?.aborted || error?.name === "AbortError") return void 0; const presented = presentError2(error); setModel((current) => current.phase === "loading" || !silent ? { ...current, phase: "error", pageError: presented } : { ...current, statusError: presented }); return void 0; } finally { - if (!silent) setPageBusy(false); + if (!silent && !signal?.aborted) setPageBusy(false); } }, [invoke, mergeSnapshot]); - React2.useEffect(() => { + React3.useEffect(() => { const controller = new AbortController(); - void loadStatus({ signal: controller.signal }); + void loadStatus({ signal: controller.signal, restoreProvisioning: true }); return () => controller.abort(); }, [loadStatus]); - React2.useEffect(() => { + React3.useEffect(() => { if (model.phase !== "ready") return void 0; const controller = new AbortController(); let inFlight = false; const timer = window.setInterval(async () => { if (inFlight) return; inFlight = true; - await loadStatus({ signal: controller.signal, silent: true }); + await loadStatus({ + signal: controller.signal, + silent: true, + restoreProvisioning: false + }); inFlight = false; }, 15e3); return () => { @@ -2616,7 +2642,7 @@ function FeishuSettingsTab({ rpcCall }) { window.clearInterval(timer); }; }, [loadStatus, model.phase]); - React2.useEffect(() => { + React3.useEffect(() => { if (!focusBotId) return; const node = cardRefs.current.get(focusBotId); if (!node) return; @@ -2624,7 +2650,7 @@ function FeishuSettingsTab({ rpcCall }) { node.focus({ preventScroll: true }); setFocusBotId(null); }, [focusBotId, model.bots]); - const startProvisioning = React2.useCallback(async ({ replace = false } = {}) => { + const startProvisioning = React3.useCallback(async ({ replace = false } = {}) => { setProvisionBusy(true); announce(""); const previousAttemptId = model.provisioning?.attemptId; @@ -2662,7 +2688,7 @@ function FeishuSettingsTab({ rpcCall }) { setProvisionBusy(false); } }, [announce, invoke, model.provisioning?.attemptId]); - const cancelProvisioning = React2.useCallback(async () => { + const cancelProvisioning = React3.useCallback(async () => { const attemptId = model.provisioning?.attemptId; setProvisionBusy(true); try { @@ -2670,7 +2696,7 @@ function FeishuSettingsTab({ rpcCall }) { setModel((current) => ({ ...current, provisioning: null })); announce("\u5DF2\u53D6\u6D88\u6DFB\u52A0\u673A\u5668\u4EBA\u3002"); await loadStatus({ silent: true, restoreProvisioning: false }); - window.requestAnimationFrame(() => addButtonRef.current?.focus()); + scheduleAnimationFrame(() => addButtonRef.current?.focus(), "focus"); } catch (error) { setModel((current) => ({ ...current, @@ -2679,22 +2705,25 @@ function FeishuSettingsTab({ rpcCall }) { } finally { setProvisionBusy(false); } - }, [announce, invoke, loadStatus, model.provisioning?.attemptId]); - React2.useEffect(() => { - const provision2 = model.provisioning; - if (!provision2 || provision2.phase !== "qr" || provision2.expired) return void 0; + }, [announce, invoke, loadStatus, model.provisioning?.attemptId, scheduleAnimationFrame]); + const countdownAttemptId = model.provisioning?.attemptId; + const countdownPhase = model.provisioning?.phase; + const countdownExpiresAt = model.provisioning?.expiresAt; + const countdownExpired = model.provisioning?.expired; + React3.useEffect(() => { + if (!countdownAttemptId || countdownPhase !== "qr" || countdownExpired) return void 0; const tick = () => { const timestamp3 = Date.now(); setNow(timestamp3); - if (timestamp3 >= provision2.expiresAt) { - setModel((current) => current.provisioning?.attemptId === provision2.attemptId ? { ...current, provisioning: { ...current.provisioning, expired: true } } : current); + if (timestamp3 >= countdownExpiresAt) { + setModel((current) => current.provisioning?.attemptId === countdownAttemptId ? { ...current, provisioning: { ...current.provisioning, expired: true } } : current); } }; tick(); const timer = window.setInterval(tick, 1e3); return () => window.clearInterval(timer); - }, [model.provisioning]); - React2.useEffect(() => { + }, [countdownAttemptId, countdownPhase, countdownExpiresAt, countdownExpired]); + React3.useEffect(() => { const provision2 = model.provisioning; if (!provision2 || !["qr", "connecting"].includes(provision2.phase) || !provision2.attemptId || provision2.expired) return void 0; const controller = new AbortController(); @@ -2758,7 +2787,7 @@ function FeishuSettingsTab({ rpcCall }) { window.clearTimeout(timer); }; }, [announce, invoke, loadStatus, model.provisioning]); - const setBotBusy = React2.useCallback((botId, value) => { + const setBotBusy = React3.useCallback((botId, value) => { setBusyByBot((current) => { const next = { ...current }; if (value) next[botId] = value; @@ -2766,7 +2795,7 @@ function FeishuSettingsTab({ rpcCall }) { return next; }); }, []); - const setBotError = React2.useCallback((botId, error) => { + const setBotError = React3.useCallback((botId, error) => { setErrorsByBot((current) => { const next = { ...current }; if (error) next[botId] = presentError2(error); @@ -2774,7 +2803,7 @@ function FeishuSettingsTab({ rpcCall }) { return next; }); }, []); - const reconnectOneBot = React2.useCallback(async (connection) => { + const reconnectOneBot = React3.useCallback(async (connection) => { const { botId, bot } = connection; setBotBusy(botId, "reconnect"); setBotError(botId, null); @@ -2797,15 +2826,15 @@ function FeishuSettingsTab({ rpcCall }) { setBotBusy(botId, null); } }, [announce, invoke, mergeSnapshot, setBotBusy, setBotError]); - const requestRemove = React2.useCallback((connection) => { + const requestRemove = React3.useCallback((connection) => { setRemoveTargetId(connection.botId); }, []); - const cancelRemove = React2.useCallback(() => { + const cancelRemove = React3.useCallback(() => { const botId = removeTargetId; setRemoveTargetId(null); - window.requestAnimationFrame(() => removeButtonRefs.current.get(botId)?.focus()); - }, [removeTargetId]); - const confirmRemove = React2.useCallback(async (connection) => { + scheduleAnimationFrame(() => removeButtonRefs.current.get(botId)?.focus(), "focus"); + }, [removeTargetId, scheduleAnimationFrame]); + const confirmRemove = React3.useCallback(async (connection) => { const { botId, bot } = connection; setBotBusy(botId, "delete"); setBotError(botId, null); @@ -2825,14 +2854,14 @@ function FeishuSettingsTab({ rpcCall }) { }); announce(`${bot.name}\u5DF2\u4ECE\u6B64 DeepSeek Harness \u79FB\u9664\uFF1B\u98DE\u4E66\u5F00\u653E\u5E73\u53F0\u4E2D\u7684\u5E94\u7528\u672A\u88AB\u5220\u9664\u3002`); await loadStatus({ silent: true }); - window.requestAnimationFrame(() => addButtonRef.current?.focus()); + scheduleAnimationFrame(() => addButtonRef.current?.focus(), "focus"); } catch (error) { setBotError(botId, error); announce(`${bot.name}\u79FB\u9664\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002`); } finally { setBotBusy(botId, null); } - }, [announce, invoke, loadStatus, setBotBusy, setBotError]); + }, [announce, invoke, loadStatus, scheduleAnimationFrame, setBotBusy, setBotError]); const provision = model.provisioning; let provisionContent = null; if (provision?.phase === "creating") { @@ -2859,11 +2888,11 @@ function FeishuSettingsTab({ rpcCall }) { busy: provisionBusy }); } - const setCardRef = React2.useCallback((botId, node) => { + const setCardRef = React3.useCallback((botId, node) => { if (node) cardRefs.current.set(botId, node); else cardRefs.current.delete(botId); }, []); - const setRemoveButtonRef = React2.useCallback((botId, node) => { + const setRemoveButtonRef = React3.useCallback((botId, node) => { if (node) removeButtonRefs.current.set(botId, node); else removeButtonRefs.current.delete(botId); }, []); @@ -2895,7 +2924,7 @@ function FeishuSettingsTab({ rpcCall }) { onRetry: () => void loadStatus(), busy: pageBusy }) : h2( - React2.Fragment, + React3.Fragment, null, provisionContent, model.bots.length === 0 && !provision ? h2(EmptyView2, { onStart: () => void startProvisioning(), busy: provisionBusy }) : null, @@ -2916,7 +2945,7 @@ function FeishuSettingsTab({ rpcCall }) { } // plugin-src/client/channels/weixin/index.js -var React3 = __toESM(require("react"), 1); +var React4 = __toESM(require("react"), 1); // plugin-src/client/channels/weixin/api.js var WEIXIN_RPC_CHANNEL = "/weixin"; @@ -3179,7 +3208,7 @@ function installWeixinStyles() { } // plugin-src/client/channels/weixin/index.js -var h3 = React3.createElement; +var h3 = React4.createElement; function WeixinIcon({ size = 26 }) { return h3( "svg", @@ -3208,7 +3237,7 @@ function WeixinIcon({ size = 26 }) { h3("circle", { cx: "16.3", cy: "13.2", r: ".9", fill: "white" }) ); } -var Button5 = React3.forwardRef(function Button6({ children, kind = "secondary", className = "", ...props }, ref) { +var Button5 = React4.forwardRef(function Button6({ children, kind = "secondary", className = "", ...props }, ref) { return h3("button", { ...props, ref, @@ -3285,14 +3314,14 @@ function EmptyView3({ onStart, busy }) { ); } function QrPanel2({ provision, now, busy, onRefresh, onCancel }) { - const [imageFailed, setImageFailed] = React3.useState(false); + const [imageFailed, setImageFailed] = React4.useState(false); const source = safeQrSource3(provision.qrCodeDataUrl); const href = safeVerificationUrl(provision.verificationUrl); const remaining = Math.max(0, provision.expiresAt - now); const expired = remaining === 0 || provision.status === "expired"; const duration = Math.max(1, provision.durationMs ?? 5 * 6e4); const progress = Math.round(Math.min(1, remaining / duration) * 100); - React3.useEffect(() => setImageFailed(false), [source]); + React4.useEffect(() => setImageFailed(false), [source]); return h3( "div", { className: "dxw-card" }, @@ -3359,9 +3388,9 @@ function QrPanel2({ provision, now, busy, onRefresh, onCancel }) { ); } function VerificationPanel({ provision, busy, onSubmit, onCancel }) { - const [code, setCode] = React3.useState(""); + const [code, setCode] = React4.useState(""); const valid = /^\d{4,8}$/.test(code); - React3.useEffect(() => setCode(""), [provision.attemptId]); + React4.useEffect(() => setCode(""), [provision.attemptId]); return h3( "div", { className: "dxw-card" }, @@ -3554,32 +3583,49 @@ function AccountList2(props) { ); } var EMPTY_TOTALS3 = Object.freeze({ configured: 0, connected: 0 }); +function mergeWeixinProvisioningSnapshot(current, incoming, { restoreProvisioning = false } = {}) { + if (!incoming || !current && !restoreProvisioning) return current; + if (current && current.attemptId !== incoming.attemptId) return current; + return { + ...current, + ...incoming, + durationMs: current?.durationMs ?? 5 * 6e4 + }; +} function WeixinSettingsTab({ rpcCall }) { - const [model, setModel] = React3.useState({ + const [model, setModel] = React4.useState({ phase: "loading", bots: [], totals: EMPTY_TOTALS3, revision: 0, error: null }); - const [provision, setProvision] = React3.useState(null); - const [busy, setBusy] = React3.useState(false); - const [busyByBot, setBusyByBot] = React3.useState({}); - const [removeTarget, setRemoveTarget] = React3.useState(null); - const [notice, setNotice] = React3.useState(""); - const [now, setNow] = React3.useState(() => Date.now()); - const addButtonRef = React3.useRef(null); - const announce = React3.useCallback((value) => { + const [provision, setProvision] = React4.useState(null); + const [busy, setBusy] = React4.useState(false); + const [busyByBot, setBusyByBot] = React4.useState({}); + const [removeTarget, setRemoveTarget] = React4.useState(null); + const [notice, setNotice] = React4.useState(""); + const [now, setNow] = React4.useState(() => Date.now()); + const addButtonRef = React4.useRef(null); + const scheduleAnimationFrame = useAnimationFrameScheduler(); + const announce = React4.useCallback((value) => { setNotice(""); - if (value) window.requestAnimationFrame(() => setNotice(value)); - }, []); - const invoke = React3.useCallback(async (endpoint, payload = {}, signal) => { + scheduleAnimationFrame(() => { + if (value) setNotice(value); + }, "announcement"); + }, [scheduleAnimationFrame]); + const invoke = React4.useCallback(async (endpoint, payload = {}, signal) => { return unwrapRpcResult3(await rpcCall(endpoint, payload, signal)); }, [rpcCall]); - const loadStatus = React3.useCallback(async ({ signal, silent = false } = {}) => { + const loadStatus = React4.useCallback(async ({ + signal, + silent = false, + restoreProvisioning = false + } = {}) => { if (!silent) setModel((current) => ({ ...current, phase: "loading", error: null })); try { const snapshot = normalizeSnapshot2(await invoke(WEIXIN_ENDPOINTS.status, {}, signal)); + if (signal?.aborted) return void 0; setModel({ phase: "ready", bots: snapshot.bots, @@ -3588,11 +3634,15 @@ function WeixinSettingsTab({ rpcCall }) { error: null }); if (snapshot.provisioning) { - setProvision((current) => !current || current.attemptId === snapshot.provisioning.attemptId ? { ...current, ...snapshot.provisioning, durationMs: current?.durationMs ?? 5 * 6e4 } : current); + setProvision((current) => mergeWeixinProvisioningSnapshot( + current, + snapshot.provisioning, + { restoreProvisioning } + )); } return snapshot; } catch (error) { - if (error?.name === "AbortError") return void 0; + if (signal?.aborted || error?.name === "AbortError") return void 0; setModel((current) => ({ ...current, phase: silent && current.phase === "ready" ? "ready" : "error", @@ -3601,19 +3651,23 @@ function WeixinSettingsTab({ rpcCall }) { return void 0; } }, [invoke]); - React3.useEffect(() => { + React4.useEffect(() => { const controller = new AbortController(); - void loadStatus({ signal: controller.signal }); + void loadStatus({ signal: controller.signal, restoreProvisioning: true }); return () => controller.abort(); }, [loadStatus]); - React3.useEffect(() => { + React4.useEffect(() => { if (model.phase !== "ready") return void 0; const controller = new AbortController(); let running = false; const timer = window.setInterval(async () => { if (running) return; running = true; - await loadStatus({ signal: controller.signal, silent: true }); + await loadStatus({ + signal: controller.signal, + silent: true, + restoreProvisioning: false + }); running = false; }, 15e3); return () => { @@ -3621,12 +3675,12 @@ function WeixinSettingsTab({ rpcCall }) { window.clearInterval(timer); }; }, [loadStatus, model.phase]); - React3.useEffect(() => { + React4.useEffect(() => { if (!provision || !["pending", "scanned"].includes(provision.status)) return void 0; const timer = window.setInterval(() => setNow(Date.now()), 1e3); return () => window.clearInterval(timer); }, [provision?.attemptId, provision?.status]); - const startProvisioning = React3.useCallback(async ({ replace = false } = {}) => { + const startProvisioning = React4.useCallback(async ({ replace = false } = {}) => { setBusy(true); try { if (replace && provision?.attemptId) { @@ -3647,7 +3701,7 @@ function WeixinSettingsTab({ rpcCall }) { setBusy(false); } }, [announce, invoke, provision?.attemptId]); - const cancelProvisioning = React3.useCallback(async () => { + const cancelProvisioning = React4.useCallback(async () => { setBusy(true); try { if (provision?.attemptId && !["failed", "expired", "cancelled"].includes(provision.status)) { @@ -3655,14 +3709,14 @@ function WeixinSettingsTab({ rpcCall }) { } setProvision(null); announce("\u5DF2\u53D6\u6D88\u5FAE\u4FE1\u7ED1\u5B9A\u3002"); - window.requestAnimationFrame(() => addButtonRef.current?.focus()); + scheduleAnimationFrame(() => addButtonRef.current?.focus(), "focus"); } catch (error) { setProvision((current) => ({ ...current, status: "failed", error: presentError3(error) })); } finally { setBusy(false); } - }, [announce, invoke, provision?.attemptId, provision?.status]); - const submitVerification = React3.useCallback(async (verifyCode) => { + }, [announce, invoke, provision?.attemptId, provision?.status, scheduleAnimationFrame]); + const submitVerification = React4.useCallback(async (verifyCode) => { if (!provision?.attemptId) return; setBusy(true); try { @@ -3678,11 +3732,14 @@ function WeixinSettingsTab({ rpcCall }) { setBusy(false); } }, [announce, invoke, provision?.attemptId]); - React3.useEffect(() => { + React4.useEffect(() => { const attemptId = provision?.attemptId; if (!attemptId || !["pending", "scanned", "connecting"].includes(provision.status)) return void 0; const controller = new AbortController(); - let timer; + const scheduler = createPollScheduler({ + setTimeoutFn: (callback, delayMs) => window.setTimeout(callback, delayMs), + clearTimeoutFn: (timer) => window.clearTimeout(timer) + }); const poll = async () => { try { const result = normalizeProvisioning3(await invoke( @@ -3690,12 +3747,18 @@ function WeixinSettingsTab({ rpcCall }) { { attemptId }, controller.signal )); + if (scheduler.disposed) return; if (result.status === "connected") { - const snapshot = await loadStatus({ signal: controller.signal, silent: true }); + const snapshot = await loadStatus({ + signal: controller.signal, + silent: true, + restoreProvisioning: false + }); + if (scheduler.disposed) return; const account = snapshot?.bots.find((bot) => bot.botId === result.botId); if (!account?.connected) { setProvision((current) => current?.attemptId === attemptId ? { ...current, ...result, status: "connecting" } : current); - timer = window.setTimeout(poll, result.pollIntervalMs); + scheduler.schedule(poll, result.pollIntervalMs); return; } setProvision(null); @@ -3704,20 +3767,20 @@ function WeixinSettingsTab({ rpcCall }) { } setProvision((current) => current?.attemptId === attemptId ? { ...current, ...result, durationMs: current.durationMs } : current); if (["pending", "scanned", "connecting"].includes(result.status)) { - timer = window.setTimeout(poll, result.pollIntervalMs); + scheduler.schedule(poll, result.pollIntervalMs); } } catch (error) { - if (error?.name === "AbortError") return; + if (scheduler.disposed || error?.name === "AbortError") return; setProvision((current) => current?.attemptId === attemptId ? { ...current, status: "failed", error: presentError3(error) } : current); } }; - timer = window.setTimeout(poll, provision.pollIntervalMs ?? 1e3); + scheduler.schedule(poll, provision.pollIntervalMs ?? 1e3); return () => { + scheduler.dispose(); controller.abort(); - window.clearTimeout(timer); }; }, [announce, invoke, loadStatus, provision?.attemptId, provision?.status, provision?.pollIntervalMs]); - const setBotBusy = React3.useCallback((botId, value) => { + const setBotBusy = React4.useCallback((botId, value) => { setBusyByBot((current) => { const next = { ...current }; if (value) next[botId] = value; @@ -3725,7 +3788,7 @@ function WeixinSettingsTab({ rpcCall }) { return next; }); }, []); - const reconnect = React3.useCallback(async (account) => { + const reconnect = React4.useCallback(async (account) => { setBotBusy(account.botId, "reconnect"); try { const snapshot = normalizeSnapshot2(await invoke(WEIXIN_ENDPOINTS.reconnectBot, { botId: account.botId })); @@ -3738,7 +3801,7 @@ function WeixinSettingsTab({ rpcCall }) { setBotBusy(account.botId, null); } }, [announce, invoke, setBotBusy]); - const remove = React3.useCallback(async (account) => { + const remove = React4.useCallback(async (account) => { setBotBusy(account.botId, "delete"); try { const snapshot = normalizeSnapshot2(await invoke(WEIXIN_ENDPOINTS.deleteBot, { @@ -3809,7 +3872,7 @@ function WeixinSettingsTab({ rpcCall }) { h3(Button5, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6") ) ) : h3( - React3.Fragment, + React4.Fragment, null, provisionView, model.bots.length === 0 && !provision ? h3(EmptyView3, { onStart: () => void startProvisioning(), busy }) : null, @@ -3904,7 +3967,7 @@ function installImStyles() { } // plugin-src/client/index.js -var h4 = React4.createElement; +var h4 = React5.createElement; var name = "im-settings"; var inject = ["slots", "connection"]; var CHANNELS = Object.freeze([ @@ -3959,7 +4022,7 @@ function ChannelLogo({ channel }) { return h4(DingtalkLogo); } function IMSettingsTab({ dingtalkRpcCall, feishuRpcCall, weixinRpcCall }) { - const [selected, setSelected] = React4.useState("weixin"); + const [selected, setSelected] = React5.useState("weixin"); const active = CHANNELS.find((channel) => channel.id === selected) ?? CHANNELS[0]; return h4( "section", diff --git a/package-lock.json b/package-lock.json index f4d8969..90eeb0e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "@xmanrui/dsh-dingtalk": "https://github.com/xmanrui/dsh-dingtalk/archive/e21177c3ba845f42d8f72f68b63cce67fe1a1d55.tar.gz", + "@xmanrui/dsh-dingtalk": "https://github.com/xmanrui/dsh-dingtalk/archive/05f5a319b52ef7f0952baf5d603ebc144657262a.tar.gz", "@xmanrui/dsh-feishu": "https://github.com/xmanrui/dsh-feishu/archive/aad650feabadd511241aa58b236d64273d5e397f.tar.gz", "@xmanrui/dsh-weixin": "https://github.com/xmanrui/dsh-weixin/archive/76d076771b2c84fb4c5598c2344d486695eda080.tar.gz" }, @@ -550,8 +550,8 @@ }, "node_modules/@xmanrui/dsh-dingtalk": { "version": "0.1.0", - "resolved": "https://github.com/xmanrui/dsh-dingtalk/archive/e21177c3ba845f42d8f72f68b63cce67fe1a1d55.tar.gz", - "integrity": "sha512-zox9q7LIhD26re/8SFGGQxLFOH1J8viRXHKdmHftfLyoZBfRNLYkXijjhpdT6tjpAqIgSYo2Zp6NTV+2/Kcg7g==", + "resolved": "https://github.com/xmanrui/dsh-dingtalk/archive/05f5a319b52ef7f0952baf5d603ebc144657262a.tar.gz", + "integrity": "sha512-jQMxICgQueYMRpDcK5R1pcsIxlB1DQNYzZqx2iE/LuhQvBpM7lPF8X/VmkC9Co6Gzgr919KcqJ6HvkO2pzF48w==", "license": "MIT", "dependencies": { "dingtalk-stream": "2.1.4", diff --git a/package.json b/package.json index 1d772e2..1c89afa 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "node": ">=22.19" }, "dependencies": { - "@xmanrui/dsh-dingtalk": "https://github.com/xmanrui/dsh-dingtalk/archive/e21177c3ba845f42d8f72f68b63cce67fe1a1d55.tar.gz", + "@xmanrui/dsh-dingtalk": "https://github.com/xmanrui/dsh-dingtalk/archive/05f5a319b52ef7f0952baf5d603ebc144657262a.tar.gz", "@xmanrui/dsh-feishu": "https://github.com/xmanrui/dsh-feishu/archive/aad650feabadd511241aa58b236d64273d5e397f.tar.gz", "@xmanrui/dsh-weixin": "https://github.com/xmanrui/dsh-weixin/archive/76d076771b2c84fb4c5598c2344d486695eda080.tar.gz" }, diff --git a/plugin-src/client/channels/feishu/index.js b/plugin-src/client/channels/feishu/index.js index 74f0634..f6b15b5 100644 --- a/plugin-src/client/channels/feishu/index.js +++ b/plugin-src/client/channels/feishu/index.js @@ -10,6 +10,7 @@ import { presentError, unwrapRpcResult, } from "./api.js"; +import { useAnimationFrameScheduler } from "../../lifecycle.js"; import { installFeishuStyles } from "./styles.js"; const h = React.createElement; @@ -486,6 +487,33 @@ function PageError({ error, onRetry, busy }) { const EMPTY_TOTALS = Object.freeze({ configured: 0, connected: 0 }); +export function mergeFeishuSnapshotState( + current, + snapshot, + { restoreProvisioning = false, now = Date.now() } = {}, +) { + if (snapshot.revision > 0 && current.revision > snapshot.revision) return current; + let provisioning = current.provisioning; + if (!provisioning && restoreProvisioning && snapshot.provisioning) { + provisioning = { + phase: snapshot.state === "connecting" ? "connecting" : "qr", + ...snapshot.provisioning, + durationMs: Math.max(1, snapshot.provisioning.expiresAt - now), + expired: snapshot.provisioning.expiresAt <= now, + }; + } + return { + ...current, + phase: "ready", + revision: snapshot.revision, + bots: snapshot.bots, + totals: snapshot.totals, + provisioning, + pageError: null, + statusError: null, + }; +} + export function FeishuSettingsTab({ rpcCall }) { const [model, setModel] = React.useState({ phase: "loading", @@ -507,63 +535,50 @@ export function FeishuSettingsTab({ rpcCall }) { const cardRefs = React.useRef(new Map()); const removeButtonRefs = React.useRef(new Map()); const addButtonRef = React.useRef(null); + const scheduleAnimationFrame = useAnimationFrameScheduler(); const announce = React.useCallback((message) => { setAnnouncement(""); - if (!message) return; - window.requestAnimationFrame(() => setAnnouncement(message)); - }, []); + scheduleAnimationFrame(() => { + if (message) setAnnouncement(message); + }, "announcement"); + }, [scheduleAnimationFrame]); const invoke = React.useCallback(async (endpoint, payload = {}, signal) => { return unwrapRpcResult(await rpcCall(endpoint, payload, signal)); }, [rpcCall]); - const mergeSnapshot = React.useCallback((snapshot, { restoreProvisioning = true } = {}) => { - setModel((current) => { - if (snapshot.revision > 0 && current.revision > snapshot.revision) return current; - let provisioning = current.provisioning; - if (!provisioning && restoreProvisioning && snapshot.provisioning) { - provisioning = { - phase: snapshot.state === "connecting" ? "connecting" : "qr", - ...snapshot.provisioning, - durationMs: Math.max(1, snapshot.provisioning.expiresAt - Date.now()), - expired: snapshot.provisioning.expiresAt <= Date.now(), - }; - } - return { - ...current, - phase: "ready", - revision: snapshot.revision, - bots: snapshot.bots, - totals: snapshot.totals, - provisioning, - pageError: null, - statusError: null, - }; - }); + const mergeSnapshot = React.useCallback((snapshot, { restoreProvisioning = false } = {}) => { + const now = Date.now(); + setModel((current) => mergeFeishuSnapshotState( + current, + snapshot, + { restoreProvisioning, now }, + )); }, []); - const loadStatus = React.useCallback(async ({ signal, silent = false, restoreProvisioning = true } = {}) => { + const loadStatus = React.useCallback(async ({ signal, silent = false, restoreProvisioning = false } = {}) => { if (!silent) setPageBusy(true); try { const snapshot = normalizeBotsSnapshot(await invoke(FEISHU_ENDPOINTS.status, {}, signal)); + if (signal?.aborted) return undefined; mergeSnapshot(snapshot, { restoreProvisioning }); return snapshot; } catch (error) { - if (error?.name === "AbortError") return undefined; + if (signal?.aborted || error?.name === "AbortError") return undefined; const presented = presentError(error); setModel((current) => current.phase === "loading" || !silent ? { ...current, phase: "error", pageError: presented } : { ...current, statusError: presented }); return undefined; } finally { - if (!silent) setPageBusy(false); + if (!silent && !signal?.aborted) setPageBusy(false); } }, [invoke, mergeSnapshot]); React.useEffect(() => { const controller = new AbortController(); - void loadStatus({ signal: controller.signal }); + void loadStatus({ signal: controller.signal, restoreProvisioning: true }); return () => controller.abort(); }, [loadStatus]); @@ -576,7 +591,11 @@ export function FeishuSettingsTab({ rpcCall }) { const timer = window.setInterval(async () => { if (inFlight) return; inFlight = true; - await loadStatus({ signal: controller.signal, silent: true }); + await loadStatus({ + signal: controller.signal, + silent: true, + restoreProvisioning: false, + }); inFlight = false; }, 15_000); return () => { @@ -641,7 +660,7 @@ export function FeishuSettingsTab({ rpcCall }) { setModel((current) => ({ ...current, provisioning: null })); announce("已取消添加机器人。"); await loadStatus({ silent: true, restoreProvisioning: false }); - window.requestAnimationFrame(() => addButtonRef.current?.focus()); + scheduleAnimationFrame(() => addButtonRef.current?.focus(), "focus"); } catch (error) { setModel((current) => ({ ...current, @@ -650,16 +669,19 @@ export function FeishuSettingsTab({ rpcCall }) { } finally { setProvisionBusy(false); } - }, [announce, invoke, loadStatus, model.provisioning?.attemptId]); + }, [announce, invoke, loadStatus, model.provisioning?.attemptId, scheduleAnimationFrame]); + const countdownAttemptId = model.provisioning?.attemptId; + const countdownPhase = model.provisioning?.phase; + const countdownExpiresAt = model.provisioning?.expiresAt; + const countdownExpired = model.provisioning?.expired; React.useEffect(() => { - const provision = model.provisioning; - if (!provision || provision.phase !== "qr" || provision.expired) return undefined; + if (!countdownAttemptId || countdownPhase !== "qr" || countdownExpired) return undefined; const tick = () => { const timestamp = Date.now(); setNow(timestamp); - if (timestamp >= provision.expiresAt) { - setModel((current) => current.provisioning?.attemptId === provision.attemptId + if (timestamp >= countdownExpiresAt) { + setModel((current) => current.provisioning?.attemptId === countdownAttemptId ? { ...current, provisioning: { ...current.provisioning, expired: true } } : current); } @@ -667,7 +689,7 @@ export function FeishuSettingsTab({ rpcCall }) { tick(); const timer = window.setInterval(tick, 1_000); return () => window.clearInterval(timer); - }, [model.provisioning]); + }, [countdownAttemptId, countdownPhase, countdownExpiresAt, countdownExpired]); React.useEffect(() => { const provision = model.provisioning; @@ -797,8 +819,8 @@ export function FeishuSettingsTab({ rpcCall }) { const cancelRemove = React.useCallback(() => { const botId = removeTargetId; setRemoveTargetId(null); - window.requestAnimationFrame(() => removeButtonRefs.current.get(botId)?.focus()); - }, [removeTargetId]); + scheduleAnimationFrame(() => removeButtonRefs.current.get(botId)?.focus(), "focus"); + }, [removeTargetId, scheduleAnimationFrame]); const confirmRemove = React.useCallback(async (connection) => { const { botId, bot } = connection; @@ -820,14 +842,14 @@ export function FeishuSettingsTab({ rpcCall }) { }); announce(`${bot.name}已从此 DeepSeek Harness 移除;飞书开放平台中的应用未被删除。`); await loadStatus({ silent: true }); - window.requestAnimationFrame(() => addButtonRef.current?.focus()); + scheduleAnimationFrame(() => addButtonRef.current?.focus(), "focus"); } catch (error) { setBotError(botId, error); announce(`${bot.name}移除失败,请重试。`); } finally { setBotBusy(botId, null); } - }, [announce, invoke, loadStatus, setBotBusy, setBotError]); + }, [announce, invoke, loadStatus, scheduleAnimationFrame, setBotBusy, setBotError]); const provision = model.provisioning; let provisionContent = null; diff --git a/plugin-src/client/channels/weixin/index.js b/plugin-src/client/channels/weixin/index.js index f44e91b..f390378 100644 --- a/plugin-src/client/channels/weixin/index.js +++ b/plugin-src/client/channels/weixin/index.js @@ -11,6 +11,7 @@ import { safeVerificationUrl, unwrapRpcResult, } from './api.js'; +import { createPollScheduler, useAnimationFrameScheduler } from '../../lifecycle.js'; import { installWeixinStyles } from './styles.js'; const h = React.createElement; @@ -267,6 +268,20 @@ function AccountList(props) { const EMPTY_TOTALS = Object.freeze({ configured: 0, connected: 0 }); +export function mergeWeixinProvisioningSnapshot( + current, + incoming, + { restoreProvisioning = false } = {}, +) { + if (!incoming || (!current && !restoreProvisioning)) return current; + if (current && current.attemptId !== incoming.attemptId) return current; + return { + ...current, + ...incoming, + durationMs: current?.durationMs ?? 5 * 60_000, + }; +} + export function WeixinSettingsTab({ rpcCall }) { const [model, setModel] = React.useState({ phase: 'loading', bots: [], totals: EMPTY_TOTALS, revision: 0, error: null, @@ -278,30 +293,40 @@ export function WeixinSettingsTab({ rpcCall }) { const [notice, setNotice] = React.useState(''); const [now, setNow] = React.useState(() => Date.now()); const addButtonRef = React.useRef(null); + const scheduleAnimationFrame = useAnimationFrameScheduler(); const announce = React.useCallback((value) => { setNotice(''); - if (value) window.requestAnimationFrame(() => setNotice(value)); - }, []); + scheduleAnimationFrame(() => { + if (value) setNotice(value); + }, 'announcement'); + }, [scheduleAnimationFrame]); const invoke = React.useCallback(async (endpoint, payload = {}, signal) => { return unwrapRpcResult(await rpcCall(endpoint, payload, signal)); }, [rpcCall]); - const loadStatus = React.useCallback(async ({ signal, silent = false } = {}) => { + const loadStatus = React.useCallback(async ({ + signal, + silent = false, + restoreProvisioning = false, + } = {}) => { if (!silent) setModel((current) => ({ ...current, phase: 'loading', error: null })); try { const snapshot = normalizeSnapshot(await invoke(WEIXIN_ENDPOINTS.status, {}, signal)); + if (signal?.aborted) return undefined; setModel({ phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, revision: snapshot.revision, error: null, }); if (snapshot.provisioning) { - setProvision((current) => !current || current.attemptId === snapshot.provisioning.attemptId - ? { ...current, ...snapshot.provisioning, durationMs: current?.durationMs ?? 5 * 60_000 } - : current); + setProvision((current) => mergeWeixinProvisioningSnapshot( + current, + snapshot.provisioning, + { restoreProvisioning }, + )); } return snapshot; } catch (error) { - if (error?.name === 'AbortError') return undefined; + if (signal?.aborted || error?.name === 'AbortError') return undefined; setModel((current) => ({ ...current, phase: silent && current.phase === 'ready' ? 'ready' : 'error', @@ -313,7 +338,7 @@ export function WeixinSettingsTab({ rpcCall }) { React.useEffect(() => { const controller = new AbortController(); - void loadStatus({ signal: controller.signal }); + void loadStatus({ signal: controller.signal, restoreProvisioning: true }); return () => controller.abort(); }, [loadStatus]); @@ -324,7 +349,11 @@ export function WeixinSettingsTab({ rpcCall }) { const timer = window.setInterval(async () => { if (running) return; running = true; - await loadStatus({ signal: controller.signal, silent: true }); + await loadStatus({ + signal: controller.signal, + silent: true, + restoreProvisioning: false, + }); running = false; }, 15_000); return () => { @@ -369,13 +398,13 @@ export function WeixinSettingsTab({ rpcCall }) { } setProvision(null); announce('已取消微信绑定。'); - window.requestAnimationFrame(() => addButtonRef.current?.focus()); + scheduleAnimationFrame(() => addButtonRef.current?.focus(), 'focus'); } catch (error) { setProvision((current) => ({ ...current, status: 'failed', error: presentError(error) })); } finally { setBusy(false); } - }, [announce, invoke, provision?.attemptId, provision?.status]); + }, [announce, invoke, provision?.attemptId, provision?.status, scheduleAnimationFrame]); const submitVerification = React.useCallback(async (verifyCode) => { if (!provision?.attemptId) return; @@ -398,7 +427,10 @@ export function WeixinSettingsTab({ rpcCall }) { const attemptId = provision?.attemptId; if (!attemptId || !['pending', 'scanned', 'connecting'].includes(provision.status)) return undefined; const controller = new AbortController(); - let timer; + const scheduler = createPollScheduler({ + setTimeoutFn: (callback, delayMs) => window.setTimeout(callback, delayMs), + clearTimeoutFn: (timer) => window.clearTimeout(timer), + }); const poll = async () => { try { const result = normalizeProvisioning(await invoke( @@ -406,14 +438,20 @@ export function WeixinSettingsTab({ rpcCall }) { { attemptId }, controller.signal, )); + if (scheduler.disposed) return; if (result.status === 'connected') { - const snapshot = await loadStatus({ signal: controller.signal, silent: true }); + const snapshot = await loadStatus({ + signal: controller.signal, + silent: true, + restoreProvisioning: false, + }); + if (scheduler.disposed) return; const account = snapshot?.bots.find((bot) => bot.botId === result.botId); if (!account?.connected) { setProvision((current) => current?.attemptId === attemptId ? { ...current, ...result, status: 'connecting' } : current); - timer = window.setTimeout(poll, result.pollIntervalMs); + scheduler.schedule(poll, result.pollIntervalMs); return; } setProvision(null); @@ -426,19 +464,19 @@ export function WeixinSettingsTab({ rpcCall }) { ? { ...current, ...result, durationMs: current.durationMs } : current); if (['pending', 'scanned', 'connecting'].includes(result.status)) { - timer = window.setTimeout(poll, result.pollIntervalMs); + scheduler.schedule(poll, result.pollIntervalMs); } } catch (error) { - if (error?.name === 'AbortError') return; + if (scheduler.disposed || error?.name === 'AbortError') return; setProvision((current) => current?.attemptId === attemptId ? { ...current, status: 'failed', error: presentError(error) } : current); } }; - timer = window.setTimeout(poll, provision.pollIntervalMs ?? 1_000); + scheduler.schedule(poll, provision.pollIntervalMs ?? 1_000); return () => { + scheduler.dispose(); controller.abort(); - window.clearTimeout(timer); }; }, [announce, invoke, loadStatus, provision?.attemptId, provision?.status, provision?.pollIntervalMs]); diff --git a/plugin-src/client/lifecycle.js b/plugin-src/client/lifecycle.js new file mode 100644 index 0000000..e6295ac --- /dev/null +++ b/plugin-src/client/lifecycle.js @@ -0,0 +1,86 @@ +import * as React from 'react'; + +export function createPollScheduler({ setTimeoutFn, clearTimeoutFn }) { + let disposed = false; + let timer; + + return { + get disposed() { + return disposed; + }, + schedule(callback, delayMs) { + if (disposed) return false; + if (timer !== undefined) clearTimeoutFn(timer); + timer = setTimeoutFn(() => { + timer = undefined; + if (!disposed) void callback(); + }, delayMs); + return true; + }, + dispose() { + if (disposed) return; + disposed = true; + if (timer !== undefined) clearTimeoutFn(timer); + timer = undefined; + }, + }; +} + +export function createAnimationFrameScheduler({ requestFrame, cancelFrame }) { + let disposed = false; + const frames = new Set(); + const keyedFrames = new Map(); + + return { + schedule(callback, key) { + if (disposed) return false; + const previous = key === undefined ? undefined : keyedFrames.get(key); + if (previous !== undefined) { + keyedFrames.delete(key); + frames.delete(previous); + cancelFrame(previous); + } + let frame; + let completed = false; + frame = requestFrame(() => { + completed = true; + if (frame !== undefined) frames.delete(frame); + if (key !== undefined && keyedFrames.get(key) === frame) keyedFrames.delete(key); + if (!disposed) callback(); + }); + if (!completed) { + frames.add(frame); + if (key !== undefined) keyedFrames.set(key, frame); + } + return true; + }, + dispose() { + if (disposed) return; + disposed = true; + for (const frame of frames) cancelFrame(frame); + frames.clear(); + keyedFrames.clear(); + }, + }; +} + +export function useAnimationFrameScheduler() { + const schedulerRef = React.useRef(null); + + React.useEffect(() => { + const scheduler = createAnimationFrameScheduler({ + requestFrame: (callback) => window.requestAnimationFrame(callback), + cancelFrame: (frame) => window.cancelAnimationFrame(frame), + }); + schedulerRef.current = scheduler; + return () => { + scheduler.dispose(); + if (schedulerRef.current === scheduler) schedulerRef.current = null; + }; + }, []); + + return React.useCallback( + (callback, key) => schedulerRef.current?.schedule(callback, key) ?? false, + [], + ); +} diff --git a/test/client-lifecycle.test.mjs b/test/client-lifecycle.test.mjs new file mode 100644 index 0000000..481f102 --- /dev/null +++ b/test/client-lifecycle.test.mjs @@ -0,0 +1,185 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; + +import { mergeFeishuSnapshotState } from '../plugin-src/client/channels/feishu/index.js'; +import { mergeWeixinProvisioningSnapshot } from '../plugin-src/client/channels/weixin/index.js'; +import { + createAnimationFrameScheduler, + createPollScheduler, +} from '../plugin-src/client/lifecycle.js'; + +function deferred() { + let resolve; + const promise = new Promise((done) => { + resolve = done; + }); + return { promise, resolve }; +} + +function fakeTimers() { + let nextId = 1; + const pending = new Map(); + return { + setTimeout(callback) { + const id = nextId; + nextId += 1; + pending.set(id, callback); + return id; + }, + clearTimeout(id) { + pending.delete(id); + }, + runNext() { + const entry = pending.entries().next().value; + if (!entry) return false; + const [id, callback] = entry; + pending.delete(id); + callback(); + return true; + }, + get size() { + return pending.size; + }, + }; +} + +test('disposed Weixin polling cannot schedule a zombie timer after a late RPC', async () => { + const timers = fakeTimers(); + const response = deferred(); + const scheduler = createPollScheduler({ + setTimeoutFn: (callback) => timers.setTimeout(callback), + clearTimeoutFn: (id) => timers.clearTimeout(id), + }); + let polls = 0; + const poll = async () => { + polls += 1; + await response.promise; + scheduler.schedule(poll, 1_000); + }; + + scheduler.schedule(poll, 0); + assert.equal(timers.runNext(), true); + assert.equal(polls, 1); + assert.equal(timers.size, 0); + + scheduler.dispose(); + response.resolve(); + await Promise.resolve(); + await Promise.resolve(); + + assert.equal(timers.size, 0); + assert.equal(scheduler.schedule(poll, 1_000), false); +}); + +test('disposing an animation-frame scheduler cancels callbacks queued by a removed tab', () => { + let nextId = 1; + const pending = new Map(); + const cancelled = []; + const scheduler = createAnimationFrameScheduler({ + requestFrame(callback) { + const id = nextId; + nextId += 1; + pending.set(id, callback); + return id; + }, + cancelFrame(id) { + cancelled.push(id); + pending.delete(id); + }, + }); + let ran = false; + + scheduler.schedule(() => { + ran = true; + }); + scheduler.dispose(); + + assert.deepEqual(cancelled, [1]); + assert.equal(pending.size, 0); + assert.equal(ran, false); + assert.equal(scheduler.schedule(() => {}), false); +}); + +test('keyed animation frames keep only the latest announcement', () => { + let nextId = 1; + const pending = new Map(); + const cancelled = []; + const scheduler = createAnimationFrameScheduler({ + requestFrame(callback) { + const id = nextId; + nextId += 1; + pending.set(id, callback); + return id; + }, + cancelFrame(id) { + cancelled.push(id); + pending.delete(id); + }, + }); + const announcements = []; + + scheduler.schedule(() => announcements.push('old'), 'announcement'); + scheduler.schedule(() => announcements.push('new'), 'announcement'); + + assert.deepEqual(cancelled, [1]); + assert.deepEqual([...pending.keys()], [2]); + const latest = pending.get(2); + pending.delete(2); + latest(); + assert.deepEqual(announcements, ['new']); + scheduler.dispose(); +}); + +test('periodic snapshots cannot restore locally cancelled Weixin or Feishu provisioning', () => { + const weixinProvisioning = { + attemptId: 'wx_attempt_stale', + status: 'pending', + expiresAt: 2_000, + }; + assert.equal(mergeWeixinProvisioningSnapshot( + null, + weixinProvisioning, + { restoreProvisioning: false }, + ), null); + assert.equal( + mergeWeixinProvisioningSnapshot( + null, + weixinProvisioning, + { restoreProvisioning: true }, + )?.attemptId, + 'wx_attempt_stale', + ); + + const current = { + phase: 'ready', + revision: 3, + bots: [], + totals: { configured: 0, connected: 0 }, + provisioning: null, + pageError: null, + statusError: null, + }; + const snapshot = { + revision: 4, + state: 'provisioning', + bots: [], + totals: { configured: 0, connected: 0 }, + provisioning: { + attemptId: 'fs_attempt_stale', + expiresAt: 2_000, + }, + }; + assert.equal(mergeFeishuSnapshotState( + current, + snapshot, + { restoreProvisioning: false, now: 1_000 }, + ).provisioning, null); + assert.equal( + mergeFeishuSnapshotState( + current, + snapshot, + { restoreProvisioning: true, now: 1_000 }, + ).provisioning.attemptId, + 'fs_attempt_stale', + ); +}); diff --git a/test/client-ui.test.mjs b/test/client-ui.test.mjs index fec4a09..f5c112f 100644 --- a/test/client-ui.test.mjs +++ b/test/client-ui.test.mjs @@ -8,6 +8,10 @@ import { renderToStaticMarkup } from 'react-dom/server'; import { IMSettingsTab } from '../plugin-src/client/index.js'; const STYLES_URL = new URL('../plugin-src/client/styles.js', import.meta.url); +const CLIENT_BUNDLE_URL = new URL('../lib/client.js', import.meta.url); +const DINGTALK_CLIENT_SOURCE_URL = new URL( + import.meta.resolve('@xmanrui/dsh-dingtalk/client-source'), +); test('IM settings renders three compact logo channel tabs without enable switches', () => { const markup = renderToStaticMarkup(React.createElement(IMSettingsTab, { @@ -42,3 +46,19 @@ test('the DingTalk QR card stacks within the narrow combined-channel panel', asy assert.match(styles, /\.dim-panel \.ddt-qrColumn \{ width: 100%; min-width: 0; \}/); assert.match(styles, /\.dim-panel \.ddt-qrCopy \{ width: 100%; min-width: 0; overflow-wrap: anywhere; \}/); }); + +test('the bundled DingTalk channel has no local sender approval workflow', async () => { + const [{ DINGTALK_ENDPOINTS }, source, bundle] = await Promise.all([ + import('@xmanrui/dsh-dingtalk/client-api'), + readFile(DINGTALK_CLIENT_SOURCE_URL, 'utf8'), + readFile(CLIENT_BUNDLE_URL, 'utf8'), + ]); + + assert.equal('approveSender' in DINGTALK_ENDPOINTS, false); + assert.equal('revokeSender' in DINGTALK_ENDPOINTS, false); + assert.doesNotMatch(source, /SenderAccess|onApprove|onRevoke|approveSender|revokeSender/); + assert.doesNotMatch( + bundle, + /bot\.sender\.approve|bot\.sender\.revoke|允许使用机器人的钉钉账号|批准使用/, + ); +});