From 0549e3d7eb7be15c4726f274e88206d78bb3be6f Mon Sep 17 00:00:00 2001 From: musistudio Date: Sun, 28 Jun 2026 21:19:01 +0800 Subject: [PATCH 01/94] Update docs deployment and provider import copy --- .github/workflows/cdn.yml | 33 ++ .github/workflows/docs.yml | 4 +- README.md | 6 +- README_zh.md | 6 +- cdn/README.md | 63 +++ cdn/package.json | 10 + cdn/public/_headers | 10 + cdn/public/ccr-icon.png | Bin 0 -> 779110 bytes cdn/public/ccr-provider-buttons.js | 302 +++++++++++++++ cdn/wrangler.toml | 3 + docs/README.md | 4 +- docs/astro.config.mjs | 10 +- docs/public/CNAME | 1 + docs/public/ccr-icon.png | Bin 0 -> 779110 bytes docs/public/provider-icons/anthropic.png | Bin 0 -> 1588 bytes docs/public/provider-icons/bailian.ico | Bin 0 -> 1406 bytes docs/public/provider-icons/deepseek.ico | Bin 0 -> 209762 bytes docs/public/provider-icons/gemini.svg | 1 + docs/public/provider-icons/mistral.webp | Bin 0 -> 1214 bytes docs/public/provider-icons/moonshot.ico | Bin 0 -> 16958 bytes docs/public/provider-icons/openai.png | Bin 0 -> 6578 bytes docs/public/provider-icons/openrouter.ico | Bin 0 -> 15406 bytes docs/public/provider-icons/siliconflow.png | Bin 0 -> 358 bytes .../provider-icons/zai-global-coding.svg | 219 +++++++++++ .../provider-icons/zai-global-general.svg | 219 +++++++++++ .../public/provider-icons/zhipu-cn-coding.png | Bin 0 -> 1099 bytes .../provider-icons/zhipu-cn-general.png | Bin 0 -> 1099 bytes docs/src/content/docs/en/configuration.md | 4 +- .../docs/en/configuration/extensions.md | 281 ++++++++++++++ .../en/configuration/provider-deeplink.md | 271 +++++++++++++ docs/src/content/docs/zh/configuration.md | 5 +- .../docs/zh/configuration/extensions.md | 281 ++++++++++++++ .../zh/configuration/provider-deeplink.md | 271 +++++++++++++ docs/src/i18n/content.ts | 8 + docs/src/pages/configuration/[slug].astro | 2 + docs/src/pages/en/configuration/[slug].astro | 2 + docs/src/styles/global.css | 358 +++++++++++++++++- src/main/app-menu.ts | 122 +----- src/main/preload.ts | 5 + src/renderer/pages/home/App.tsx | 126 +++++- .../pages/home/components/dialog-stack.tsx | 6 +- src/renderer/pages/home/components/index.ts | 1 + src/renderer/pages/home/components/layout.tsx | 25 +- src/renderer/pages/home/components/update.tsx | 220 +++++++++++ src/renderer/pages/home/shared/i18n.tsx | 1 - src/renderer/types/electron.d.ts | 1 + src/shared/deep-link.ts | 130 +++---- src/shared/ipc-channels.ts | 1 + 48 files changed, 2790 insertions(+), 222 deletions(-) create mode 100644 .github/workflows/cdn.yml create mode 100644 cdn/README.md create mode 100644 cdn/package.json create mode 100644 cdn/public/_headers create mode 100644 cdn/public/ccr-icon.png create mode 100644 cdn/public/ccr-provider-buttons.js create mode 100644 cdn/wrangler.toml create mode 100644 docs/public/CNAME create mode 100644 docs/public/ccr-icon.png create mode 100644 docs/public/provider-icons/anthropic.png create mode 100644 docs/public/provider-icons/bailian.ico create mode 100644 docs/public/provider-icons/deepseek.ico create mode 100644 docs/public/provider-icons/gemini.svg create mode 100644 docs/public/provider-icons/mistral.webp create mode 100644 docs/public/provider-icons/moonshot.ico create mode 100644 docs/public/provider-icons/openai.png create mode 100644 docs/public/provider-icons/openrouter.ico create mode 100644 docs/public/provider-icons/siliconflow.png create mode 100644 docs/public/provider-icons/zai-global-coding.svg create mode 100644 docs/public/provider-icons/zai-global-general.svg create mode 100644 docs/public/provider-icons/zhipu-cn-coding.png create mode 100644 docs/public/provider-icons/zhipu-cn-general.png create mode 100644 docs/src/content/docs/en/configuration/extensions.md create mode 100644 docs/src/content/docs/en/configuration/provider-deeplink.md create mode 100644 docs/src/content/docs/zh/configuration/extensions.md create mode 100644 docs/src/content/docs/zh/configuration/provider-deeplink.md create mode 100644 src/renderer/pages/home/components/update.tsx diff --git a/.github/workflows/cdn.yml b/.github/workflows/cdn.yml new file mode 100644 index 0000000..6223aa6 --- /dev/null +++ b/.github/workflows/cdn.yml @@ -0,0 +1,33 @@ +name: CDN + +on: + push: + branches: + - main + paths: + - "cdn/**" + - ".github/workflows/cdn.yml" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: cdn + cancel-in-progress: false + +jobs: + deploy: + name: Deploy Cloudflare Pages + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Deploy CDN assets + uses: cloudflare/wrangler-action@v3 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + workingDirectory: cdn + command: pages deploy public --project-name=claude-code-router-cdn --branch=${{ github.ref_name }} diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index e2f2965..00463e0 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -29,8 +29,8 @@ jobs: - name: Build and upload docs uses: withastro/action@v6 env: - ASTRO_SITE: https://musistudio.github.io - ASTRO_BASE: /claude-code-router + ASTRO_SITE: https://ccrdesk.top + ASTRO_BASE: / with: path: docs node-version: 24 diff --git a/README.md b/README.md index f079394..1377e08 100644 --- a/README.md +++ b/README.md @@ -89,9 +89,9 @@ ccr://provider?name=Example%20AI&base_url=https%3A%2F%2Fapi.example.com%2Fv1&api Supported query parameters: - `name`: display name for the provider. -- `base_url`: provider API base URL. Aliases: `baseUrl`, `api_base_url`, `url`, `endpoint`. -- `api_key`: optional provider API key. Aliases: `apiKey`, `apikey`, `key`, `token`. -- `models`: comma-separated or newline-separated model list. You can also repeat `model=...`. +- `base_url`: provider API base URL. +- `api_key`: optional provider API key. +- `models`: comma-separated or newline-separated model list. You can also repeat `models=...`. - `protocol`: one of `openai_chat_completions`, `openai_responses`, `anthropic_messages`, or `gemini_generate_content`. For larger payloads, pass `payload` as URL-encoded JSON or base64url JSON with the same fields. CCR always opens a confirmation dialog before writing a provider imported from an external link. diff --git a/README_zh.md b/README_zh.md index 4c07395..dc81f80 100644 --- a/README_zh.md +++ b/README_zh.md @@ -89,9 +89,9 @@ ccr://provider?name=Example%20AI&base_url=https%3A%2F%2Fapi.example.com%2Fv1&api 支持的 query 参数: - `name`:Provider 展示名称。 -- `base_url`:Provider API Base URL。别名:`baseUrl`、`api_base_url`、`url`、`endpoint`。 -- `api_key`:可选 Provider API Key。别名:`apiKey`、`apikey`、`key`、`token`。 -- `models`:逗号或换行分隔的模型列表,也可以重复传入 `model=...`。 +- `base_url`:Provider API Base URL。 +- `api_key`:可选 Provider API Key。 +- `models`:逗号或换行分隔的模型列表,也可以重复传入 `models=...`。 - `protocol`:`openai_chat_completions`、`openai_responses`、`anthropic_messages` 或 `gemini_generate_content`。 更大的 payload 可以通过 URL 编码 JSON 或 base64url JSON 传入 `payload` 字段。CCR 在写入外部链接导入的 Provider 前,总会弹出确认窗口。 diff --git a/cdn/README.md b/cdn/README.md new file mode 100644 index 0000000..772e06a --- /dev/null +++ b/cdn/README.md @@ -0,0 +1,63 @@ +# CCR CDN + +This directory contains the static assets for the embeddable CCR provider import button. + +Default Cloudflare Pages project: + +```text +claude-code-router-cdn +``` + +Default CDN URLs: + +```text +https://cdn.ccrdesk.top/ccr-provider-buttons.js +https://cdn.ccrdesk.top/ccr-icon.png +``` + +Cloudflare Pages custom domain: + +```text +cdn.ccrdesk.top +``` + +The Pages custom domain must have a CNAME record pointing to the Pages project: + +```text +cdn.ccrdesk.top CNAME claude-code-router-cdn.pages.dev +``` + +The documentation site uses `ccrdesk.top` through GitHub Pages. Configure DNS like this: + +```text +ccrdesk.top A 185.199.108.153 +ccrdesk.top A 185.199.109.153 +ccrdesk.top A 185.199.110.153 +ccrdesk.top A 185.199.111.153 +ccrdesk.top AAAA 2606:50c0:8000::153 +ccrdesk.top AAAA 2606:50c0:8001::153 +ccrdesk.top AAAA 2606:50c0:8002::153 +ccrdesk.top AAAA 2606:50c0:8003::153 +cdn.ccrdesk.top CNAME claude-code-router-cdn.pages.dev +``` + +If `ccrdesk.top` is delegated to Cloudflare, use these Cloudflare nameservers at the registrar: + +```text +benedict.ns.cloudflare.com +evangeline.ns.cloudflare.com +``` + +Deploy manually: + +```sh +cd cdn +npx wrangler pages deploy public --project-name=claude-code-router-cdn +``` + +The GitHub Actions workflow requires these repository secrets: + +```text +CLOUDFLARE_API_TOKEN +CLOUDFLARE_ACCOUNT_ID +``` diff --git a/cdn/package.json b/cdn/package.json new file mode 100644 index 0000000..3968876 --- /dev/null +++ b/cdn/package.json @@ -0,0 +1,10 @@ +{ + "name": "claude-code-router-cdn", + "private": true, + "version": "0.1.0", + "type": "module", + "scripts": { + "deploy": "wrangler pages deploy public --project-name=claude-code-router-cdn", + "deploy:preview": "wrangler pages deploy public --project-name=claude-code-router-cdn --branch=preview" + } +} diff --git a/cdn/public/_headers b/cdn/public/_headers new file mode 100644 index 0000000..c4b9aa0 --- /dev/null +++ b/cdn/public/_headers @@ -0,0 +1,10 @@ +/* + Access-Control-Allow-Origin: * + X-Content-Type-Options: nosniff + +/ccr-provider-buttons.js + Cache-Control: public, max-age=300, s-maxage=86400 + Content-Type: application/javascript; charset=utf-8 + +/ccr-icon.png + Cache-Control: public, max-age=31536000, immutable diff --git a/cdn/public/ccr-icon.png b/cdn/public/ccr-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..42cdfc1bddbed24b569abd85b06996326c1482a5 GIT binary patch literal 779110 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 z z#3_4q1qa}|w~lP!PHPGsKR`2yjcthwHNisE(Yj_QSu?%G$>wL)(wJjlE40ViHH0fj z)&yjk%YBg_H5L5r2Ec!t|0E)m>J0wV8u^g|omOP2$rG+yl#z``#|*e`W;{T}?9I;E zV3*gJZtiSE(pI>+)B^91%jz%6;yZ1Vq)M5kNS1sNtcS}!5Tuh1qlLSmMX|)#5eKk~ z#ieeh0{TOX;IQD)gp*HOC{V0{x9CBplSnkG7cEX;B}QhZV2;ZFA&M+UJt}dTasm*K z7au@|R>p*J(seWy6WUn+NPW2G9#(WR-Oj;#=D-Y{^3f#%lp=+x!`8w>FihoR&by6m zyY3X+8UUVZ(ihx81d>0&&;!g;^yE{g0)`Khkp-d(G&^?eH;u&+Z&CIvU7^&W>#2C5+eXldy z;ITFi5|UbS7@FVca^h@g3X2AC1VmcTp;wl`alt6pKQ*~@4r#GU-DUs

l@Xwy8? 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

FJbe469l~h3*ny4>c0A(j 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 z z_lQl@1wjK2wABnwrx*vpZU=nmXHS7WuwDk#_{3~(SS+!TA!9(;#LL<|8=;2eb%6gCpR>0|7a|(@EJZcJuRgzx%VN{}s z*mA&2bPv$`QO$E?v~Ty2+bmmru11@Hw0)4*5=BNXXN(#A=h;ZL0V(R^cQnRfal?-7 z#;qn!bo|;F%UJ< zI=yO>2bye8&Swdy5a8T;3DY(sC>vGY=sS>vf_R|5nM4QJM(vvLhX9?w3Yv>>w zc@#7)^MDJuTvp-i?9PAdY56W!*m=K9{lGPvBY^S@mhxk-=mFlt*xyi?)f4T!Yk1PoyM@x*LFB+=njiqeLxwIrP^h5_ZV3{NC zIplN*od>sbqvR6wGnapH2Ds`AMVgAE%VGhTBy5^v>z8WC>n7EL19$NhcZmgVKpto1 zmsn8uX&-*Y*T4ffAcW>o4Q9I2C@bvlY;pJK5)XXOtC0Ss`*CY`0oekh)_XC0_YuCR zN(6IQUbD+Mu3k28P3b()^z88G%?{6JlQ>rRDCtTyW{UfrJziTJS1Fs;|b&h&`tJ$yhC~Bi&*~!TD&qR=?tdd2svb&3~d5y#rj=~|sop->CJ5>9aGg8t}hRi%gDtm>BQ=@<)cpY7v zM|x#aY|u`dq(Xqr#SS+g81clX&+%jb^fq=|U|hvofeyNk>F677hkkjm)eHj^0k+#6 z-thYS@cV!N2XOWJbv*aXGq`ec1**FyL*<_(LC!1T=X%1u|1;=f?i>ijnn4<3IlEby zE|?#dER=B$pgqP$87wNk8K+*>Y>+P75Yn*1`PmMydG%xXvw!k8W4qa59MWNHLG<=7 zG^qJn+b?Par1`$MawXxTpE$!0{ln)l4kJWn#)>6-(E0^y>Z?zB^;2r)rk;j|S5X-W zikI4yEs>mF*YQks4pExiL9njBnG^)*>Pb+XYOPC>YvQ>;K{eY*TPH}@5ml^H+pFp@ zZ~m;x8TavyOLPyl_Y&?jvOlPp!YyD5NzF?Kc(<1@g+I}sDetw`4gub$Y#nB5ers?f zw-#naXbvO?85flf?98=-a?*f!wi!Y!vDCev*D$X4Xt=7%vAU#wLsjo;*k5gpZjGj@ zbi~jGfKUzqEu12{>0FAAXs04=BnA#Ot9|w2amm2^tvJ8Mg<%V>5 z^-4#!4oW76#U8wv3IQh^O&+t1*GX{zxX@nV&DwVanYvjgXXcMj?SSe?xvfEvmZP7X z1W_Cad&&0AO%8QlRU!sOPgTs#PQm^iJZ?>LO@8?L-`BFn<=GaekMD5(555As_nhGA zTc;TJ1CFK@WP(xNqu({e8f%zrdf&M&jTB0^zV9GN1h7C389{r0csO*o?k1fOn#3~X z@~Kl&z*|D@N%C5lr#9<|xMoHtW>8(k0xljmdINjJ5p>0sEKH2`5aCb`q$E6#hCmPG zPpO8rqVqT^pKO3hF~|y=^9fI_Hn{TpUIY2&`*3@@3z;SiVnu{ha9M!S%>!F$z)g*0 ze_kU(_d0%P4fm}7e$_P;0$A7CcFbc)Qe2^iRgekSNxXD~_ef8s+ddTR8dL;OL%5l% zQr(*uNkpoTJPmYGY_JuB6%Ydddg_VjCh<4fqeQ6%csfnfs8WoJgF40ogI9VuNo9zI zgcMk6Qec}gUL7&MYK`5Hq5JI8Iif6mG1_ktuO`4a1u0y4Sn^?4IYUr@2a2h)W8l(F0I@$KLCF8uD_`Rj3Z`!;r)3#?b` zB9oPSe3^PU)K8;P8o^Jk%h{`L`3*3&1MCBhiYnExhA*$@A(yDrbnzbO(>E$~^$3?+ z!ET@Mr~lNyinqV*CZ?=5b?tOT@ndSg-X5$m)!DAbVgCMp>O*(%(;q&?wTDlDX`<$* zy5t+4R`*fD!N)JXNkSV}z}JV1^?@E@eDL*C?<=R|BEY95A=s3_HbM;a{fP(QN(AVr ztMx%DWBYCek_KNxi<`oEzkM3Wx*!fKBmvkZRbAWCd6HMWinvm%`_KO1lQ?ayA`UNyJUT+hj9zjD@p1smK)trU0;;0AxuR zWrfY1EuMXRi<9qt7`ykZ@ywkwtnvy&S~V-A05grf!BM<8l2YI(D?X+Ri%I@n6pnb! zi122ifd&v&P$qK_T9hSLArvH=#iOk&0VaaArT z#0aYk3SuWYR#_b2i!4Ynv`-G>fxW#C4GBGb5BhK2R2-pc!}4%f8ktnt6*kQ+B<(xY ztQcg(ely|Oqf6ZX`(J_l8?WQ({auWEMM9dB(UZ<{dhrrrB`8=-?Xc^uE}{w=^W)V+ z-uRBBL=f-4W63f_ZmHRbgr9SDXVh-KH>qa^C>1hrObj+s(0ap}?3C4xUsO36JT_r%9lisM}43mzXX??ErxNxFrX!}@PR|SH{AP;4<;DYS}+6Cq&Qk9 z+&<0tUw`B&$Zm&W1x)q&x_!(yo?=uU$N5-xJgKkLa_MK)=wxNQ!mTe5&qgupD-}kK zyHY6a8rLHDd&Nz_)u}lJta@Cy+ZXIJocHdMDOw#>6)3qy7AkQLI9gD1jh)pHt*1^+ zl$gE5Iyhb6TyQcNvk%~@^RDbF|DM4K0z!Nf^}`paV;!(kz;V5FsqLp_EsUu_MZF83 zMB^1fp7_sv3JD1n?(DGi-4Q|CaWy(CUm)xYh)X0bHO>F5s2J3s>X6Q^!u%z$N{>{@ zN`pC75Ffx#{a--4_vHovUR?VBK7YFwVQqa)v!~DjJbY2$;?vypNPHHp%lDMdcklC` zNwVKE+rw0(bn1krhsv=>6(&fNIuXC%y9o$&-ZaYWS{IZ)uh0~QX{xG$o9~YP#vw`4 zMPqR%&mGqav}9^*@fEgr6t}Ki;`qC6VE6tjxP5+x)g%~^0L_?WK42+(bVX@Weqav5 znXpc(?@x;?x1%1QE10#T`}?MXh9V4sFse?dO#)0If0~9oUzX8Pv1%3tbsbM>1nL6- zCML3oB`;~qDD_v@kWzkyrlmsss(F4Biong9Vp8C4?AkIW2` z;8#>X8Rde3qwgGJgg;G$2GqlP@+ui!qHvR9#~+;NIiTy@g&ddd#b9CLI0yW`RDE+x zf=>|qd^jtm1m7;{`$=+b>l9frr1#E?TLb((<%&fytI+(0Gn9p|OAQnWSi^7h>Z#h6k?w358cP1dM6V_;5nlT~${#nQ-03EPVeu3k&Pbcw(B!%yO~ zPfl2^M(neA{)Etkt;+x@>lW$T+3w+j00l?`Fs=ry*CQ@3w)n!gK7c>*Z+#GtKk^Ws z{M>C^Ik`HkLGr@323GB>dmeNVimVPg$(#=1v?Ha)5--y{RYk8HW0j;~G`uvg?Nz** zNQgdGwrz&#UTxgU8nV8EyU#t1Z+!pT@%?}LTX1o)E0a}V7zU)2x_z*t`@@^Tln%Q~ zPtlZteFpZ^d_8{gm+#{5{_|&X^zhL%Q$i9FyPp=J)6icVSnK5mz>C2Sz}+kI=*R+8M}3@{)G0-jER)=KUXuocK()d} zeW#D~l?{HRB;OMM&j@~A@5$9ZY)Ba-SF^4MC*@Z1iD#`3sy3*f^ZJMf$z(-G0&WLF zI^;>xMnf01#)7K%<7z#iYRTuF1gks(XA_>qIfn0i1o+0AxOH)Xv`ZLtoZojXx|byP zs`mhO%0ls@2V{)uvS7O%(uE#%5m&M>p^6`0Y{Dp_ z2O(VtKd?LxfA4wX#TTu~uHeuE3eg2Y{{a<@s}j4ZazHt$2L`re1uodcniZ2OAR{i% zGtRG1c;I_pgUc_ug1e`eINl4=G_!;g)PV(k@PjyYPJ6D2DPnv8OAn~i#YCO(M8xJ+ zBvTBcs+#*#z4uia_#?qL7ZX(4VL+Q4lqLMCe(D``7LDL+d<`Gr*QO;|TOx&Mq-U5i zz#Xb+GURy#40zRuf}Yp5TJSY%hNYb-ym~8h!%*k7GpnVda7?EJ_n>2n2ut@h#MQFW zW451YR;(U5hFl$xH~ZQ9$&HFb%Y-glw7l>80PM3&>yJjk*(dG-+kKJ1YoOjE$XtbT zAK(F&iqJ&z%Cefv?NkkIFCq!N!t)lCO)VAHq%Gc)nJk<+?Gy@Xba6?F-Q^B9UU7tf z@MF*7$A9Wh@yMDQ3Zc2mkWHrV$5m;`i(aqMFeHp=Rs^13?(ogu@-F;|fBV}p>K1pN zJHyG*Rre{oWj;;`weNN6fu)sg{~ZuTv@5V%BWqzFmU|jBe@z~Vc=ae^biSv?<2CL) za~p4b^@I42|HI#n`)-ai(OkpSpMM>I*}_HP>hr*+=jgv7DRvX^kN)W`Jo$-BT)lAw zou&hp8#GX+xOG*DOizCJ4CfPfwExP~kvbvwHr2?1vgbo6X{pw@bv;RCUCPVNqPmWX zj~cZ#Smw2molacWs;!%Iouj_)QExay7^t-RlX)==o>2f8Y#>E6`Wm4*f3%P+K!oHn}pz@e1!fv(y!t;Ju=b=v`{=6jXth?f~Kq znA$3N23Ue4?6-@h){yrsqNO<5S=y^$HA86ZsxOc=)^g%iAjXVg5U=)sH%Db8$KDbb zO4M1JSaqZt2PZG>>HkZ<0f2=`EeliSFr;{%;MKz{XHzP=2TVw=nBruZWoJPdP&cwH zeqwBdSfMU{P$;N`$e#$-@m)v<##*vQvmeJ{X_K2uwYa6K8c?ByYK~HI6Tfz+rOxY# zB10Z9V%!Y4b$JT?;C0Aveh9bs7m)pgQARo#mE(`}g{w-)nQqf4o*e6P&bDji24^H~ z%uOx9us8M*1z&RD3;GD90QckA#ASPdQaxrlOcjpA?=>7LtZ=1_=N3Pyi*MJxM*9pB zC5ycU+e{=jQ+_st3C=NDLGK+*xw#C$66Z>|##H;D-mh1`yIOc7&!NIAgf5+??l zX0&2FHl2i#M;2em>!C&&tjHWqROrZ>2Dg5(dVp!Y%9F*QD^{0>ZWl^IJ~Hw2NX6wN z*c@MEiQ_94i`nFuO`u@ja^DjaXASB9D>ZAS6rRGc1r1th1I@9mF=v8s1&fZ92Ca%> z^~x3Wia@tz>YR095hrVciicVv!qd$6>?WTyWyxt?(KnwyL(+_SDR$IAf3K)<9{j|J z_Ys-`mZ(3@a8APaLUK}fwxG3Un3%0Y;L`*A?GWPlTKsCF`>__8BevTu9(eEwx1PMj zkNo3XIJ*FbVYYS~JHj96EB~V@V$_GCxO+QTcbFrzS`C0Iraa+y|K9iEdw<{iaeC|1 zxH!AOmE&V*o~)U!heBWc`ri}}4suEW-{90~JyP&-S3Xt4A( zHisQhR!3``-nxUE#|hv6{ojpuzUyJ^wiDLt5yOxmLzq6(^g8Y*eaz_d|1al^^?JZ3 zKD)sW{*Rx-cx4Tw>|2)V;iw|@V6TCZBNWH$SxKT|Ff>G8xEe&wH9u3I*T93Fnp9P5 z%|e~y1Dap|#ChQ*sBKyOs6pw`Jmq2n7OlEZO5`xvy0-nuOXS6seV*t(*Ph}O49PVq zyZFaeyJeeLXF3E5Mazn<>zpLga^gcmC&?=%45GeP`0hL~PKpfe7i-ne;CwxW||6Z0B9DK++M; z?ryPr%L?~>&#UqD!;1a+9%~&)uBN`QTVii>)^E415z_$boIGEJ+Kfq9+fXxZG~o*|A^XzJwFmU11SA!;djGBYi0 z(@v3B03J)&jT5lbW&vA*fG#_;wfv=U4?C>z`?Y`Gz?_Zmxx5KMOd_d$T8qY?Z4gyZ88|HnV}ZTR4~ zekGpy+@~?^b~swCp;Img<@g21&~y04f6&t(R%mG#9m$g3+NKv9p zxk+4?=g5~E{QLjzZ@~vY_!eAlwvbe!702Y`ahKka+-nm$00bm0`D&c40RQmE zpT*Dq+$~&tWQF~1+hR4H61yDga?X^Kt}RB6xa?L_mUTII0z&UU&$+HKlhv{v1)UUA z*;+o%&n{{nsG_vY5T{7WTC3Sy8V1CE67|+Cjb>3_cC^`UUH5WdR(NZ!<%xJ;UsdQy z8_=Lcr$&fjyAEKcf*%sqIxJDLiN%J~kgx1%Dijc>A!=83wr#WqXd+&RPt-x>;7GJ) zF+af9cRm3}I?arRI+X`_xyC=5ah{ku>KYA7>$|CrDK;GjJYQu>8iG6-@ge9saTUJrn%srmDA0{}06*}q%hX#{2i znq2Oy3egag|4;`Fx!$~!kI9RkAM)VZ0<4|CBrb;xdg1y()qCXX1eBWtR#@h?$>&yV zQ4&G{TxkIOKtiTHO-Bu)5?_y~-Vm1<)HV&&I73hu*Cvgbrhi!l2`il6-Qeu)itE4k z6*#-O!~XmdD-6h0i6I`tPYFfATZ+B|tGyM(I)NrR7xJw3zCR5fNp(t8Ecx~iDlygd z)4LFX@_iw6oRy^p&GaOF$`?t;yQT&eMAZxC6#@qRvxpp~7Y7L?0~Fo3Tl(TBC{3)J z?=e)(KZy8!MZA^TA)@mk_(>z@#T9_2 zU60}ElM7&zF<^9w84x=NiOZjbWt8(qDg4}5n^66T#crfT*LUdxbe@>l zWHeJ@o&%2{Ljz>Tiw(7A6LU;oxz4J+ouy^Zw+1A~s`*2f1Vw{I4#8zhS0BE1(Lh*E zfoKig`Cy*fGN!bi()*}#S$BXEw>G=nW2ub{EXtovdF< z1FEP^IHt)uFv+Y9B27BWY85#7cTbLYn;{>JX$_M!U~{p-{f`{sqrdnJ{?R{q4p{}O z!2>91V?<;~gA_?NbWm!7d$pI5+UG|5dNpFZ-Q$(7xQ0LTXMYFY_v_z<&wlDt5Z&Ww zy_%B&Bv?t>edu*uN)5v%b6!e?6qBmgwOzpb0n2oQ!iu>WJS`49j%ZhqMuk^{7Lux^ zT_4>;WRLYx!s(qe+`WBQ1Nkd z+N&@bD(V{T^>8l~y{pV%%4YZF{kTjP#qqv#`3pHvnGTEdVVNC)MSHEl-EUO=2q9 zUMN#C72lS1u966nkPKUhu3d9L#d3-h!@OiEds5p%gg~`!hwM77Q}vLP)|m~(Wy|~} zZ&5O7VuPG%MpnyLCi7Nniey^sayIg4C3rTEj`+o8UPnFs+`PWPDzBiYiaQT%aR2wb z0^2tvY);Q{oJT-sIADg5Y;0lU6eHBS@uN_TPI-^%z}(p6vslPQ6lb?kH8=cUitbD<}6lEN{z@EUUtqsG{NP_>RpwLQ$&Tyyb;4co5 zY}Sim3cvb65b6|*UO#yve=9n2^muEF-ArkLvdwqRg7Za#{!a8@|J?ZiXNU%%c|sDw zFeRLBE^y@o4XacR|KAy0 z3PmciZYhTxmv%((E?4lC5mTk*N|}yYlQ4UwKrM!}6e?v6onuV>AyKNQpFE5;(zJO< zwH_$5b6M9(`y9orE}yy{99#x^+r%lpNI6^EJi2y3Pr;s%=Msyx(eTkR>{EHeP_(G)$zhXuuQD?51b4 zuA>5X=Q|-cIduVkiXczZ4CXtY@c;hbp24R+v&U+c%HywX@+v_tYHPCGQwncG2-wt^ zlu8Q4Fb=r5*y0^;eF#7B1HT(z_MW%n)1Q0-su{;e>oWDN{Gsd`p-i%uy3SVCLe1EA z$w=j}?Fq2H_bUr0YF@LlShS~lE+>ERXyHO@KxfZc8nLBeVr3fPPz()0zzGxkw?6lg&&F#*UEFr;}U{Kr3j3qST_&*JK1 zS1|4OCAKm=56((hz58BSCw%xX_iR-Hqy!`yU*^_~C^ON42f6v%pw6Op&kH4> zBzkt04}J6BODS2MTWZ{SN1%vJwfd9?NBmkw6f`Ai5=Cm9sAT^2;!#h@_VUI}(N-A~+&C=?Px<_b>FtjTvLAj-6@vi$#YTIyX2jMzo;q~8`G{q9D zIbfXBzI)BBCeyNa)z@y#`$n)Y3R&WIIhK$l$B&q!Gi?uK*mrDFD#Xj{)khPe*iEWp zq**>lIVTQ0nmuoz{dr*@gQ<>_wAjct7%kTzr9?BI_rMkL8J^NGv@-DK<~TWZoj(w436}J+^rf zKh%{lA+RP&7(=tdKqw|qst<|?x8l==)aC3-`|}mVlZlQW1J!(XI!uyFyn}YI;_00D zU=t4zQ&LqB3D|or{LgYfcj1?#``3eQ;1kfjt(`kUe5QQ#R4(McFXlW8t9B#+p|pW& z#L)=6GNVlvoKnOJIGGe-d_zj~?VY-^~W&5GGJ^>9>lbr?rRrSia(j2Mrx7LWC3)<8sr->$aiJEoV*&q13 zt4BwxBQs6Eva^M>f?Uu|v!!HiZ?`)<^y(A**gt(5|L{lemV#5U-xpvddrnEA0xG1P ztuWXRnKQyEHubqoNidEhF3z|3vUfd(zxG#uAKv@x-i6OT@fqxQI~J`>o>7 z{QKXIfA`<{zATJI3UaDY}As za#d9Yxp?iZ@8RPPRV9E2Qz?v%ecUWp1b|72!YFj^4~{URWc z2kF`8^4YOM#V5r)F-R~(hDAwY+uS1TMcj)`Z;r@CcJ9mrf#Z#APANLz8eiEFnVl5=ZAHlN`2b+99Vg zOR_V3c3qi}s950{Ek+3Nt_EbytvG5$mva1)HafR~3u}Z{o;~Lz!{iv&cCM-olD%TM zk}$sM2;1Wc*kwqvL%N^!dAQ{vW1KF6N21?!ZrF_zaPi?=NYfrNPR{2#7nMaCl(k&wo_^86k4Fhk-TsW#EdRr zalG8ttOMLIDc3nAUxhLdIcFeG7>@>EJ>sb+pT=qg{-gi!JMr)Qv2Vm~zXdQy{T0rz zuQm7pCjDH9Etm@5%u(mKCxl7`d5Yj4|HLi)$d7&&C$G4QX}2d|IXAUgMrSc|+5uYq zy4Td6ZGG{SU6Fx9{pWEo{_pg(f#{CE~bCEO;m@=H7-VVnU zyH*pnYJIA-$JZ;JwA~U>Ej2d}1D~c1#@Yz7E_9rXkC9L;(WHP zlsu*`@T|3cGXlUU8u$dw*px5=1ZCpi8c58(E-E%YK|t%58Wn$!{Jm*)4%bbW9P&4TM* z`ivw|p9auCII2FbNP_PyD1_=h(vzl19nobF*q#;rm+Gd8+E3qwvRs7>Qlj1a)3~4q zh>3ERx%lF%jsqQ;K6@jzl61=aeB~a^1eR$NzGU-;D$(u^kJ33Zxlj`KKuV7_jKt^A z4eoNoC8&AaH)RbCE`F=b)g&qz0>ug=E^b}m9e!+gJC{EihtqFs?RPBAK&~ zfUB~;a>z|VE*>()=L2mnhac^dCF) zYDe<=z9j+44)WT3z+M?ZL_=FMr7t91$s*-m%_JdhErDFr5xEugx#hs;*65tOnPq)X z>LO6x#E@v+O8N4lMwSy)+2#Z_v|lG-Pcq&E`hi?n%lw8n%r zp8V{yc-3P!@mK!R_uvox!T00h@)9{KQW}u6y`~7qdJ*S<6P-FQm@y8#1lj}l>d9|4 z3Qo@^{J;->3`eUKWK8W|ZU?-q&DNvhV_ub>d4MPXT2Dm|Bn<|alAOB4U>G=1QTBms zO3C`O3cRzaj>H%!iJqqo~! z;val243_Xf4&&Zy^;~h}2uu5DW8oXp#b8Ci2DhEtAt=NXr6n?W&@Z$pmp?42>n3L> zOY*QalCu{8V)N>VNn9n+R=F~4^HYi0TilUW%9!_5=2T&T0wftAQ^J{^;p%UG2xqTN zxIEiql}D(ia_$rdj$>*l$LA>>RN7W8_X?tq{Br3h8O+^bVQJ_EsLBF08_r{<5{dHj z*)a=w-(Vg(1FqSB%OakEMV5{E_Z3;h{~gQEYyP7U6R;*Pz240Z?M#c z-~69a^L3{*GlK7)UEpu% z-~U^2<79`YKKofD5v-4nATrPzz66|uSH$RYVoJZ_913PJ^^E6`Yv9s|!3EttT$KQ} zPYmOL_0ciT&N80<)GfT{i{Fa>{I7i2b$U0brRq<;#L(^=MoPmDw)Ysm>4pxUTl~54*QZz;m61WB~2@Ixt|Lvg84> z+AK>_61if9#6_KO9*Kl$6T{C{TueIK1k_WQ`@N@GhjTmE)O(gcqeis@pCu@+x78g^ zNTjs?2zt|3s4dB6-P{Z6qhy1Z&Tpe~cq%5P6@=Sq$2s#usrs?Oa} zwOB22w9J0W)x1lff)>&NaB64YgC}MWP+i=Um|Q41xSw?#xl37pKh6)v9GVtVQv!;l)^1RB>#nr2e-Tljz@%!a6YOKS0as2+cXZ*n9w zud$K-38ESJ2afbl2|4a(yC$O2VMyCO%XIGf))=tcW<2!xb^NoRxQ)O4cb>*DBn*Qz zd*GB0r>(l{5R*pKr)exzo>d7@Ki~Rj#Kq+f>*I_+@Q1z*|HWVbKD_;Hufk`a_$)5Z zw>VxMVKuDEJ|Vr)9cJ5$$`BhAEFYicfWt%`<6tp2&#B3*6d&d&V-$PbFkro2Bgug0 zp1Hu~ojv}w-~J8wn?Lvm@EgAIO*lKfgsNaQrWOIlww4Z%xe#81197xmX7CWae!o*z zjHBSQpT5AK{=a+_$M;`@f!wU%VMa)^TQq6A&dp%v7OCw#b-IZ}jXtgWjCLuHnn_8Z z{=_d7ZSAa}2zFLAmr~{iTcX%ZEr33X^R&-tgC?c)s%B?{x@cRaxL9r0AfKWFX5|#= ztp<d|JUsT1&Is+Pp60#em_n4M?SWH3*H zBN(Tfa$s=`39cWL?<2(Xb z39uGmjq*$A7wp$j`**<_z^eS>e{R2I{{Lh9rR~?M{M7Dm0J>BXYa11* zDs-B802jBm7+y2r%5QuGXZH(a0}M!(2V@zYBSt^-n zLJIG^@5KaEl+gd&8^^-W`m*s{$ z$>)iDs&Z4fYgj4Q;MhcJULg2JhP-*OETCdj0- zJa^%gqpa*uc>5W9e*qT{J4?3XUNhftA7KIxY&N0rx#P;=lac zpTp06_yQ>j_Iut-dWG+QEeV~07vT)c>?yk9X{}ZFT{WU-~AE% zj_>{e&QDMA)YH!(=RJ;w0pn13HJWR#aq|GsZx*O{eo5sXB~F&{`T`%FNhuAgVJOhnYEw1{xFO6@-{K&@b&Yqbv+Pw= zs^oF!eJ|AQ?K`G_c8vs%1C?IZ!weyAlyX!zngt~uIh53V+)oI&ZMyFymzI@-4yv2@ zOFKh&*f>Xb^B_PF)@KG2)tZzC4fHGkus~10G8B8MhO=i;JOe=GB`pUSUy}ZRI;1)P zk17;{iy7jgN1B9LktnuE%9EQxe%vlXNt>6q{+UdgDdi4Xg;|gkX#VZd{3?aY+&Yep z>}vnK1kAD`S)4|U7m*T7XrL~V?5cWsm}<;)H-E3SI%&bm+S*E$u9{IL&;#yFXIOpv zD{%h$5qW!o^?rW9vZxod)7$kFLYnAaa;kc6U5ZCF`iyl1g@w2**iI6+a9B=30;dNe zm6$l5w6BHd>|p6hWZF+>7}O>QrZ~D-NKp*sq3KUN&lxM)5nZs-c2ap7{7`b#IUVTk{b1&$|3?CiRF&E zb&l3u*CZiLxR{SWg3?+&CJYiQx{$8+e)HNlG|4- zyUQrd664pzPwOSzX~Hy%M4KDjAyZvJ&i5f*sZ`))?f#K=SP=0GqDaED+vEO+*7((r z-@y<5-#?2#^QT^iVVs))<0>u4>|bEbs8QV}-I}B=63hPZYBgZL%Q!#Z;0>>P0Dt8# z{Z@S4*S!^g{?Gp%eDouq!0PxI*RLI8wG!+nX;3$9vg=&#mDJ)b?d}i5du{zy z@p~{R0^9a*w7n^2Qi&CCu95BN^%|m*BOA!6db4MVu^LN4qEStkY4NAr@wfMpg$ACB zs@sHOBDV9lBlyq52FZX{&>q7NwMMNbubggCxa$AB5T`|h(oSL)wl!ctXE%2rv~!Jw zt`xk@h3%9A+|w`gas%K+MF2#wTe+z?>g1=aq8>yhjlk&-4Mr$#^24guYD8=8Xuh_Y zAjq=(G_Ow0)saFOB*-=P(v((O;lfRfIMs6**IX?}L#VRROuT{9Th4Tt zNsVl+&AXq%AzuWYEI85kkV0WyTMG64_(Pt!oNgXu3#1h;1&WVEK0uC3W5iAjemGpk z`6AltMD0fq7WmVbu12@(>?B>dvIgHGofU^+#B_Oyi)%Yv`<91r_hV<6KC#2v{*`7OB?}PTr=5S6W1KhHUU(4 zpQYNGsQ_d%rxTpKoaMfTh`H+SCIJk$?*g%?6vK#*^~FuQVu!aYVkO z*nRvQCuszRauDHZ2qn?Dm{j<~fVkq+A0Rv|Uc8h}>?vg!q$G}OWaSDUI^fq+@sVso zg@0ywr>I{Y+Gi}EJ0(E(xc|{L{>Fd*IlS+^H}JuK=>hDg33sa_cxa)zDxaaV#?}B*`}}DTNE!gm*laeqINe|z1@C(I z8}Wy}_nYv|-|{wGyLN=_Ub>w|%O%#J2DmO>ypd7_H zPO+vvfkY~=M!S)ktp`D=!oXMG*eXMbs-{e(RiQp*fR%k3z7yA62P+=9m=vGPQ6rvn zH?4B%FEL76!q3XlTStur<@nQ=8vrl%27o$muYK{U1VLoyO%{?jYg-psT&@%+0`rZq zgi-ClP(Arc{B+$Nh)SW7!jv;>o;^wR=A=$li_+>Q|3W#^wm_yNPAO_KW4JgZt($<$ zQr0%z62NymNouMNB14CFwLFrHj8z)2+bHflxW|Lv{5Vb@*dw1#7%SMX=7lc_t@U|; zZUmd5ahB&YL`mg>bwRplENR0Fb&-j15SF@FZlTy=h6O2rlqamAcx09E*f8M!alrL) z#4C>mylN%5Pv+utFU8w8pX}#*r~JIBp=ipIm*>v>9>5mB1i?fxd0Bv+Dz*^pOIFyV zzz)ErDmJRK3ZX*!^ZcG`tXM7wfSm#x0Gq7Xi(<&u44)vlKrXK}-`x}Wh#5RRmGYX8 z+k0eO0?-Mjlu3e7fR!YS30Pxje`kWF8rYFvrF zPPS(tsdfdjmlSf$lLnhZo4`u&+-i+4RIr=B&+C4jf(M=l2>VHK ze0{{7TU-3u|MXLM`xjor8(+J|<>emJkT8f~7?uHyovrE~?9$GGc;J624H(A+42o%= zvD;<5>eW~A=f3}2@IC+9d+`H*>4)&Y{M{eLQ%~K7<`KsyYaAWTO&uxm#nnHJlp4#Y z6~2`Jt%Qm6i>(qA$WOv^8zdAa$uL_(rk!H9y})j_$MF^5z3+J=e%E(@J$};%zW~>+ zA0g|6?Pdo_f^itzgG&Zx%6SefIwA1RSpi{F%S}i};a$@C5FA z>x01V5`wW1{)wd^m6)C!4N_T&#(8a_*0Ocj=*)od*f$7tl08-*i zlPhv~c%<(2l-4+tnk&`Zyq7vhl2~(<&ULOY+t%kc`_W*7p>;*fGhd;laO-Z!6w+Ra zmet_6rL=O=dFGrrRY&_iv?UKHuxK%3<&i)an#J~d+|)NkG9AjxcaUqFAycYZNMmtL zQQBLqjw+T#r;ZTE1PJ;tYOp#y!EBEraI26KxI%%YSqpSQ?GzNP!U_hmRMa&mdxc*w z{UTr?@e%6d#ujc%dI+DD!IC2P@SeWh0C*7!0Q9iBtF!@-)Fw1-$dp_&0ImrD=d)_k z+ayj#+9nGzVyez8(?rtGDT>5>3O7G=IHjk=dh!u+Tf$0)9-11u+Nkv zP#O*UVtc4Qw+(<>S@HBv@VV`TXEX5W%RPQM@A2%#9;e7S&lyQ3tOvnSDX6tH?CRu! zdh*U@BVce2f+Zx#q-mmJwNbPJh(EN&zc2pUy7XyLU`QDD!0p`yj=$kSJol5&Vg1u{ zuC=CNt}at2HR>LU7S9wKlulwn>y|G01j9?G09yhpkFcz~(Q?>sO9T5+S(3M)*5IjY zE4ac`BrbHUHmXEzvpn=5CZTiyRX^4BDu(&HPXY&EWLRRO4Hip>&{VD21ZYYE7h7cq zkoNpYphUCMK`4eU3#R=9ecc-R&TBZ|?J!nOMr{@bsHfikvgX zDSLGEQeL3icyw{W{Q6+Y2;9WN_2>*;`LaI+2!+AcWZ#B2~}-DRt8u zyOt9j48%2N+8)-HgL%RzUV7w}03t!SJlgXh?rRHT!RVL zawyGvz<#sE#l;4?RXlWa#Fu^L7vOh(*Zc9!AABp0k5+(WTx|9;pC%QKL_)C;DuP{l zv4i4$&NyhfaEfHMwotTKR>8@Y0YCCjpTU3f7k?2aZ@3EVFXuzDPLPqk&RjRBTSs%} zgQm%_usly4fENVXvuAff@y4Z!OV%jOghT-9dPw7av+}7j`K49e0ndBb)hp6Eg$i>Y zl0O(r6y#}(dGqQkpT1Qm`sJoCYwYCIgRVYr_6V~YHpLw{de^JvlqiNLO8EHWqG6H-7wdu45^jCklqhwLt0$7>K)DrZER)hmIi)|#QGp=&m60%|CD{8; zBY9VAHZ`AXHMcBN_Ev$RU2XHpeqmr0K*@E^;=6|07Md8(rcQP#js&WWxG9xblojh? zjm_N&+gGn~{p%jU*--}WD26GKM3g4*EV~6o=}8~gw?ui@g{hY~Ih%S<8j-1m#y#pF zUWUj^G9P&FAY+#WCz|nk8Sur|kMXtlt?<5U^MPMvrskFssri0$);`=`Bm0SC;liY- zZo1mlh%VkP{$J*)yTl(oOb3++2B6 z;cSNwU+(ZLyDgs1du&s}N`R9zU~RXorA7osf!~NbNr0a^O7G+E!B*Z|&bT_^b%W%? zD3M~CCk(3*mv?tKe(e#~Uvm?uA9)Hlc7owZuon*`QtE;?U%1dLryXv{cpx0hZQ&QcWkImEJA^VBsH6SYD$T=yn9Ehz)a>xp*nQ#(1my0|FIj`lvm zzJbF60sk6FGC}af148-W85%L%P{~vb3*}o2vUzkhPW|Bc15E6fB#^x#uO_VC`Vi!i zBjl&=U`Rt?)^O8uK}Dke`PPNNKTB8;Z&x1#NfXX~?is8mg&djVzB_1HH=C4W;&>P} zijq3t@YoiTHD`sSp{YvXr2D+5X<#{ogRw3Q0^9nxNaJ)AJIlbmViS5&B#jXckFH=C z62@V077aM*b^RV^I8LJf-fU;(`^kGCPZ|4ZkBeOfCgAGvfH%D2QGC_=-huD<)~~>C z_=eYFwNA*IG3EJATaUxioPLr2ftP#f^=F1$P=1UZj}VL?r!i0{`5~H zUq32pglz)@=~VpAZ9qg86Y_z(2`?QS{r;_Fg=Fr9hCOJ0qnRvFPNnGhF$~@FjgHhn ztm^+Pc-_iQrDJ?6s|6Gk|J-U*kV?Q?D)?vdP&d{0O^l{Y-T4S`T#B!*89e_~XnpO8uE!V?;_kO}I?(oj3D!f+Ymd4fg9`$r; z6)$CB6{J(0In!dW2LyFO<5WT1J!CR`acW9#?%MuP{%)>ueBz!57YE40eIeCnW4xrm zZ{z8dI>mBVOTzG%!nYmL>5~NJA zCu|s3H_aihu8Y)B@2y!751Sk|^(6LMNOJHR(bgrx2VpXGU8Kr+?+jEa1C)wkF4C0P z0!p$Y0ww7}XA?4FJq>vB@)E0WxF6fst+2nm#5hTl-bnO*#;9+8;6oj%f&4F>UKXw{=qkSHz7-y>-*Ya~`Eu{21PtIf zqBuj71AxO&?ZmGCK`VSb5F8vVcTNiBd+r`Er75_u^newo|5qU~m$qo5n*zM&O2T_i z67b*(8Nkn7?C`(d-r`5k_W1aA!c*G`caz}SC^)JkWO0f)f9b@7=j7MQ5b1jdh|$XZ z)CW!tq%|6F5S*QDaP@0%;@N+E7rUQQtX4zG^wqXG0{&T3mU~ymL|cmzQHz!0I>{x^c(@rj`i@s&MTmjZPGro~dY+C!dkLd&n`PgqR1a`WS+L(`OxJd6Y{>k0qf z5Bv(={+6rw(s!QV{9;0ycnzW^V~V{g?Er{zyLNE5d#q#gP&7fa!4uPJ-q&{9J+|25 z{)boi*Zz@VRI2;O^a16 zXYtXWy@l(qc>uD%fDEYv&$gOL+0QO!kJ9;CWKy^|PrI{+#%-T{I@}PpX(e0|j18Yu z)N!2Hw{8<)wdbQk@7%$wz86amNSZx2QaqAWQk|TuTX`}JIIH2h<;V#DHFd=w_+~&X zXQ#s1cG)UnusiLng2I46VgRv%Q0e;=1h{Iw0dm>zEmN121+o}T-NE&;TJnh7Cw(2_ zlvYiJkvgS;5LZh(g@P^&PQ*1-+z=`fq8fac8*_eDs}YMc%c0&2iSx}}!jIz{1(<6N zYDI1Wy8vhQp3nfc#W{xkQAy1m6?C;ZA}>|_|4X+4fS&jpi#YBC+UeI~tIETdt4C8x z$2U(wO0RCgtPf6homuZSRhlp$){qL9DJ|XPzCIwiKsTtP=2S|4O5!JfMjdaTWgd%} zcPDfMITA*~HRf4Fg}`J5#&N*;?Frl0t#IY5ujBG~58TQ~IW4jg_^`4_XczD44$l6- z3{XeA*RI?u5>=k+@n%bw2o>P=w8v!z9+d&_IX=R7J$eOSdo8us+cR8R(4q|)U=NAe zn};k`pTc)ZPPaID%D{|GLB;d5AR06CHp1xM@(slGH;e$ZiE;~j0D$oft) zOZ-XB#^n0uFD0LyD1)ojH3VD1k^N%D5<|<>tzMeqeUw?}%#3P*3b ziqo?lR+7r0LVZMKXkacrlU>A!EFPLFoj>P{qmvbOKX;Dli4Cr#u`vQfBG`*1)a>`q zrNCs~hL))`=>Fya86|rFo6!`}U80np(DvY{Mxnb$N!1sjfRO^^@KT!FK`obOREx)5 z%e}VQZt=jw$N1DQ-^PFRAAbyg`7eAS9(pk0^gLs|p07X>gj50RoXJvSm%r8eY&aE% z2L@_Td{nDL1BgK|tVW1rpd%ymb_;tVGRmjtC#7BSWE`Ic%eGDJ^7azmN zKmHUx^Te~bb?Y`x?_A(wv%z*VVVb5gq1TyJq*_2O^F1*Rz-kD8h~F zS6&0!v{&KP6|H91{yC=C0y~{&Z`E#+#EqjP2$18F|fht4>YLXB|-ziw@c_jrdi?%`Qy!It~U)c`e z8dwxA;j%UQy^4S>D?ZvU+8eSTx~lk?Oo)S{b4r-tMIm5heIP3?X0{O%YgTBsnuSsZAdLts8nQIR&<}Kc2i*M!e?lJCW;$oHQsmv zMLP99e2GmA_9c@oN|H@1B!Yt29}Fq-eT(Bnhy~*O`kFUyzE|>>*B(NYl54c?WGnca zgtZ<3r|+LPz-Ab6`4`^6aQ_Osr*C0BEFGMwyAL&}QZ86Mbk^uo&8JfnWU*S}?mIq# z#a+c>HDDBdN%Ie%rC?y(f)s1RH&=KE|7M*?DrGxQF|g{34~=y6;DHX*Z;@2ae&rCX zspZ~lCz1BtovhkV!W2kJF^(B2o#4i+*Z8g6XU%iM~a@iHoe9DTZg%0I5h^g}lOxgdI z#>%m4m8l`XF{_1rFcy_kLf0fT_$m$X)E^3MSg64wW%q-i4hpkV%2`sby5qGmHhd&v z1axvtHG_!zg`w{4#Bgcx=oAr}W+IhBQ^a3emuun^N7~`(6$QYj$UhX^K40?{IA0Il zX%ocOMH4qMG_Sq)yExAx(><9xO}WG^GRVbtsoNNA8U~`siYxGaNvs{btW6G!{IR_! z3GQ&!E?&%gk$O#qlmlAaj7MZF(-ON|JB$x4vHE34I6d4#Z;wbA+Ei)QyC*?NG&B9` zUdg`PW!FJxIi&>okExPc@_16*${D*6_}tY3zx!2(_-$90NKl-PxfILN71JpYW4+H) zW;sv7KIc71K$%Tj*8Ky zBz*0|2l$HnmiQY_ZSVtkH+X!jI7ot{0g%yTe0dI`??mTD^6g%LD+GHQFyEUZM5ZR8 zN-b1#$&gONfX&G{^i^9Ne#uSTe&2_2&sjz~oF?|QI5fjTw&z+ic_RTSsw9rYvFFw~ zj*T%AMSm(Tp~xnQ>bvZ9g_;GV(bU`#`uVd(fVkC&KR=m=%dpl?^3MqX(nYbV_w`bk zB>cN5=RRvcXz<|XD!)|iI4=xG6`C5lXE=%ZW?iVIbM;cjcWrzn>`pWC&4kr2y@u0q ziyCjeNtyGiE09Wy zm)H(<$-gcUNBMW)ub?jD2tg6^xr$mtt8!S{9vJp5pZpOb4k%()Inq~i1G#ZrT5W!* zdXKbC#1z}@h~+Bb%FTpt`Hsi%hR;02fA`yNVz(JF?ttaexH@SzwhUoF!Hl2)=#5dl z5q!t3)kGjKan%5Z1t7zKyqK=l*;z)CbEGs$yf0k=KKFC4<8wdvo}af0bGzN)`1rgP zmO~n_Uaux=LYwMNmZS*mi|$Qfz6$#Iw2QVM6{E_x_Nw+Uv&MH&V7W{oC8OdS>e|KErZ&lMP_lw|hxYqCx(QIXJ`%oZ`Iq*$}+mRJBjNx--`KDt(C;!JJ7 zDZi7%Chp9<%32_-RM_WCjfkLFiwaKlr^TiMR09vY^HWN83L({jsSAR&SF|*x5>4-u zw+ku*XT=8s1of}0&4NlSCW&zLv+IYn;l|5X{{J#f0gyeeQblru0qNp)vIZYXAgn5S zN-=kr9NY>$gvK8hBaWla!_=A$8gTC-A!BroU?yGKhKCtyFu{dJh6F+jB*c07oY}rH zrbtP#kc6|%h{G?sg!4Bpu-$Ai>{27*D*8B(SWLUa5wjJvrFd~{bYE*STR`b3#hK$R zddlAIC>hW16!#7b{O@lZ;H&Rn;o$)|8K+pg12M>*Jef2ly<(@p-4VENk?@U= z9NLOu1gl{OB?QbZwV>EaL3o%I895Vuo zVZigZH@NRtU%{Op8nAia2FrtkNhN^nGsrdg$^-wyt|d*IW^#nRQf z+-u-O6X}{mY~!)4cAb63Etw>&Z?0j}bfeU`N^L?S!ahH{$Hg72#X~ad`%NG~sFI3_ zl@SseXPr#R&2t6fCeS7d@SrCN2S$NHcEG4RtUluk)}MI=FWfnuU}!pm`?c;-6eT*l z%v)4uibc%yy>?^9!SzGz9^YX1=m{<@Rz{_EHUsg#U+wd-EAiRU;OF8i1tFzEoSQdM z_O;!YwsvdvC3oLTo!s96J>$Kr7KJ8k1n&}2?7!OKU-A?Id%D4;t4rKH+2Wi2+NE zuf@89l-yZL=aCK+_hW0N_q0n;yn!12_4{R7@x82__g_mn(};m_O;vHJEWvK1ZtQRO zdrB<|aQd8x+u&C2UQVhSk{00EWZ))LQx$mDO@!U@1jkqi(i9-m<<-FBu?ANIf+M{w zEdc4|_J$ zUf_Rx_!9o?gKIn_iaX;Z+9_6lkVs7e+UeilS(5Xg~e)B;-pS?iZ41^;oDzx3E%jt1H3r_ z&uqqtYCT8`^~@q2&`^ESyZc;a#zf&hizrzj37qg>D$jnR!Z67Kda?x`7;*5+?!oC& zAsYin`SnCPLxS_Pp(cEc_8e2;A_VEI>yOYhvqp2-x60wbs^Mp8pE0ZiPAX6^7aPi1M5dgQxr45HTJw}X#)dessYKyb0fM~u^(!rl+yE{3LOO75jejNT>au3*j@s#$rw^$HvPm9%nv$sEs^z=A|xKJZn_!*YI$g1y&eXn54iF$D@dzwMJPGVRj6;Ta#S(>#f zA?2iU2LfT+_G*E2O;zpI&bIm}O`@wJu>M}o^&rt+-*&sfLk|F-c=8th=%0Bve&TJ% zI6PWF^JEoJohFPI?OXfyyv}u`ejU5;Jme(m(|ISQ>EFeoLP71l<~&Umheg72Ibc!$ zT?|+(7g#J7)Bmm(SRX8KaInOBy~M$Kg;n|Q)zN^(GSPZ9eMTvzNoCMtaW85V3+C}# z`}ab*OQQq-p^@Rz>z{rL9(_v1(pUczD+3Fus=*D~0;N|8KlKh2FIKdp;d zJy6n>?jn8EsP^l9w5o{vUZzTIgDh4eamo`zQI#kCWl_~YXi|F^xJFa;k->E;tL@jc z_qJG6Y3Tn9OWNQ#M)sD+qSs-B>*rmF{z8AT4(pO=Qw$OLjp|`7)P5hKt>Nz)11nl= zmB85C=t*7~(bf&|U~N7}7tV+}m?`PFctT%pyP7K1ED{)~_>X5|@y+-49I1UIq!4M} zJLeXO$I4HQ#YI(xaVmmqOXju)YQ5Je$$>v}CjzXBt9EbJW<+)z8=-TFSE&XzP9|k# z+q;uBBEafh%;bZ=(g64*?f}S4(Lvp_#M1}{f_O=#N&v%&m%jq`fQZED3{BIMv`JSV zr4bZl4U&>bP85J-BB9z)vZ#2lnLC1uk|(ka)+iRVEX3SJk=h9~pQW4fIKr$n&mwfd z?&KVcU$n&OXDo5P-C@`%Z1%53+6mXOw7OYa454(TtJ1on{RCW$U}}dxUwonhVO84c zc$t7Zs<^uae&gi>e8+3A;x}Dh;CPp@0dSZEgHvf*o~#5JaGRLWWRpFDS~h&2;|h0h zZSP_3NF&jsMD+ws?4)__`eI!>O255s*J7IQNX+{b%H~VyGy%+Lel+`8>WWr@b)Va{ z6gwOx0hTFYJtUl|;&fDe#k~jkE3dnZ|K?J{3nyC~@1`acRFI2wJ}XTC%C#3h!O-DN zI-Nz`fUJm-J*fdglSCOT(y#6zLjq3jp5gG9-@x&Wgx&eB$w>tdrVtHpzb`?D2-QNG#80o3XY zRBYu$5_q0~5}s7ub4P`1k(f$KHUvu2J{wzt>`Gf9Euo1q#~1S!lPo;Y6KAiIT#JN+ zjb;pQIKsgfT*cixCs?NWDX$eHee;j~&O#G6gekjafNLrGGSX_m@@&BA&pd<0W`wMP zTp({eV|4%4W{r4NNN+i!&p)K*2z>KRX|v4LXX<3HK0!s}#E}YJ{>XBPvxM^@OWTnQ zHkN0mrZiMgTQHiOyc7**yB+R-?HccX`xE$6-|#lP_t9e<9xO4Az?f-bC4T#teasd< zM2ot@rQM^!Pp7(PpMcV}64wy$TZ6hLuoN3aDmJ+x0mCr;Zz@(?Nq`I`hLGd!X&+0x zmIqI~Z`e6IHVtFEY`x+U*o-2QBRAK?}{VCyG5CM=3ag7$PT-W$w+h7Yxn{cNP(dU}?GrHx#NS^{7i*@CqWoSJ<`F#>nVlmXXKSzDB9BL*HA$ z0Ct%HK8sg=slF`j0587=Ktoo|^ww)-pK5Ns+QOsM1*6%wV-hPbR2uxZK3BGNYTYzA zd3cl3GZVUu6(}Ms*-`6`wFN8W<0P6;Rr!&U?@d&x+idS!Y6dv}`E)ugg)SO793cbN zv;rAvIbb{;k*_Y1zu*dXS4PO$Xjg<{I;7)!P(dx2&9rwLkG^QXzq`Y=&F-fO*9c&> z0B&o>@do&c8*BW>Ce6W?D)l*gt$meKbO6Ik-Qf( zQ7A82K_gCyF;idsoL;kBl-2ZNDvt1N2T}zj0E_b4UKkahwI1-7Uw0LM{N4lP(;c4Q zWef`;0Fv7G7ugpg-SsnG{K9G2^Ab}gd~>5jiJc#BF}!XG`Gr?-f-TZ!gpSk6MRRSU z_-PT#aND|-hDmBo6OSgO)J&c6>?{F-5{y zphQ6>fh28HEEUT0EmGT*>V2F*<0ZXjzM`O9Pl6+dA=LzEgT>GN?vAHQ2k9Uw^z*t{hGZ>G!_-L(&vk zI*1?*GW~i;SS-?{02l`Q-(kQ|{&$fEERqzZK*BH#)(nvS%Pn4tnNxob%Fh@19X`uv z5^y9;@B453!;j#P{kivHaql%8Ed_Zu7U-XS{ybd;M??k)tnyi@+{2wuUnwfEw4&-- z6eqYb$;c}iv^BKqO{_2&9k1!)u=m8oAfAViYKHk72#Q!ZnKZBA6hl}!BS>W$KDN&- zHHAAhD@&uU@+c6LVzqK6!`)pIXD#6Q_7;4G*6zHf>5dO!r}sR+7^T4?83_L#<#@+Ha3Fn*b<-QZGaB?uNhIFc)hWNJ|5fLz^kAk|kI z0H6F80I?fIXJB!TY@RybvQXq-J5JLyUc^szt#eq&@lY2>Q>S-AG+)jmjos`-5S1nk z=I&=Xun3cd)jF4|-m6*3pY5S34{n`sxmJg7y!og;iJs%>H`_*E9^ge1>`r#bpS{BN zvzFNAEz$`3S=`|Zvwf+wSevlZYQ=H|@G!G=dqJ=7M?R z4`0Ur<)I@iwmaO~Wu!&m5tC#eF-tti+l8AbGIzvOZ+VFBk1D+<`7l%_mVs_@`9HrO z=LZAy91|Rq?j03wd}Z$!7Jj}BHk3e==w?gs9dEZGXZ1)B@f&1??Nt>+od)@dR3h&s zt(UcXDdN^5W3XsH|5QLLsaQ2oT>Nufr%sNcBH~YV#1Db=IK-w8N`j8CB_-}mJgrz} zC}YA*22nRD@>4&!NUP}{N!V>iq=y$c`l9Q&dwh=7kk}*1NX$pl3M7U!Y4J*H@Nw3OyufLdz)fE_sn=H6)EFrx zGJi^`vjLP-A8U%rerbQE#3pD>yIHSrXHIwUwzlHsjA0OLHglN>bIM1VQ2~yC<(6Q-;ys1r_%ULpDxz$|w+**b0+2dGIeCd?>C}A7WnE55@IU{fkKzx1 z9V9^U zs%;{uT9ocRo#7uec|FN?+1?~|N?+|mIqIVaG#Q$=N+ zv2ijp#+0L`tfNE+VltS(m0t%b{FJX}EyR=ioZ4%bR3d{Bx_V^w3X!lCvMXU+;b(h?mrB;@miyh^|q9ANV*1Z!V z67PpF?RQi5+-)$NvkKMb>FR`@>H(CUad{HicHb?OrT=LnIIQu60j zW}<@vTOmGQ{WMD4HOT5=KfW0kJjN~Kn`C#QUH)?t!7@#%jD-kxsyH4Mzw?13{68PL zgwx-J+A|nMO&($lO3MEkTpJOZWc*}y zC$9|4Cg3XFMkP6H;}{g#ekQUq>V!o6gsCwHs*rWU*d+7ntiW8b+a8Hl>?C2rf1(3ylw;R> zZjxhXQm}#216akO@SKMxNk(cO&T6UPdLcFM_B39~ttn{}r<77_po`;i_*hz1(3CY# zpgggN$Fv-9cD6$rc6jiOhxiBI^AUXA|L|@+{p^Utg8{o;o~F;)WavT_kubMQVteNT z{ELLh65b;&IJefJg=wMZm@fX`?9?y&-THeSnit)cx_|m6FFK2hWCwMK&a7r}&$%f1 z2En)f{g2=ee#5&lu2#5oMR2}7#UNwjsKlffY4>w#GW%Mv*IFg#3gYAS#{;EZG;3|9 zxyEf#2DEdXwjoe`o*L(*+EP_X>5_?eqXAL~SNw4k+ejU0qV_i`du{mgvsojPYtt_k zGCe4=kj`={PSRa>m8_O}Go7)sHU2;>5)mH-OPq;Zlyf#4OBTVY)Ag4GZ|M-Tl`@yg z-T7JDS|?8Z5MxO_FJNk3iP7o>T}ndM-1aFK$KBRIA!SAG21~9;hQAluWWRjYx!8+A zgDP$7b*A$_7f{v&uqmB3cxpWd@-A^8M#!*8m8%UMJ|Ug}$zAplj)Xd2kUCF8zkGw&I3~>Ds9pm?J6uk^7+-#FKdH$>7Ys4 zI-_#&IpOMK51EM(kQt8nD(V~ivm63Y-a`ws0Q(Wi-7EGe*4i|!(+yrg84M?R-(rWihWqwvqP`0-KA zibiQFW=Hf@BlFkqBHCj*nvzj_Z#DZ)8R*nv+pJJxpY609BsV0|`dZ7OhoMRqjg;+* ztF&e;@BD7k5WI%MjoC!QNscV62OpA;P7~`2BTU$CM+^_Iapg;H;LhnOR%sfKyZgQ# z>Pb{%6I51mQ86BhLZymWDZyn_#p-Cl_}Dpi?|uQxApr}8%IK6{rgT&H;4{@a387Gv zuPG(?C{xKBk40-lw6aID=A$dZYsoXyQI~Aq!<4+PXZTSamzlX`BPG^FhdJXU}r9U$wzZ z;U13)Y{xt?42Fbn{ktE+*M8I6F{}iauPh%L2gAf4@*!PA`+Z?t%uQumGh}30Eg;9 zGjmByzTH4ZZWP|mmF&9j%CZ%q+*OX7<045zr>=rbs`7=R5q48;AJX2M)2!8E09shGGdtVhO!^iTb`7go3AZk2qGrVj$UFIS8fMB}ChO*(n7yuoyJb|7eeF9XyJA%QbCd!0C32gI{tL z=hp<|xN8l`iBA!QAL9Ij&F13TFUexi>RMK)-x4wYj;U|JToJ%%E%_rCHYlAV0f>z>;LQ%!E2S>vJ8^cZEj zz(WF**KS)tZa22L=(`ls4C%UX(<~)O9>$U?y4S!r8vBBQzM`c*1TuAJYkK%!=Jku~R0<9waKwwkGA7gnND(oL5rWYlEVPqFYm2Gn4Hl z+DC1Jq8lXu9qTjA4IF{rR0L8`T^@?aQzP{q+-8Z9HjL!+97h8Cdtqvjs7n-@?xEz) zFEu&GSO`TO=lB$2!^jXP*}hd#Kr1zdv=rVQH%lndfNPN&$H-?*l)tOqZsJ)FlOnk_ z6G+XFx^r58qSX))?K;h`GypzT0s(o76u>MbC(LS-!CDqrBY-Egd`30cNLKdvd;iqb zefEdHI3lbAWQm!2urk0y z0DnM$zotT2R3|CWu^?X)jK6S=?E!$zB+cbXi=W){NUQh5Lp|)TcK`Gu&9;VvNHPg_ z*3_gzPj7el(rYVx-GfKC2ElQj7X4ypEHQEv?5FcRK$)ZJYw+>&^hRd9i~M@${FCH$ za^!!u1&BHtF#kNnDU*bVE+WNu+=)G`fjRBJ^LvU{1voOfGa^U_$}x+9`YDlWm!84L zy+p4l39!kE-+k{IU;n@XK5=)4ZAw_B(kzhJ;YAl|)?le}8{*)6N{}V{ z$WuFHrBZMuaKeDN%c88*U|W*=2u#*$&DLVgD3V8Mr(2>MGaG`Yz;kdcgJ31&iHLa; zYbt~^Yn8aHbX5;JQg~}uwGi+jH#LuDBB?NW+M8P;A!$P1j2K?C!aZMl19wkPagdBY zZI4mzGn1WPnj#h9i%9onLQ{|SL1wHD1k2lsFALSvrfu)~1Uvy4T`c-8CH_?{nn0)OC7yc_R)^bCt36_0Ug3dWQhdY(1L zX6v3L74ZswiQauCfa%-;cfQvuiaq{+bYZnJ15kA<;G8OqV^Hf|7wxwWAn75SDjICb z;$OUCN@Cb4E*&K7Hi|$0=ih^``=+uj`8`IGQ02@y_87MZbjTLA&$=}-^ zty6P|pZ|7$W_5uMW|32pXG^;ejh#|00TK#w34mA$4XkA9P@%>I^MNW%PZPy>)+U9i zV4_%{K|iZJyT;vRoAg%zu97{d0^pR+I8{y6_yUbLWfO5@;>px_W>{`h_14LJ-)#L@jFu)vzlLXz1Hs%*chZN$e(R_kW1 zbJV;pN+~KFGY+-1p(?vjdfjf#sry<{g0z@GS_D-CQC~fiMyn-EE$Z086xn{D*>NFi z6V0nF9b>cITa6pSk+^7@LMx&;)WiANQiRe9GbF#(e^$Dk+zr@+FgY4*G)A>uHd;X|%3Gk%V zjjG{2ZhK<>>d#sw!SiFro0kiG{R5Zq@Ir7rW~@c9A)f<9M&nQeWiX7eOz)rfSJ`26HRJH}uVE_y z#ta#3+QBIoPHsx}0|Wj@^I7^-T|3+~{slFSl;r9h{h1(sy6ob@gc^dBCPI@Kh}i&` z<~&7$(fjGD24&WhsS3(;pK%_Fw%H*r+ISnvxIlAP^alcZ%b*#f@KcA2zQ7^4SK$Gaw|SkiXiPo2d$IJ zMyRmHR=_3XtTVu~wh$zk)srKo*rKaYhK|G3BuZX< zYi`oDBd>66(l-UDD8bfJ+ZS@4q2xO}v;;ewHEiJGZfOk&b~WK=gJVA%Br}zwYrwEr z;(VL27|wD3>zDZd{Md8&U4P(R__3ck#$q`gR8xry9Y@7DM%E3w=v#SFQ$1!?b^RWz zZavdIXI-69kMyz2eMeqiB01Z|eCjniu@99y)3ylZo`Ve>)bobKHk)={oo=bxZ0@62Tf88FI=Mf{ljgQhIR#Ok z)CR1wFJ=}b5h5+T?Q!xkL4X5C&kzQ@5ZoRLv z+kVCIr~z?N*~|=J&|+&G6U0%2B@I!+@8S-vyTL38;h4x7KzX!2ywU*pr0xJ@isfSf zig$7dOZB0lfL1s8Z> zlqzZDX-pTp#o6TAO=pc?z|mN_%7>_EW3@>MP0tCN-4??ek8tf*-;cX@Pq9c;Fi5b7 zX*6oXt77J!!kJ8CJA5T%1qMkt7?-&7AD#uC*Ic5w(fC~1q!{r9^#TqH!vBCL9uLK)56zilGP@&O+G?U%?g-l z@?WE5p%$6eWVTWFl@-GxVYd~GGSkrGmU&ouOci@UYxTp!OH>W&a$*%Tv9hMviaW|pBTl|smR8&q=1z`d;h+wka7ob+2fn?B{-4p@hCeVf#dvu@deZ^MK_DCTon{Sks91gqU7zY9>lP7luT}ksc(&k@GO-$x zUHZ)gP>XEZQvgl6J{Q3zAhpW&Pi5>3g;3yP@pZHYNG4v zj~7s0wx5c?#T8m8vrt4@ORFGXrYInpCQ+pF>S)c};-Z5^{o0(f#c?SG1`2y0Nd-k_ z>e>{MM&AyFQJ$c8gC=0BI9ram|2IB>lj|AdHcvj{a{X$Ek*GLDi89oT4(Zw{YWjH9 zXm`oTSYKLU^U-tMecSU`=LA^@GRC%8OPG4QiArm*mN?js3M&54De6g(G;tD{sb0Au zTJH-Ly7q~hG0B7ANGTvm92C!8lgfff^tuxJW|<0}oT|hCiSTiI!av8(6)7c*JHc+; z;=Wg}@ci8!e(#@r55Dm)egvoIg2f;hl1aG70<7agh}S=51C`FBQ=I=JumY6#TNRhH zj?nvboqQs?D|yE;*Q3%IB~?6@>3*ueasmW-q zKq6>ro-X_$?CHjYL6~?mh>j%uwF|*kJ%JlUDL=ce@lKJ@(sTt4Dh+2N877M*sd|K> zj$_=h8}2%7xq9A<(mqh(xCF{>3X2RUeV4YD`9u|qV)kbhDU64y_!d&*5~CqN9$ATz zlDJsB_8MnYg|4aAEGRY+muBgbUX0_GCvOF^bD3wnm_o(UZ#X)*9Wj!)S>TkQQ|bK4 zR1hMwa!_SZvD=>qvTA8-%r*3aRD4aq34Gyp#N z>wkh(Xc(ZlLq&u5ncvEL;wO>^dlKCjgK}jW3Q$Q-nB7KJ!BSZfL&gm-sPP^NC^LP32}QAai;@ECqXuYpA(4s#A!j9Gw{$=#jzVh< z6!ny9o~VEh{0LdaG47{%4{37C(;8L#d{rw$q5!R1lw0{g}P8w9RQVc&z&+0rxdb>>lN<)%rn?Mc8v9< z1#(rXsXDzXgYtZ|Uuh%pp}oj&8$U%rQK)DtjoO4)p2$Rxw36yVef(E_Kh4S~G09Gw z6{}8q#7*bK(iO89Y^ty#nn$`o94#lz?*JJR5)rIOk_HA;XdVHXyy&OrBd*`K#&Bhc zZ~jY<;w%5iJMrkp&aheyNCU9T8QX2K15m`7aTx?JQC?F&q~e@oNySn)>e?eNEL)m^!)y8S0%8C^26|#B2zg3l zU`|AB+}pM(PDNbL_-$C(nk48A2rYV#tk0Rx-Tof0mf|+~Cu^2+ctukosJr*Hz{r(& zM#_p{rO)4WgTzh#TQkrlM}>hI`zln4MZ|IMx@Tj3iPEH38UUXRJpgslapDHs*{(lD zXhC@zPla#tU0x@frzt~gjwf;5t=#gm{iI*JDp37qlf?NWon)6~F%jZRQ5q7yx4MqL zc2k_zC=htR4Fk<%2`&Ie!MNLEc)b7*uaM6(hMbxYn3)ASL%VmX0=dZ6E=~-sn3sJk zZMa(G{l_}xp?}4V1ANv>Fk&hW6L0cFfcL&F2_ca5T4@3gk_GRZ@Q2Y#DDWw6FhnuV zou-_m5`I$BtRu3&UE!v0K}_MmfZ{!G?q|(K4|axAQZumMszs&2Nx^&4|J=XMi8D(y z9_aDXGWGqfH`e&gS6BGN>4?R`ANCAz#s!nQxIZ{W4mu~Tx+8_Z9K9(c z?r?za1WBb=XSDcF<=UQrLd3sd_?q}lkhH;@2O3 zeJ*NQohL1U-CBDj29sNJt%Qb>kC_;#%0;L`f+W|!= zeP+OJqd32;c=$KJ8h2M)q){=5_eaW1R3%&vJsAXM8cqB8BDHaxvA(=OzOA_Z6VG9N zl93JsSw>n{`g^4+E1I%Q21MbE`&zoWln~nz>_Y7xb0~ZEwPqNCRvoK`i!vb(wv9#! z1nF3!;wIeFv`btZB}jcj!!pM@=m+lAM_XrB$-q-}dJf=pv%%59i2EL1;Gh4?C-CdO z;_di{-~T+8i^-a|8%K(fv*O z=2>na9!ytqIYdNAhZ+d(47rArf?BB0W>b<)GW2r^+ExD>5aC zYr^*TpV>LxHbvlJTm zP&{|F{=FoWWaQ84e9x$Qd5EMy^>hjc@%wUn<`YDS7p8GPnf;}C zntFt8T|p?hP0O-g#itZ-=P(KKxnQ?S&^NEKy^^7u5uyQ{v!_&#kdi^+Gj6luYhT{+yX|rDA7W$z-v=x>(|O z+&scz9&tQo48vTEX$|-U?DqA*lp($Pu^M zDIEw~BcKw1+tg#J2}Nt7SJpmZe&&+q#DTkRAg&-lvF$Po6z%iaJa;s_rHMC_uRtz za5e2_qFvmdDuM-vqMwvlDD>Q9zE9}0mXyM^q5TrXNeDUPoTS1LR`wdRQw8vpwPt#^ zOm;djD$9CPDvg3nF=Q+wOb@#Y7>p|_|cqJ)9(WQP&qFx4G zpUuWDQK2|ab!Sxte0F^JUYhyx8k>w zzSjC|7}T#ez($Ltt>@^Xs4#DQUCT0M3zch~skxf7Q!gr65>wR!WG;-!m!ET|o-sQk zH^_bS(GR#nx>K`3sI6zDU{D4GX4@l`l^vO-Y;|uJuRzQMK}2AS@fln*aV98(Rmcj+ zYLCj@AREcV9jfcHPef5_CMyjIoXy!auBisf>IkkXy?RCIJP`NYR~i7H#Qz9QQG0Z$ zv{*Ekm@3lo*7s@BBur}x2gweD{Lo4miz;XlXR84LsmQrMEy&rtDe9w#x|i8#r_s;j z<2hPqHQo#t_^~BQ^~-xt5a1*uUmGyK=@4g7$fz_V@C^=D;;46ab)~5C$wGP$wwq$i z5RwIgL4eyMfL+3GyLo_zm%!PWu?wC)rRLGTn7*zFAU>W?<}L{K(ByT`_Z3C&@9>{} zYuL;c&H}d>?~~v&71uWl`o*q2Q!2Ch%@WQ15{L;(@W{wS&^=rPH(b>{3mI6XPX;9b0&0`%9y*4}Q9Qew}r$2Yq3 z;{G;c*Xs-Zi*f+8Q|1O{n1j-}_`<4&$iQx=g{?V#^+!K)im&{`Z^zes-8->eE^+z3 zL*y}I9CHDV4c4q-`F>R$u~HP2j>7LoDT0(E8%0SCATEF1C(GCbwQG!biYRdaQogoA zerRqyLVTR9`P4azH$mVD{lXC|@aB7(>l5c9v?E)>Rt_whK2lCfzGk)EG%`&R^0z0) z3Nep&9o$J_!HHSg*!IBolqTi3%~~~;_hLn}P2&wt$CP{Lql-)QN5uOv^tZwdj|UANC673#mc-ee15ICBB#pRjQqe7`89{wZu2Bz1{DdNHe7-NPJEH#A{%M4 zAOs|XxLjFxP^JPi19)MV@e2!U>*ULf<;mp`zr^2m2bjATmSK-g{ldW(!i{|HcCV zuf2pb$Fkp8+;`|2i@PGRVZePh5H#-bUEE#A&eV7XZrM1-yN-n^2mNht+ z)Ry(p65|I>ar_@{VZBlE#$ zrNNTbk(?nj`&N5hq1JX+a{L7CokFg-x6MtI&j3$ipgb!j_Adxi)fg zo=E0dGtM`PK`5ixouli$e#|J-nf;1!y!*nRl1J9NA;yyaOOz4i*0YeC*^=yjw%y@$YfPCZlCi)qYWL0hjts;RbzCu6DPX(g!~u-2HXpK(U} zUJ^N;q@LjYS>oOUb*koiUrnzg(R=oQQnMRTGj&$M!+v{pDlm6{adWrM#j-sNjUT{O zQsv7yuE0!muE0U0t@fAs1JWxNfKQeBUxNHf`iL^}&k9XdfnXUDafPw+NtgRx?J$;2 zRyZ?luo#a}{0f~=QUzB@@QA@@UdDX+FwJU=OPZ>&m)pEl+Ptky#C8V+IyBo7EyzhQ z3UIzq zfH+Zq#F-7~(;2=*#Ba|`_#N;t5|tD_82e&ROIP^L(f9G9W(55?!J#hvQVF@Ldz)}Q zExCUmGnyL-4w+e*1G=;a?g06|xHu%wHYKY&0=7|BOOj8#Z9H%cTf|dPU zqi(FF?zu>lD+ePoN`p)%@ezrK%SUvY!e)%Lw57i^E8#4SsH`Q zJSo`u4qdY|45w?vQp*RPes+@7$i3?P$lSTNXd!iRi%LldUKPL@!(5yyr5f0DSVAg+ z_7P>YR_7*f>>EP%`cTv-19VJy;oune|JIvWeZc`vPVYdnHM;T~n?V$A!R;!#7msaI zX5AFs^so{v^AdM|^jY8|$2h#Yn!Nww@?|YwSlH0qJf}IdzSP~AktAZFm3}! z#k35%UKN7p6bE4%WY;v?#J+mF5i{G9TT=`+n3_+RlV>D6Tb7p{R17J3r9klWLKTv0 zG-9>JWNdQAZrtF;BZ6zMQT(01|6crOzwXEIKmO&9;i;!~00tbaQiI;*Vsp;=bJ5bj zcnoZ39MOxdZNm67#mv3%B1bj!g0~6bmK&5a>lBf=+f8@KVj&oYgr}b0;yeDqC-CMk z|7rY*Kle6_2a1CSF5&!qi|sflj=UFL&M?Vk0=OxB0p`aH_&c z?dKCsKc&E}KqJ#dP}mkQ-FMnP2Ug5E8<5&WRx1QkO)=@R z)62at$y4=NW-axZ7oP|vuE*~cIUPn?g1lR`q-s%fu|#?{8I*Vt0!QucgQpq`L8#H6 z98JH*$Vsqdby9Q*O$v$1U-d}@Vtg*GsT)+=@UHAqtLytou-I~EL39((*4%0eibRSj zG4+U~gjCrViEYX7-untSBswmqmc+(WJb&}`kT|@ps-48ooqF|wJA3-Hfi8O8@nq)i z5jZZy%drXYvQz-f$F$j*s&#yzjpoeLdQ%1{$rgRr%KWMIkdfO0*E)G>(oF#Fd-;r5 zi%w%Op;9+l5g|Q+%t{OaBVz^jXkubaLSJk(zSZR=b{{#xtsi*`mzFCa4W<;M&&feJqhz~fX?t38<4y<~ zi=t5ilry5Ed|1Q2MJlKISIe|#@&;>z5&FrFJzs4)*{(A}nuZ!n!zlxb_qBB|yfUHz z#+;pmK*2~YNuUzXGblu$F$1RGNKimWsE+Nv*>1KNmI)8N`6{;S4Zi*x--ciPU;PyR z?mze#KK|4Okc7i^!eTK!@+sA5;VD&v|MhTH@yfwHkk!31|M=gXs@dWzK|gm+ttQfE z!q%6WwG!cN&&gR!a>Z2XKK1N5{?Ye-9KY^&{Um<>pZIAU-+BS}zv&(<7YmH1XF#s0 z1g<`T*K7rVp8BXs=gThzb9WX)RFKrKC$M1vvkH8pK`9@-sWDmB0S`Rn2`*gYg%t|I zBr8fzDJp=-%5u|OeeL@2$h zzNTE^blE)Y)KF+P52rS=y&&$X%As_ug3Z}=h{ZKn=v=D|L-lJeD9G-`aj$I(cM*%BFsISnVk-_pQBvbJ*&sI3J z8id!d>}xYn$`8m=$B%bANUFCyM3^X3L^Y7(#PdMu5ao%WVSl3cI0seBzC``5kIK;S7tZH@>jJKfaT3eKok&g6wg8c0HoD`WnSU-zxxEn58cJ#^{d$AbK*77-lFk26?gwc@SnF|ie+?Dv%PuH z3&}c+U@2eSXx*v2q1+<+g3Bk7=M&ucDa|R7pp-H=Rc)v!UH=G{67rO`OOS@v6spr- z^%2wEVbZ2wIWRaT1(4NW({sPeitYIphu0D=-Lu3eo_rQx^)*l7vwq1<{PC}TJ$~)4 zyn#m^SUZ3DZp;{W(<7A9wALjNFT~;kg$}=*lkL)f+%!VSi+{U+b9aV9 z&-W((lF)>nQ#?HEt@FR{q40Ov4;d@HpmNPkBLLvae zflG~K$}3{Q)aOL~l+}_DvYXj9CXGro(a=UxBUZIF2gGtyr|;PAV{vDKHX#>^tPX3R zYf3lpUtp9ZPs^5YcJ9`1_gnmP(JESXqR%RL!Sygeb!01>T zX5Ea%e`lzhvGby(qrFU%|6bk=0G1Ldd8!qeQa03)D~00l1WKfT&f!i0AYwCsdN!$> zx`?#6D3gXM!6~L3Pm7Wgcd*sqMQXnvqT!nDuH|kjr2wdJ8MNQ84Gn8pq-0PDe5O?5KKm1m28F>%YO zW*k8A>n^WxB@MWh#u6`Yl!|CG&iyHuVs9 z7*j1ocz!m;-wQ()Z0?RYy|TjtU-4?3ydmNI^b~7N0fIP_F~op5jet+}l~dQm@F1l6 z{EwnIJX&J>@Ck1Hi%;MvEzF1Bexz_a86ME5uZob6R;=@Se?Q{qvW+9I`_ z=;_)gN9OhuSAGq3e*&xWn3mI}NDQPd5vPz-71xloMm;s`Vv+@WeT9@PT9q5QSPIJb z%h~&j%d?u>#Mz3q%}z1sIj%jp#MOsactS&k8p`?QmMl&(9`{N=i^lP^pp6ykBXte&kxniVa?3 zuhND);xg7-LpJuE1tD%ZQG;5-XPDeQlXJ+lPeRl1G$~cp1Ie5PO8aN8scs}dJDEnG zlC7y~*QZUYbAj<`wx(kRVr6yHYNx26CZ&W)x#M@Vv$0Pvnqv1-H%&)O!s6&`5mM?U zy5B4rtLcL^S<{n;dsVe0;*?1U30?{y5uncG^lV)SV;VYxE1$>RXC_URES+@T_Qd2O zNf98Z;#egT3UrPku>KpGj4JfEF4N>eo(eBwan~n8ylmIMVezSiK)b7^qvRqU;g!))4 zb&F}7@_Ck+wLi zD{>jU;XH43vQlfRF+nphylR1TU&3y?!@@yndZk3{l*V6(`z*vHyK&$pGgE1SlmFk^ zC_evSz~>#Lsb~_vVKIcq!wPh^5Eq%l!E4_=jKy9jFS7sM?J`X};FK5~*Cv4XePbVc zpdZhu=srFCjL^e-7~Kco2{e4idmK#~=;GVT|CwS|cB`(KY z14^baps9JKieGVciO+mthj;B14-ON=E2={zXhM8Cl!jZo&j;5Cs0^`_3P4B2ZVeoK z+9jN(XCb=;StTgO@?KM?7SYh$>r)l1Xk@P@1AbCU{b2U21e?nXoG0AMF zR^dr|;HtE1Dx`iAuXfNqQsP>`wn-6a3M~W(f=!F6%|IYwaAL8jQxP!)PpG;zjbnzU zVbcQRiQ;xT!vnwlA*_D+5uQK0i^a~GXnAJ(5KAdf42LyEUW7@Z6lXTgM-*5mEOfx_ zANd$I@4tg9H}1i?Zv4_L2nrD&fzftEmscG<;p&L}TpTrsS2Zk)y3an-Bq*-!q5`Bo z?Sws|O-9cN0O|pqQ>0iuM2=q7M?~Q2GtHSi(nQg(&3n~2@`h`eiLz`pNZvC6(bNVJ zK$?!A)gq7*&dx>*vct_s4v-!>z=t0{#_###@51ZfbP2!nH$Q|g`&IYiO>bP|#yx8) zrcDZ~SzDvF?_q1#nv^-wUEKb{zF7;Kv1qz^C2Xy}?6=n>mER&r30N$wfPdnd4c_yC zyZEO+^a*_D_kIM=ec%{}H`ciCb1p-Wu{k?OhCqfoiOiP02JuZB^|bm_3q+ox3NfHU zuDUpu5`6AD-S66p&O~QEE0%JFlrd)|tk*&bFd}~Xsln2~#iZ{OY6Hc&=uO&>S_naA zAYpQO>8<@LbZ$-3&bYddj9b@ZxBp6O!ZKK}H8qqKx5@VR#K(_Vl&gc}a=e`L)3aKQ z+;V*F`G!}gl9Lu4sKi;&rng2EZq|_&<8Rq-^$WxY>#dF`{)q0M~<*(BZRY zr%pNdR7}^!oudM4S1?QyKnejjnV6osqJ9*bY;b~?mghvO(y|WYvF;!&G!f-EKS z1O8`gW~D;54rM*4x$jS2*+GGE0+z`mOYANS^2ykwV7gB&=;YkeSsWN3G)?`JFB&8i zcQZsGk!ec0%NbvMd5Qa0Qzw2Auum^)Nr=}|{pa9b_gsAD5d8!8Kkr^GE$p~hg!BoocL zChYXFiE#nG*!^+Qn{X1|K&`6lz;OmX<6wy|IN0H*Zk=Jb-~&5P5uHJgnuXGRX7RpO zoykqCK3OWPB?Ds;;DG@-jnJI{OL`7D9Z3BS%2LOwK+#qdp_lWDlB^ysubpwoIZEhQ zPEHDLRLkOJKVP1_k?^7Jij}}y}$K-T>J8yc>3fRGAah71~CrIubGU= z&>0h**HY7jNBmvhicC$w@^FdWhfi_qpMM;e)@$UX$m59AAy}P9!WTNq3aN~glAHbs zMM(@R{!I1fhb0q{PkhshtYJH;xpkTx(M^NS9^-$L&*2w{OF2 zbbI16^V<7ks$%YFv0xlKez6V_|Qj=@zX!^0>1BGK8YXrv1f4l^ck+* z9Pq$rA0dw!+w=2DEm0thtxzFFkDG2f5GdU{q;zO04=zU2CcHw@d=0QXCB(w=uIe2H4jJFqv$ z;8LT68^tYoO0^Zs)8(R6FH*JUcHY%6Frb74_&CWpp=VXI#0Ifzu%^~wQwEzW>przj zyX#OTM->Tzon)p!Hq~k-%UJf2y;Qr1#8WVQ6hYqJB?f!M5PzcO9g*K1IpP?M9@ zT8TcnL>8!iQq}2PU$FpuN_7CFMFt6ZC$0OGOlit+4p@=y8UpRy!#AB!G{$sT%3OYl zJ4e>KY`TIKPY!z4O+L?qKg84rtenceO&Bw3zb2`Dw+sBD+jkD4jtIRe2}CA|XAn z#Q6e{aVppie5k<($el>_B<+}M&j(t{+E%Zo>(vC4)&plcVl5fJ=+Y90lCa5&h4`pH zjh)!olDL4o6#!tf8L>G($2g7{Qo`ZU3I_*E088w4 zdqNPv@UerY#g%%%8ddNYkM#7l3IqoW5~5wd~HLLsJ6 ziayuJpxj~!&ty|NB|hNUb7N~sG8F~-Jol(@5YE769AmKvv1FS+lbIg>1nbhsn~?7C z6=mN-pRIygyJrJ*%Hg2Ar1D_c%V?K+=fI*O$0{?;2-!clam&;yL{N?|T-X z^QJ@m`Y*nYU-Sjn@#@!I#={RB;^F((xU`&J!?;sy$BZ#+DbA;}(g0Igv{Qn?hM8$@@@a&R3@SIp}%jefJ&YP zK@Fbio~7=K&}3U9pMuxP)TJc(CS8R$lRTZ^e;PP22y%MI*6G$pp*7h2`by0$ot-Eo z8Z1lC%Ap|zHwxO>oEo@s9mwt}?Vo+n*H`t0Iy}v zVUT5xZB_O;innUfXKVvU{@`hdNo$Bp0%j$0D1b#`9pkS{lg<;g(b-bSZr~Yev2n)o zwvd>l99wGEY^CSQs|wVC{Y23<=;4|oGqR^<`K%4V1fLQ9N(11N{GS{)t89KCS~8$f zPBeAVQYE*7$j^1EfoYneOrefS;~=?mROCi;=SdxcAC-Y#+!@U6z~O!2LNse}vL{xm z3Uy9vMkrHY&GN1Dj^*Ha#Gue~MLtx>{W2AHSx611Q?q+!qVtk+HpAesn*+-pBwlH< zoB??4O7N!T(7t-;q^$ojPxLJ9&%N%fK2us17{`poa)HIL#B|8Ftg+jT*lczs6I!rH@sQTPE)5?4 zeINh)*X~nplq~99M$Q?_)dGvfw3OU>;TTUm@jPzdzKd}ju~;r}^~xpOf8Ra0e&f>A z4A|~4j-wS90c@u;+KIgqESXaf?2#oxofmOgc8FhkX~64;gvZ7a*9Ksx>NtW>+2fg* z5z~r~YY>_m-~#!!=m2bY7_P4%_pPyeY{X(Zl6OGTF^`sr;Jnyy{<)SoA!2?-HRx46 z{MLdsbvzSC4iq5bpStCzw;DHG_VP5`qTIU&X?*5 z&7nQoCL(r7>PgOmDejchIxNGv930aE*Z{ZAPjL93UB<(|^AS9M`4pSeEe_Jsln>EX zL`|Y-?b`3R-lxe1@o0>Fvs@0)$Ifu;pM3&XSL?}CQmFcEDu+3bXmX5(+H_7%G94qB z%9IcpqJ2teK(gA;ZufeopPEe>L6wru?U3RRrjiAk1TqXr5S(syfR0!n z40zS2U&3~y_~0i_@Xdeyao}$~hF9G`;1_+yb$sD3x{1&J1=sPwjTLU*yT&6A9OClj zU{uOBMXfac%%LK^xy;GptCFcw@Rtk0Vo2_*Hap-$AK&2V=g#rOv#0pkcihI$zUwZ2 z^k;A5BOg5j&bGL6bA^ZAc!V@4&d#?uxwA9BxeRVF(h~HVKiY;1!jm3Wg*Yu+YA{D+ z7z2c^eOpWG{oBrnv?d@;=SPkwWnVscDk}7Bq1|UGz+ts1u}p!}ay5h|m2T~_$}I^4 zDp`3@?nGL{rKnso6>S=TLE4HhZ*xIu@MN|kFl?INKQ+W$Y7l}|p`Me~(_phpNAUZc zw6#N<<*+uM+I>*Z@65X?vhw=bWMS)&{~TPzb(`)>r_il{MMgq(YF)ao(3BDCAA9u& zbd9VH@uRP{rxaN~EBkzmc~vzfH~djj&UFLKW*uNlqxjd|60pP)Q@V|fZF|$t&Qg>z z#3S?89cz;1!k|d1UUBKp38bFnGgPLCV5+a0$~ypnUrL1PD-D28#s+{y`D9&8)I|X* z0>W5)6qRyUGQ_N9BI%~OFwzR4k^?b05StTIS6uX}o6;j57M#z9;^LyMpe@F^$TG#% zA*z;#%h4!+pXx>MK#h(Yb4I=<7#j(sD?7mdqd$IS+(y*I9a#HI8U2| z3^?13_{CS3c=b9pU}FkQVENyP_i5H-TP!};%rn{tsV@U-2tH2a93A$=Nvu z9gr)iO+5itUB0b&8#U96$t%Z`eU~+>y${;}ONzszHS%u6kN?=a@Z&%JZv50wy%&#t z@MC!T*%z=Kw^%Kgxc}bkc-`yn$D7~$Mtt$F_#AxU7ybgQ)+?Nzo+G8ySU)W1o+r}w z^m@$D&Rxd>T*TYwQA!f*vf?w=37@fC;63Lf@=yx{19!!kF>&{v%q9@1%q5bbk?%2+ z$0}K|xH4e%sv~S4y^Vzqjh<`VQTdh0(?Nc}e23>{-uQeUGZ*z@$vA2WRj9Y~h#lgh zvV^CX+#||VRLEk1OXmwb^+S&%KX!uU&CA&C&aGjT`leP@nKc=IVdn@@(xI`nE1u3> z*Gs7+e=+cFm{baQq7yyQ*#Lo1w-re#tE*&`c^Ac=xfRp* zNfqRau}wa+Xiy<>Qb+=H>|&+te$Anm$aabXFKwz1u>(HYElxKhPR`Gf5^(*Y0rx(9 z8Rw_xxbwmZe&8)1!w>w>6Bw?p@tS*Ac=ZEE_^dZy!y7;CGVZyt#C`X!@Y;tD@T!|@ zT)Vu$;d=OwK80=0GoEZr$GE<4>I9UGKh&cYojnk3V^WPds~uyU(5> ztrxg_WxzwPSs^Xf*zFYO=Ue36Xb`?qAT(7`X7!(TI%(&L4_Cg^?J2gI790+P0wQB$z+ZKoB=cB0!EY8to3yGmSDoqS33vWc~He$ zK16uZ%J-%1trfP{@j6`6jWH6Mg;D67!8TK}aM@PE^89SmuM+L71`2Nz4x{Z(R<4fGrA~>PDYBHt`Sa>c0HQ!$zpliEnwSp2mji6lZ^s%` zdrGxYYjz7I$%yBcXr*+PW*Jx|!WIXo+=~SMdVcYV7)_1r_IiK3GIkK(%gp`pyc`Sy z~k$$duh8Qb;#>fqexLx z^4(_h*cz~~{=R6K0xPDX|LO|)#(;6VouX{$knJOdW$*O);-R$e`3wHQXj8d_QGlIh zeBQwt*M@|hIy{`eABvhollIec%t7{oWDDvOB;ftyjj1py4i69T!4EuvZ}|&<58wHn z{~UKuPjTt!3U1uEiq(3FBt!E=<#EL6`G^mH{5gF4-}rv~^B??aeAQQd8Gg@K|7P5L z;402G8w@#N5Q^)GuT=znn3pg3lSr@maB?bYtd!rX9oY4Hg~uOz0)OROzX#v*-9Lgy zKllVxH&_e{EEYrgyCdHJ-jCvkfAkCh@VeJNh~MxVeg(erD}OV-;Fo?n&Q8yfk=j|R z$x!9u;J_|Ma2^Bz&!BWpO8~$Yz!Jb`tp(qISFll;tRhO#DpqE4WQEg~bNt{8SW@P| zT6VuB1u!OHJwR@(kyVkh07JRA%ZBWGl>~YUGJkJFDf35)zf?{u!3e=%1av<4RT{L^ z>AMGsB|h}hw8#1{iY#R+nnF|+PnC)=w9tdWxgZzk>Vz`$zEn16!Ql+2A0pYka+*RA`-M z*?}~R&0_Yhfiv?6C+RHbGA)OqEWqJ+v z3f|)n0ZlHG6#$J&O+}g%ZF{o=POmj{?2uYxDe)FluyeWEZrl*i0~M8`5{03GwP}a?UtD9wEpW5^&|-HLl#ZLf&Sa9*_9wGk5XWM_$0c{qau#s{v`9aL=_R zUiZ);9=`7g_ue?b&3jjP;8japzrMubLBetvuo$F3Xj5yb?lQ33W^A?@r{_BypN_b5 zyu;l)8{EEij*mWhhL1dPf){S@aO>_4&)?omKC9IdN0$?hE)Tfp(=S6)!gf32e3PN4 zyY{?I>`n8jbJQxIF_a@9m3HFVG-(Nc^(>esYg7R?H`Nmn$gSWI*Fel77LnJsnZmUp zK9e<86BYm7QjtqhAv&p0a!Z?1s@YTXHA7QVS8A}4 zpOs6dJ#?gWFLglAwgTp2@en5~9a2^%cpz&SAxfr+pI%}6rK7BSD zWUL3^GY$qUL~x>tr9_cs_{_r}>5Olj$-N!e5&cJ1A!=ih$-m>RAHpB{Bj195{cnB} zSFhfS*WSDzsu|nu7N;lYB!dQqBp8MTZr->DuNrRPnP-pj4d3*4@%Ep26#tJu|J8WI zr#*<%({mstEbHP~uN~xq!a3p=_oN-n_Mz)x0a&C3{`J57S$xBv`40Tpzkd%74i53q zgRjDJk*51nE3q}VgV=7j!;??m!nc0gci}Dn=B@bCU;9<~Z@=3 zKwb#+TEduE)-N6>P>;h6@tiu3p2_v4Yr?sRAW)Hr8Kw88^S`5xnr~5ho{SI7my#D6k>Ddhisr;_CO!qQS?j4Hl_-(NilW zm)gslSIYs1Ckc;#*JC(*a*Or-m$BKMnxB-Owp0$7Jn<3d0Zk^ot_mO*rzkXGOHTKa zb!srVSlrF1cu8<{Wk4Dh z5KY)_f$h2C?0m%A-hBr@^|reJhT@L`)+@nkF2BqfYlk#~waCtJ6w_%lJKdyZKOW_xslQDuUEa;%PiJLaK&N@z{$}U0iC7tK^1;G{7Oc6f9|K)KKKs^B~(9cu(QUdXsa9)ElU`ILtaT zwE_8@Ua%#5z&)!K94iF9mc$XLl*kp%0ZMy|OYc!up-J%GbFnLKN*$&C?tI3Lk2yPP z5H_=4pXvJz%-E*nnI`$(RI-mOeN(11N-UQ&{d1jQ{!T`Ap&0 zLYXp-n=HyG)gs~-!<^ef*e3s(7eV-lL5>iL19XOw6_ugnN*=WivY(nv9psb>t+y!< z)OnM-fqNsr*0oLTnZE{%2Y{SvH=)%zK%=X&WiR2~`@b1tNCkS3;i&+F2+l{veS_eU zWz8;!$;#XWnF-L8<1PE+CHvUA;0t5Q|Cq7ejW{}5-IT*?;re9oSkj3TrSYNGw9i1ui6aQ&`u-H*A9v{*Ygv&c1J?$Oi%cHy z>?e_0V+?b*nTmr2k&L*4*E0O{1^!^B#lH()yQnD6nGa0TY*Ewwn+kKFPJX0#G0`Tx zK4K7FdM^bOSLM?6FkYwc9JM;<&#lcSI*glyR4fLTGrm)qtf@ju6QlYX4H{R0K++~d z%VCM}B;)zxySVfv*KzZ|e>I+eaK!1!84lAh75}XuoI_K1GJO3(8V=wlf%L_R*($^- z08%b)b;&qfFLCnkp2P85K7m)=yn^j^O9xX0a%ypQpaLH*_gq_YjIdWhEu}`1AeYyX zT4yd%cC*j#cC82Dq#jOg-~()#K0}H44rjTUyx=qmC7@TP$)Le0lZCH0xS?5ET#S~g zSAK9tce@_BsRBfZ-y;z54!9@=?ra5v8WTgL#i?ml zY&J&d7YrDd1C~citSgm#Efs!k`>+V+tQZs+Jh$BhJ*tcD1HiGNrHaTiU=#Qb0jNJ^l#RC*J28q7AUko6vvF1|Ni8^Adf&*C^A zHIg&Y?<+M9xS5p^pnhdP(X15JHYs44VP$<_w%sxeHyH(j6H&V-t$8J6X)xQM(B5I^muw0qAOxF)=8^jO=gXA>`0}&>G)8E^IE2Yo zyP?X(ZzO-(z*Q(7kGpM zi`)HivIkiu#vuCsmxi3<`b{$w`HEmWFl$9RFKe(=`C?9k00QyA>=(Lyw0j}8!?^^}3vIc374|>;F0)eE5F+mjCkeusPpiv0Rjc#Y`QodpLi=CgO9E zb+#Dic)}5iH>?uw8xr2X-L}aa%lCXIT7y5-x2Of^0HOSKGGl3}_fLjmxNi+zPm|M0 zMK|SUaRNa`_T6(4*HBaKr>d%R^8WH9H_~*r4V|YRwb-Z;M-GklBT|$ z<@S2MMDxZA+kBLr`~EI=Yzi?-zLQ_7JET|~RSne2MNyS(?G7HydhA-t0-L*A+{(we z`Cs0T8^8S#JbPn<^W$?Irp1JIDZX$wX_v@^poUfUG-MHmn;D349{%T(6+{(>ivK8s!C(wLJ*Mt9t)5Pir%ESDbBs&;8D@g!Uz6F)}1b)ot)Qjxv$ zG>av*ZTg#R53biY@x&+)&uy|)&#$TNrBa&;Ave7JN=PN6ejmuZW-I(8DXc}xHox*3 zCdEv!rnsbxW|%Nl0p*u_BD*?haeJMaqT`Cp+@|4zJSxUsGL?5v>_BEkv=mZx(Z z96>v4LzQ*Wca-HYPQ%&rI>K>QKT;KRkKZ}n<_G#Rrv2V zRi{kA)^Ko8iiq&Blp2}^KIWI>pe>?M&1Tnp`-##r*lNrPE9#Vewst44ZmMcvI_wJ% zX=2g?sXdh6i+D~>O^I;vg$}I9!a=1(r3e&uxeM@!OL&a5TviX&Ql)Yw%kr*U@v^cBE{gQBc@?LwPX=UrSiXD6> zC}Acar>~4_UpQ+fZG$anZB0vefK>K zkpbgw2T7CPzjoj!0#I`IL>XUAg|{qldU_0`4Q}3Z1OMoMdkg;N-~89p_ekktq0&oQ zx1=NOYf&;xi+{#)xxnL(J%vB^N52D4J#z;SKKLMR-MWpO$0GF-P7EUdu>IeVmQYPN zKRdsO(>5r5{-{6)O`J!dg~1xxK-2ho^YeSHBuJ|GP)< z{MA$Jj?Zu;1JXDp18{@WFB+lnSD!i%m!#%?h;gL~%(Pi21@}_0K3LHFQxO>q7v1fW-BsMQ(K6TGJr!_)zns*ey$aJ>`nDiRJCwy zkUbB8tbc2(#4bINC*lc!_T5XpnzL3bov(bc+5dkNNI|dXc?TgC$X5h za_F6!_|l>dO%0x0(~Q^SX78f2L$nO31F=%1Wck^-(oBT75Q&m(s&XTlDj^a7O5me} zZI9G)Uqr@|5)(=slb|ThX&o}oO$azBYMm7P@TST_i1l`KKYm`+nmG|<$Y5C^(w<8t zDd|I^{+?5T8+eLXA{vU)NR-zYf5uk=fE!Pi8c-rmqv>J-n=+5i{zYZh?tPIirkEK6 zLeOLnQeyz4`e^N!c?v*!IU)eW>3g+fDOnb1GQeHy2)pOWMP5`3S*EDJ)-e>nX$eaX z=|OGLENWi;OcN^ZoEpJO%R$->Bi8e$>45SyNL>Vp1FF;mgR*=_yeF#3veXm5Q-LAr zh}}w&ZY(Bcg4%-Cakp*ky3=gpP>Jkjgt`vy9J3&&BX zIKN@Q>B$y<{X4!7Kk?SL;?)no2De_gHBI_77r0g4hvO?!@I+qpgxz+7tJkjJLytaz zZ~n7?8_UH4yD?)Nvpr+*@IDQ4e+h>XRs(q7KU}(jR0Rua?4RH@OJIqy064{_=00j* zzVQ?E6xkiQ;tkcuw^fhfn0FYi50Jwp@|f))!OtV2e=dScQ3`(#6iUf=?RjnD>04X3 zGEFIv%bFn;A*Jg4l3u~D|C zROQk-#hJfIM(y?ZuBTMqR z-Kt_LHJ#ow!~Vo&Cxj^!QMXyftXc|Inqo7QTREMfzCu;kjOWwVUVFU z1**q;YD6S&n-Jccr>3DeNwXEvN|RL|)kynJGKB&Il54|06*Z)_dZwnNq|%T}(rk?r z#I{W3YQe2(QX$m!U!_UKL4IpcL>`~N3=C&!4=D{MSFW58D&`aDnzTKK2Nc;I@+1ni z+TSfx?sAW1*kDCfntDldfV~C`ME&nXH&F$@+UrhjU9Ad`b{Nz>;4)QTII35()H7br zHsYU#Kgk*k>dEyQXxSQuLwsFw&eTwaUCZ+Hh%%=|y-f}lQRqN<3WMx*>MkXSq0;$X zn)R2Xftn?uHClz(BGN1b0yu9xj}FC2-r=srlu)5|hHRg|-6x_&WmT1(QtnBr{$12* z;GAgA?HrH+6 z3k+ZT*lba;_ib?4Yr?NEF_RFy2T29zwj40o&>lvd6GK@g?O)0Y0s_KE`FQ$qnD zB4OeZ2IR94!;HdP|`z{7f zo#b-RjMuN1I7|s+Eg-aq|0#ibI2XF_{m}thiGVvJQ^*}l;)-|UB?RDOvdN54m)yw3 z{zb7YUZLS|Ym><~StqZ(lmdq#jCBIwl_k|4kR0W|O|Qhhr;3NtaaYq#RRoTOz(Or> zW0i^uE-Vx}N%qt9GjsjfIbnr0w!00U9-qhhOD^N}U;k;q7p?L9_BM3PSf^!^sArUd zO0@AkW|Kw4Pp(YbYzNwQ6?0LZsZjB3>tE86ARvmg8gS`M@bPbdA1)sYmg^O=jF!w0 z*FuSwFAv>gdbgAGmnNR_^_9UD%-kI1_(bJmHWV6g9#nDbSGCP$Xj9WUVOX1ZPlwT| z?i3$^DNLRwA8aD98>agJNt*xJNt+1rRcp1x&y<^{fuiPY+9+Y-Xs<7Is)hF7G__N& z$)34hv>xyT*MVZBVFs6$A4xWHHzNIfSLb@L3g>fYp??oM6$o2E(kqXOg8017l=4eQ1YhERt^Sz?_ z@8JF%t(esYj?wzPLH=)=+&~4IM_@GqR~N{myacg~&atp&3i~~jYtANz7dY#RCo52y$k=o&r@eS+UhoHw{gZ8u?3J9ikx>- zNvWKvo82`PnTaXr4(b7}(^M}mEXok-nrT9-*6oLn+Y|CeHh5Z_V&s`~j8fCBCe^Eh zobhB_MCm~0TX8z0$`G0&IfX>|v*di(TIiNGaS{T-$*5S2ODuN>IDP&MCs($35gk^h?yvQ@xO%-(JvDDe;*VJDVC`yw+7r^C%13dYU zK8np-pTg1gtJvmUu}>uLzjO`h?0|&4H#G1hNJ$Zh+6`nCiuEAw_R2c2LXDd7Q1@rf zJyNbuQk&?3;Bm?v9s+?`Qs;CZH(mkIJDLg2Wf+ALD@u&S8VEHrVNgkAk-(15rM?xlvkp9(~~mw|&b zageyFFzdb}&@#I7k5_fMGzi*XR6_#<;*^lpV(sYt%84r^Rc;Iu@k190yMjcrfKQ}w zXF%9Jq^f?Jo9T??X{&Ub+u2ldS-IPk7%qF=u4kqvn719J{U;QLgt#*0Xjv<>HGf<5 z8^any1CVV#E~cBs)}>)8oQg5CZnS-=oXZvc+{B_2vx~s1&#lxZv~rS!4LyQ5)QV`j ze`^D#osA_9Cj5=9iwic;trnGoG1@61XZBO%@V?Z}7j!L^AbaYR zYV>;uk-+M?7Fs~z!n_an{Oew20O~-II3o>JE=_7{&q;y9GFX=PI#bb1_k?6$1F3-a zIkx$D`_(HJfKLerfRL9AT{K0KFHsQdbOEO6;B+bstS}I#?}Q)Mtnx-y3U+cy&a%;dZXiyfp}6RCFe32<2bT3LakqZJ;1>{0yizkWMJrkK5Pw-BPK%w=yv(^;RtIuB;|FT@=p75#~!dgQMyfR%{*zHSPKn4r){?9qJG zX}Lq3RGWbm?>336^8RVLpJI0?*xf z9`gAIcc(hCCqU1$Nn_!EU|hdqo3Un46u#B3m)lZ&y%-Lw#uY zHIk6BK>2Pwv=Xm%4;DMaw7SI;tT zz*J!&qAn;DRC%4$5ECb4(TYl2=~Ni))hAMjxsH#g_(S=7C_P)gHz>h|>eP#B>eOy> ztbA5YE@h;~sYYd!eVoLwaJk)qc3;o*QOGstvK3ovW`DM_a!zJ+xb!^&NY^WN?D^)w zx+$2;^bx!th+8LJ+k{nH@bFwas%TIrYdH78GlS?lH!ckf8rjywL~Bj5YyL_D;8Vc? zz(*S=GnWD(nhFa6dtu=6iIoh78hkZPE~YX|#;lVsF03l3$^c6AFu4)_j2uas4y^%@ zqg^N4I^CYQ>3IdUd)j5>G_cpC?6!`aO&;sGU7*8&aWNogrJV41(B40B)(bd}+Rs!w z6@lA7C^l&+z*Vt=>Y^FAUhqQP1|a(fc}ksRRmI_Yh4b?*e)vbQJXioZN}x(>Mjlh{Y9Xd^(cmIrw9$vgPLANolMfbDiSWksuYcR~DF zbCwOpqnW>We=H!NxRC_w;%yY!YuA5i>rjjU*wS1RvTlpeb7TEd6eODH!r*>N)+nL1P-v2%G{#UDLdvdnhC-?Z zgh}{YB;Vw*5aOEyY(~9B^^VQnts&5R`r&P77NI>!Um{I<^`{C zS+&MS9mH0@iChM%ffL^$g81b#Yo><7+)kGShHL9UnrPa;!2;2v*2> z@$k{PnWD3K;@%nq64FaT!jeVT1<)gS9nCK3!|3+a=0;FM*AEs4m}RK3Qc9EhKned$ zGRT!x1{^=$I0pe_7&r>1yaw?EjPc?GajO1Y>Xcw=AU19~DqQo|8reA{5s(VTHO5m@ z4>tBuT~i3|`f~hd@$ypugo9&84KEWD>iWPH@L^g21N&Ign}ot#T=u3rK&$sZw)Q8- zbWntc9hx<4eripJF75*X_M?=AOR`jY@ZDxgx_Y;95a>xL@~kb2i!|LqAwttlyrK{~ zMy!OtrV^?8vY+c7NbNQA`5*}{i_FySoE}c{Dw)Xv?F!A1SfVpLf^r=f%LSf(`W8O$ z*vGI_K^hV?Z;8qpqxZ^TyUKCJ=WVM*@76RWTCdmGX5a%K`Y5*B9nvsK>MwpkbpCTE zXqysnF(tw2$vNKs_75QE4K5w5u{l4bDO`{=t90L9q8FNn+ za{UT+dQJ)n`yA@$li}49s&nmGe-H8WP^qz8_okTZ43}S>EVhe%>}@~IZYt+M=`M;A zRD^0v9GJY{O6!kOMkG&0Y0b^`naZYj&RoFofu23I$qwfr8=pH|PH8Ywv&nTzw*a#) z?1rgfFC?wb^xS%1eC2Y8J5K;2gYOX0p}sXq+#W!FTbAo`?4!H>T-l4`{xG1?T0QD4 z8CEPwpW;IIN=kOoRSV3Nv&PvfA;bh|^+EE=PgQ>}CCaJi;!K3%%Vj3GLwtK#e1l9N z9_|K38Z3&>phJxnC8Zl8}u)QSqlq%yG6;TOniNRxGX_F7s>ZGx<4x5b0Qr8hW>(ZB*t)f~B#DWO_@wLC{O!q$GZ;jq@D{*`GN@Ny&`a{g zLx63)*Pu!YTLdMgp!>Lam|5&AplLiuSCe*aZIX6H~Ek-qX@1}!T^6^sQvo@9S++~B4IUq;? z3%Uj@B9%XsZbA#VMr**#k14YIahwzYi%<&+1AgXR+p`6K*Lvt*yw@m{h4Y2y?%=Wa zeWHvJMvUV~i>_})Xt-zGByFaql@>cSKxDuZPdtrrJL2Hd3R@j#dKeOt0J`3Z2e42< zwpb+GzIB2}AAPcrdO91bpNpAIA&No#N>7fX#Nq zLKneIQp`yc=eaR z3i75)xILa>b9Nh|0~TXy_eHJ;sF{UC9k^6opP>h%%6vo90q4|F<6Y_r1S3XVzj_&W z{>Afn<{y6;_gp^2RyNR-?O>$pNZ*yt-hf?7*McKvB@^ixPBP1Jx9qW%M!KgJEm;mW zE4k{#h}C5ZxGA%FIztgjSKU0L}srP^iDWfV6K+06KB^0!VmHMK!O+Cf) z1Vmcwp)08ig?+kT^VD;oeP77O3>_^}lYGJ!gUB(C{A?v>V75U+C@N;oAT}+`A#Pad zxn-}ox~7{0B?pz6*%N$A4DQfRXN|_BN!=P>)e~B#GfJs8Hr(O$CAssR@>z~PGbyru z*W~0vNcw56AE>Yd9zL>>r@4HsnsPMJ+Gc4Y_4!dJ7q|7nQc%=2QXt+(@6)Qp1Mdi| zV0;Za=c=F1T9HWipQs<)1j;>OD%!`8nL;0E_D=oa^qeU)gR&`dd@g9SOoCYwt~ubr zzO7;28gR)NN2T%Cu1}^t)~S7!BK9JuOFEW8RAXsNDj^;R`r6ycA4I|cFP!tDIxA5pMx#-!l!6;@ekc7lis@ExQz3L(ps~8Zs2M-X1;|-2j6% z{lN{R#HG9$1RSASc$w<|Uycm`s^~E{?$c^TfuBOTh#^;$&kaSC9&YMlB4%r97?4Uw z@HfAZx_sk!Q?^(j$+I+0L{J|=hj^h=D9PDuv2oLvdJqZ|M*d-{ikyJlMbPVuHUXp? z0mm|2+#pEZl<}gd`)RWnBvJPHe$l{x%9u|Lu_jMG@jO2M)H7HeERY1)>dqaW)2y{Y z-7N;;gPJ$uN;#yiR!f|p?{N2pb6mc9gfZorE~;Gkeq|1dr}dspI6XbbV;_1FkVDH< zt=Y(>qwhg)LG{u_VDLdKx0QRfNOUN!f6cs7NI$^$>z=aWM`>Lq*_~Pu=gPbS8LHXD(W95<8d6mv zod-x3j2p%A_6*w_g3G_=1|I&k_hR>HE8Nu$^2uEcqhO_F;-AFZNxGiTzSLm7Sl_>o zVH;&7;)^aw#+VgHS1uuaB;)aKeIKsnHDoQ&G(tl|tk!c@Gza^PcmEW)v_l*E=$^S)2tjt{>cF4l@mYDAHEg6>;w8S1u&s~ePO z_;ssIGO5IGDg?KAXe3HXbfy6-G)bObH8-KyD%3%2lX!Wk zp!cN5#;V)1a!>?S3A??1C6$LXD@1PR9PoX{Neis5B|2prAVk&a%uqsunzAXW5~WRv zlo->H`#V!O$KhIyvz(G`yrL&6NL+oKDTf!FPe??4cdk&s`F3gswbVZKYCNYP12s@J z)E~l$Z*Xu$;YEJ$`A4m}DD-@X8c=7)Wv?^WQEp4Ql^J;`^ ziWt#w>{p_7QfZ;&r-O!Ww7cPX^C1`JUlwr2nk-Jndz#|#JKV2kS?(7>;9l^@}~xw7StDBY`the zQfZNS0Py6K&*S*`4wj3>MCQ-AZ64wdxc>8zFliH+>E6;tyBL>xrKsbEI$C+hLjOK{)zuJw%J*si&U7qwoJH zzTlU=X_Dfr5#~E^q0D!9T)z3-y&ybc4!1%%RCkvBP(U~a1x-Zx z_%KYr%tddZ>^T+(U{ew=c73l5F#aQQ3tLneYCv(WR8WR0c0B#1YC@YV9dbbdW9?70q_6I zZ^in_Q!FoC##YYVQBy0lshTO%of z!k@j$eWu1yi4uSNz2q8N?x`q=6%x-ou*`H0Z`ANCu+o%iD$csvys4=*(z^SKihui( z+H|pXLEN4!WL+c$k2}VwhfS95r2nYYbGdSMmJd)|EfAO?4l$(uJ}XQRUQhYV?U7h> z12%QZ4{behPm#2SE8olA`)&#cajHR9z-gu^MxOMmkbSjtj_qG}HM|wRhjWM=q;K2| z$w}PMaAFpvU}ex5OSV5VL-^;Y_j)Ns9n*7I8?rgm=SfriroPgW%i2Ja<$g|fEnIq$ z`fJyp8Wr;LOaXiuHUL`3-5-g)#a#{}zf9d(h8Oe)l{ zU}ZptX9Bp+W>`D2^0b)VXYvh$`Y<(VTv*Kesh_q@zqN;zR zC;P_n?vNz&v>*vUe(Li5T%!NBbCHLUqvktMllJNXyi{0r4Nr5#I(r8m>6?Ko2h=(@83&}krq~mVr ztSX?pw^=@4#vqj@KM7Js49kRThllvsU;F^{C!WRS!)w^dS+lwc#b;`QQ*lU;#7E~h zD~}}EBp9p$fOv0lEkI~er$KV=Dw?oAL(TJSg|YdvlZ2;;)C@NH%I#2_SWPF9&>_%7 zHh?f)s?F`DS|!+26EKLjh&K_A=oKIAm+IC(wiF@|fLCB1T81lXd8nn?62uR68xteL z|5Wecu4jRMKatQ#gPRoEIxYqPt!I?r51xJweYR>-a%K8$ zYqXhNN4!rx#*~R~kWcqrFlV#morP^Q6$8VS~lR}uE1*}bIjkzAbHX<$OQ2@K_#%yd6}=aRD1j7QvBC|V4*nyyecXC$jb~&&P zvR6v%D?wz>$e!-?aU7Aiip8*MQB~7vUBmt6U%rYD%3~^~uLp9WKWB`iVtc-wq2v2F zC>bAB6t~`~XRSL^Pn|eQJ8X)jv$*0SG^9j1zPUVV1|G~w zaPQU2c=mt&7;gX5$8c@Af)3}%7|9DMZb;gxFL<(tU-?gnAEP7MI+h)Co>$=6^>Sjwy9{7^3x#1DHce6-8y7} z*%CpFBChuvxdk>W5torJg4V<_3VUw4l9>2p>JqhUEhe3BOb=(@_kMIf%cjVHP;RJa z#}_dcL{y;?TY}`ZJA35@%pmAEl^4)!?f)NxD`h0INkP zE60?hf-E#(AYeAT=(QyY2(6*L>{VOR2q_o{Q0D-TtSV)AslX*F@Px)e9^2)t>rJA; zAD^#{QNq$Zl|byu!K>dX7Bn7B`Kt(!EDpfc7O=eWNwO3)0x$WX(n!x z_p!FDD4b?bLo_fAZCxZIr@g4W(g65Wi2|gaHN~)G*Q8HPEUuoC%Cc_>I}HV(_g+#XLu9{S)p?_)5@TE8PiZdR zPXoJ7_D*P(C50?@6WU-}>28Jme8`<&@-H6sYhWKBNN_d8sc6>-P@R*}*WXM*QZ(Cj znD@wEgt6nSR?2=)EQM4hY17PQgiok&xx_G@f!o|@Rq`TIS3MV9 zMG}mIV7nPH-aW;**g^7uG??e#dC!&Uzh%R!8RNcH<_f~PNF|iKY>NJoct8+9rGVv1 zlR-%iUI&L+;b`CzI{Vzm5zqvuH6+>2Ly<$3JD?LZa)+@UgvViryinvz3B&aTmJhD6 zc=!?yUVVV&YnB)uUSV?yIMW?oIN3m-JHr6MLZrx;%4JHS?Wc2U7WMwx87YZ&;|!%n ziOkxQ+?}R=Vqfo_Tr6w%+;a(=AAKHAe&@SzB_+s8Fv=FlWhxS;C4Fory*K(<&H5I{ zH%K&rqp3SJ3w)ELx8q2X62F^DLnAw8j&!U81TYyTnknsdZsIdoq@ z3W3hf3Lsr`RD-3zJy%SC7fu?njP3f_PU#W3#0yHX6|f1PWk%|tYQlvv$-r3xE7DVM zRm4y7;Q@svH`M;UC$=BPeNrtMWp--lBS1Sst4Jr#Y7xm2{8kkIX7`DN_m)E9k+-J_ zk=3h(Le#IlHl-Be1F*E&)S((686sgVFVp_qbl~vkOf}##ni`NcQCBouVVJ|c;s9@M zFd?My=ZSM7aHRU5)ky0a-j{Z99O|&`9+2HsIPlF~i$v^)Kh6t2r|J|w-=cx|Y~wH$ zNp3M7;y#b4keF0!YWxaTB@k2;q%;sZ*b4zL+rWW`eay<};K1tGIBI5h_j$LdauEmH zYtLo!_OjWg#7x?ZA4IVFBwTAM-s5E6&T zpuO~YDp%MmYw}1tkmd^kh+d~D3|v8_ms3&kZ_$WKdCD&h-sl4*C16{mQvwLxi#l7lX_qYEqH-?C zYj^Qp!!N#p!(k0c89Ho`B^6fKH02*uE$_JQO`4|P+W~o} z$h!>W3>|aJxv%#hgL4uDNmH4==V{V53Xs!OB#jw)H%|YmAT<>T8k9(MI>cx$_oeGv z+FGFMo1>{_IoF}UC@r_SJ&Vf(T_s>8)Bmmk90-7ICoZ23SRSpg9nbAhnwj2iQ|KhgO?T>fE5*a3NMJ?ym4v~f zN)uWYE_`VEN2-u&H&gZe08^f+F?9qkDwmZ$xq9V<{niqadC(};wdjld0HxvpU_hV0 zwk8qeU=_!m zvd^2DF0IKllryEDvgW{2@e&_z{IdEk5YN^2Le-G6GL!n?rpeTBno`|_pl<5;0{kr2 zK~`O(rb|=rXSfDj94d&0WDcM^>nbZHs)bd&f<&FmG5d3#z)yfV;X;F4zh1A@#e2E) zh%r^9*)@czi!yzF4!&6+mfGHn_~9N$rn>8+9z;%xHO~quwxA-J$}TR|bvf->&e5swv$j(hAba(q9kNa8Vq%dhBy)_PgKMXMXnQVGCC2q4urbT zb+f(Xj3tdd$(8^=RQ%XNC9Yeq)U{LzQBVl}$gaSZwpk1N(qaHy9E8aX_-|h*z>H_G zA1HKT+fLpzn?4YueB$ta&x76v_u1dgG5DZ0ZQMA|dx*nf+^Y%1`{y`Dt&zDWIqW1G z)gsqd41>;a3T6sung3tRvmk>ME zb(sB?i+y9y`T$B3hm3%Z)5KNBGLb32FLWT3awDadLx?uUmpn=AMFAZZ<90+o&p=KPof-p@ zb1Bso7$^IJWJMk)GG@+j6W9bHVTdQQOV;V@i+6vlGVO&x4i-q47LdaP8~|7+=o-KZ z$V){Yrt{4Cm~ni%LB2ZzFDMoQ3_4()7Q|Z7Hi^1_yaYYVS7Iy0(r~;AHpOnTlUywHmAs49~JnqOHL^rOx$Q({x`n zOjMm5&l>jPY3ue>rCt%C-jb*@P5bt8Qz=M@bK?l|kzqmimdHQ~PU#eLN^F8=H{dlm z`Ddhgi)(YJ-rhOW^G1an=hwG14Nr@=Go!TTjyqWtJ`gJyR6=eZ%d@G;C^NMG7~I*S zHzOdaOmS60${Sn8q(29V1j>vAm0XU=R$}TB`g33=`6RkSi5I}Vope@OOy}2)=**Fngkn&z&GdE zk?i;i#6|0vQo-{0iUr_PM+7i)j<;#V6b+Ldl!IxuO?u~MLfJq(+Q$SsnSYr4x-KRA zsw9=TB!i1gol=v{Bnjzk#x`fzUDe+HSg>`W>+6J@lf0>?t# zH1@<<^|K=`Ky?3%S0jpfF7`xbBiJw}9N>*=Pdp%AVsme<_^&V{)iP}j9!czx{OaC- z5ii*mqF~zsuQr{h_`x<6c8=J6phKN+f`n$6LL4DjJAmQEd8yjr@WR2a{b>SZn54XJ z5ta*sSh{JyFziLJFN@vJ;-yzG=nS7h_p?WTPO+2Ci|wFU#AuVuTJN$FMr-Hp)g)L_VYL_W#DsKw4DSU|9ZOvOO*+y|&NSyNttY2e^DZ;3I$O-NrQDv!5_yCN_Vw z>g1_a4F@?Z*3*!jO~BFsV7cnF<8VsB)CdY5=v23B-mBwUk!dnSMRjv}RGWi_BG^x) z1Wc+YFDnLctB_^9?qpK0ce5L0`}-tWhorJTdg(Fy&K&{XDIb#q41=J4syr8=*Xj&| zQz4ag-ZjJNhPk99Sfikxk6KQQoo8h5_7IUU#hoes6UUU1W{jFRI+ONeFETJyNa8Pz z$5gx6W#U~_1)|YrD&8icYO#-EeiySI!b4D`Gj_IeZHXXoO4S~WC)8YN=VVSVaejL;QjrYH70H!@qtmrY z!hkumOw3wPx#1ItIX8gt&HKe3@<7Xs6;x6#J*H1;FeE;SHB+DgIe9E7*|+7Zec;Lz;nB zdqk)COfjo^!K@+!v&xY9DZjc2tpGtP^7XU4#Z^`RIt(oL?)SS z71c_~vTs|NWK$eP8M-okccuv=8X~4_Vxv5%2Tg#KCYKH@=MN8j0PUZ#z*+PS{u!vG zlwvj^X~NLbHU@aOV^k?=Pc;$k;gICFR+(vL#oR}PXi5zcTzeu+g$m6s14O7P$ZKid z;ozP%E{`jG{9E6Lv$s5r>(?(~E8D5ut8z|fZwr3RW-kwsCR!WA^{*zgKalOnd6B`O zGLuzmm8EAmQhJv)Un2Q3k-uZo4X^tVNXRNXb%8wmd8Y=UYQx$}f8T343ra2aTZ@l& zP|D@DB3cc8CXui$jnl#LR*Ez+8*(2(B0(`F>?tP6u*q8uG^letn^@=zKM#S5t?C+3 zrp$d{*6?yO%14S1cRzBZ244GoCV!s}Zr>Xp`ts4P;KxC@Ql=gR~SH53Hh9_S`Q@2+;8M3@1aT&Be@wHY0=+dG`5sJ&u`O&-_9+X=r=iv8 zSJ8M;tf42hfjn^)Dsa!+B~0yXU_Y;X5~<&}MZ9=kmU!ERUvFp#%jzn}NkiagfRx(s zDHBP&nmb8gHAz5~TlYzmyyPL_D-D4EzmETt9qMN1k9?zZC#rhll(Mxsm$eqaKA%*a z%!?!`Zun0${yKQ8F+}RnpX5QL#i1=C;^8V>OwzQxcDi%*YbUcL)a+kXxb9~2+tI$@ zi$^uASA8%Co#KDC8L3SlT%u}!5O%6r=?gFw*8xvl5Gc=_qsha{uC!0ZO&v#Gs+wY3 z=aQ$rY%~JrIL0N7Exp%Gy;^^w@gK4e-G=$4I(R|*|JfbhtA9Qh7J!T13g1CP-C7rA z*~71!*CW(VJ(D+4yP{HN$oRSk+~^@r0|WyW!+^yy**-U0q1(Of+dviiQgU+Pz3AEv z`-6gno4jP!mKoGwyVGOB0<((fb@4{;B0wYDFxoB{rJyDWuU@)lK#3Gbu4{!i)&Ss? z$h|1{aOx%LN}H^J#(&|d>AWYib_-mX8sWMQ9mm~>!-EwrjR*MHcRY^U|LiH;bM-Q|vcXtooT43b zG9_lbNE_m+Qr5X*iX&C4X83SFy@uqHW>~uv_YWuaRu^+o-hZLA7kRsDc+(+zg5*uK zdQqL26xB1Hrb0dgrVMD1ylf3@nPUE=PCngh;b*V5hOn`~qRnKl;P}k=siFj|fkUm^<`QFk914HDZymL zdO!kF1H{RMDKgv7%Iem=_LLVTX=)SJ)S@S~5JgQXk$PoCZQ!doxS0t1>^&358pdXW zm`FkuSgWR3sOp}K4%BJ0u59s>VoDWfgP^l4SQ#Md&Z9K}%TJw2e@K&LA(=f#{N>gU zG}}X}d!6&L$K*XrGWn2~t#~ z!W7bM*VCP+rM-5g#+=0klv8i!y>xmea`rYitAWZho>Jk7#MB2?9wpm%+&P8n2^A<_ zezO0UJp>SQly36mR}hYi5R)0NxyvP02Q_LFB=>!ioA|rHvyhkE9d1kEf*j|=(xjPw zq^k}X72``ZLN3Ijzf`=!-tuuJOl&Btog~L?%^K2+z!(BnL1w1WwP7H0BKbY~>3u}5 zD(UTX#(oNAa1lP3>xF)AI2o-t<%boqW5=2Ra+@H`M(=4dDBX-`d``k|%l!m-<1<#8 z$!?bmEE;kV46!L^v>Lq2h064KbghPYaGEn%8-?7OHUm-`plN}{K-usTBzScK{qtu)KmK9!_NI=Wf_dN!gpjw#! zVYE|3F%K(aUY#<)0&hyxU@RWr<7UAwK?BKaA(U`=hvV=?LSHG46IHVo}=K zA@TWglUJMIa`7n$)Nm2EU-j}Ab=m0bXSZT``Y}nR<6ZFckx1SQjv*9jHJ~^EZgOMw zoJ%nQ2Ba~&O>ClAKjW4Rq8{thAc0~)-{9Hi&%zq2sWnC0-z#18Oo|priNlIb#~4ql z5qece5V?7P1%W%k1!g6oXMVu5Ny(Kb+)8=(UXEMWW`)ttQ}f{a!o^_lfk8nXNY4YP zjbmxkU4y8KlZWTFPbA-HY}T1%|DDjQ&w^EI>Jw?30!N^naIUZe2J^MDX>12hXG>cf z3YA8dX!mS!6HGmklUM~kz{jOa!C-Gy>EYz@w3aHX6-lk-pLL5r6lVxd?5Z%X46&h7`sx7I86DV4dSPRRPO;JLSLDxkU*>4zR<%E;X zVkR0e+k4ayhb$K7tTLke^OQ}s!f8pO&O+mgb@$_GsL+}@ zAtq45*d#C!*y`(&N)Q&B5LaVET6F_aSC=ITq$la1yzXe#EZPAkOEHNrM+v~o*8-qI z9(*f^)pdkv116u8lUt|S2}hHbIZ|d<&U#pkz)Q4J?Yy?Au9|%*Qs!jPz% zZ(Hf8(3;E|QV@kTHBoFhUaR3s4 zgdqWo<*@g4IZAtodeQ(Nj7>fX@oo}7jf57>B^o&*zNpqF!DXuiO&LQZkEr@2Ll+A= zKmJZ^nQ_ubxXG)qN3}bIXpqN+tT~bWF?Z6RIwi&kcD=aZ-ID+jbB-(N8Yzl7q`vx% zJFGLxg{a1k1>cp~1D(Lp^t!(#K`AkQS&`7H^^<*_Yl3KW(N`(EwkhtIcUT@Raaq^+ z#Q*X^JpWyfFY1$tUmdwQ(T8k}eU_TLhGBpyI81)bwh5y`=K_&_t0a-xg0P zmflk*&0>V>CKna^wNmU~JdD=QsQy81Y$o@MatnfB~c4bCBXuL?bVtoYy6_p7mZlmAsT>-uMYG^|ywU*pRFMEEWcEO-6jh8DLeV>uCFqP@=|>HelB<3^+JAFlI*>tap>$ z8DL*5CT0}?I=8gfnOB1Bi*<8M5cIKPG8YAB-tv~gP91G0qHj`1PvaI(`=hyOFcyU9 zLZw1j8~|$OYvXiLy9FjUaWj9~t1x7zav|w^qQE_m?ZYQ~my8 z@lJ3QXkQ?Zyi?JW92`}hzK#=h=)XBJr=<{-sdgsDjMC&(*x%c0+REOoG{H7ik7{z# zc+@69TWhm{y*TQxO^nf?TaE?P3a20NAkL~Rb#ob%l0y1Pig zByPf||6lh0JZiTsJL|)qx%T@tr@MW1swyM|LVW`W$qE+1h8PKfm1KyRnBa~8#yCzu z*kpLbfCqLG+kqsG;}Ls=&O?b}EXSq~C?G5>W2BOXBt)syRbO@M_V=`3^S*n{{IU01 z^Lggl?>Sc_W2EYido-@<-gD0Peeb*9z1LcE&gc0(l)vt#oPo|gA=qV1B8{eb)}{L) zm#2NmO$s;=9Ua{^lWY{N$foiFx8R@jlmN{6wQ~U1|4GolpippN9&E83=Jd0CQJ`p*jOj3Yn&*job&>@lu%b-3eX{^Fs%c$Xnau- zT*{&-cNZmTm%ke%s;?(zbc?7qRh`mOgug(;45s;8m$oN6fCU?)u)3=K9P^gT8gYW; zKVUlmB$>fUt>JQTa!;HyhkzBkJ*hg-t;`SzI@_?hS!8lXjz&nWol@CVyKzKV_cC~- z6mb3KdRFH%w-qCPk%>LlF7{*1;|U z?4hQrP7E7y7$K!rIF5H;6q3|hA#0XmkDR)rbbyh}(AB4A5Rz+459kw(HhCb~>ViwP zo{2j^oN|+61(t>Kygiq)G5!XnD>s`Pd$w$OR;Ow)q0OGs^_@Hfrsu$I?cfm=83;DzU& z9q*Bx*Xh`>TWkeNyfr+($*|luvL80&-`P5{QP|}lZ@#Uy-vsozs#`M5(4_h1>Uzrl z^){_B%WSyEJ8Y0xBQW(|ZhJ4_`DVruXO?kFkZS==N`*d)aOExJPuNndCTHJ?+}bqL z9s`;WqAKu>X2sCPu zpV^|*-DV}|>Iwp)_U6YV#c-Q%@^jm>&4pbMi((O|o{JN}jGL)8G%1FHvXk8DK55(m zC@hxRgkZ^2TTd((lGG4#G-V6Aq}qFigUSBqLkzNsYA1$hE|UEK!BNzpQ#z7_McMl7 zS#xjnrmi*0Mb%Ukk}#o^G6KGN|6Mr&y?NT!(n3vBY9?soQWXpmeQI2QXJ$ZrUSf~2 zRIsl$12bZKq#SMu+!EWAB}ixyyIZzNFdWdD4A(|zplpByUbUXSmXg{EanM#fm$N0b zKQq*X8(^lC+CiT)4+PMyLN*8}{qb>5U(mnG+fe(T+#4DMBC$6X^11#H3O&=v7a3Be z)}7Nyb9*2rnxN~$&-NQ<2b#rZ=7s)5pnuB^UTE=W&`?aEYxk=#oT+sVeFY5$@I`%q zX6(!J-l;4CE?lPpQ-L>jLD^(wjYB}+@*EpRU_kEXxHjUl)E5rzwb*0j?cEAevTyN- zms#AdrXKHhOeofi{TJT#G+ub&S?u>UKt^?yUE=tJT4d2qMUY41#(jqQ5`{Kb#|Pj4 z0lfc9p2PM2utvcxo3O2~$JT#m=-A-lZc$9t(}E1#xN(m6e$l&db@gcN)aOaE`Zxg| z%d|s4QL=#<#bO!e88{p&-u0g6@JoN$7cG*osffA-IfH;AySAp+m0-m#1kceVcgYRf-P!uv`9D{qJT_Ro4u ztK^TXuw|0lXco4}{@lEOU5UUot-||_W8*4hAzdsMQ$jO}(~?eQ0*8SQ;3n9T*$=q=>?!WtHGKNt z{V*>6(x>t4jT^w(gu{I3k?_kE%7R7HgHBSZh1CW<$!1xVsxX%pzv`Mr-Yt`^S=$ub z8A)L!rn#K)EXBFgM3|n*?o+PHT_D9wDGdg@PUjjIfU;ej;$;IehNQ??58vLPhs(a! z3UMR4Sc{b!CkpHIvXl<&1>MWtmQhSHRXy`>NNU^f)uy`OrNFGS37V3iDHZ#ld!)5E z3|SLEhQ`X&YxTD(EdGzF6SBJRq0GXBLW2^EGbL zl>$WfnaP^p&TbBRkNx@;Ds)R<@3c8c4RUHQa71BmOSP#gtTPv)RxJJAs!f^HzgLT_ ztV^-E#+rIp=oA`Afz+fF%M2=NeSr;vIT`sJ+J3feorx7>TuPz+szf30s!Hr@K#iE` z*6b+Fk^q)fdmm!Ow|2UxLpsi?M_=~>w_u8}sv$muG&x2z=e%1!XtM7!r9Q3KsQNlk z@gefiQnE|w*OjOA@vL*aoKf&dZ>Xl?Ut*OvY!Z_G#WBx_W{{!KpP8H)dnBZ1t6wyO z+ck7(ofAcQdl)7HQ(9#Ada@v_O-`o-uWkXO^PmaD)4Fs)&x9_|WZ^a5!*(Ld&;B|3 zO5a-VQMtZGwOEnjpP&orT;VAO^c@Aj=ePGiK_l9M&Jv)55$&Fd;HKSY8_1)=P3fDn zGgK+T+}wtsb?M~tm$XHnv$Ody)0ztf0f6EBJcDM8X1lGW@6#Z5-es==_22~0!B!F4!qUxZ}k4&Q@k9G84}9RgI6vQE-{4>y-fs6% ze<9iB*Z{|^zkhmqhWEekdHjf9uJlyvomnw<7!}{nv}7N;K+VDt>X!rRe2r(Hzl9Hc z;9WQz4k)FN*O21B*22UbM`Qm&s-30pp65RO{~vs1l)qK*tJx?}84g{^0%4wdJ+91v z0a}?R8WJ1l+KbqjVw>f*#TE-f$Len5l>XS%TO0$(>*-_MbCxrS^$fFgvC^UlA0r@h zVqf+mGLVVlGTkx&(6`!RY09=`1p)bIliZp-`o0zQQ~;YR_O;@v7oNiD$FK3R|HDsV z`tdjL)UDe%?101JI=b}cm{;0;Q%u=LbIN)*mZtYX9PFD`pVVc~qYlAz!;=OVO%k6# z<&_D!G!@1yxvU!UBzKl+nBAjywQPt?ga3xFA4 zPEF>#R+vPAM!JxV+64i0xxi@q?$`_AI-F-ZaCW^PdgCLnk;Zus{PC7C|C|53-xIYX zRSTz4Ha^4#4A&FKM=cAcD3NBtN%6d9DLPq_UnphAQmT;^veVGBLMcpP9cjf;335F& zX0ShvrZItCeJJV_lUTj1;&RQP?S-;YBRkY~4Utalm6D*eBz5CqNkENl00b+bw6(iz zh;Y>fzr_B&{(JjNm15dFv5u^D{xR?FF1x!BLdzXmeSNWP+a5uo%{B zxE@NQm^0AbHh|a` z5$S{;S8klnL|UfZbH_!C&%zlxepz8>e9^>h7D*$WZ*d4eE+IJ8s-K#h3oF z_rUBDUYnX=qWi~sqs?XVxi#310(`mmXSmO(b;e;n;63kp7B_BAxW1k-P1BPdK2PKq z+V-X9{N44wxvao}qyzTYHO6u>FvgC=);F=EZUpx+c>t1Iys-xOalOixp2ciCEX4>t zVYEH#?`^oNGDm>phP?el-^WQoBKMo8j;hh4ANN;#hHT^6*^C`|>|L={{3j7khpA~h z*LA+D_W~)!nhe9q|6JRoYIfLUc<=k3M)}YqeC*%&N$h_7K5pH*jeP+QepnP?>poJQ z$l1>KO|pfgC6{x@o5y!*s z0=S2-HxxLwqGxA>jJ(C(+qo=Q=Bl$nRFyEl`7Nw^wa8Re>0VOs)ErlVk+SIId`_F3{$;Kh<` znKP||M-j69n$D0;s#V*Ff?cPo=`8IcD#>h~g*oBGx9P{-LnoxsB8o?abb7v^s(B>I!f63Gc5T)U5A*_m#J?c^2a)k2E|r zoT00DJGUS_{;}neas!EvB~4UnvU!QfL2*}lR}OJA#MzQZKMO|Dh;1zeH&IE=k(AW2 z2CPk=>H^7JKCTq!TCp*}%JGs4O5T_hq(-KyENtL1zoP*70%`zkM`gHl9<%2=#el%1 z11g=s^` z@Ezar&DiY>*S6RY%6^Y1?kAXiDaR#nQ5@)ESIkTmIyEXd zjX?bG?kV4$==(KlymV}JT4oFqWC&gk^5BWFZNon^0n&X`QiWQ@75bMa;X^c~iF$KsDwOnj z(a-931Cd}Ut12BKUUrjn9#2&xiNtiwUSi^JsqJdn|4wjFvuGp)T{WIbe?s5g zWliv*0B^*9y=TY!$OYu*suVD0km>n#P*+%jvC5X?139XvATW0TSqUTnu^x|S$kBzC ztXgD;6yUKAI4wnGBC97wi&%GqwC4 zF?Yy^a+&r1p-og|;(L+av z^e^fc=cg})oZ-S9_v=QPd+g}0%Cucw{ePYz6Mj=te5bxF+Ud{dOe(2MGH{j}Ok9WUt1HbND zzX1R+O@%A~_V{Mb^P7!PDWj?iSu?!jUg6YrT8 zO`0fY>Ejdu!UOXo9in3wkf8}aW(f>L7Mi%4Czi%A-4FLsU$=*tP8;91AAeT+ zb=v@kX66G1$e*qQ4sF&rNBQ*@op%OB<1IK0wJd*co@d;7_8iYoXZXaQ_%L4j4?c{W zcW3PGoZ%YRn0;Oy5KB~_S4v%IxzZj5ha)FHkR;eWxqyA0G>>)>yBa%-ppgl~szc*>k?{J)pt4X7f$#s7+HS?D`LwbT+)4fAvdl*epeYi{W3O!7 zmv+C`RWZbeSr_%3%WIQpzwxF^pFvWSndvnpYm0N!nb$%o?kRo3nOox#P1!1v%6l*s z!Kl5Q5$gs)Dtn}=$GOZXd8#)}Nr7c4DFO|uN4*5Sk7{rHcEzs~-Fn^EqO0Vw-+&{f z9kOt3*~?fhla$u|KB>ghs?l_I-e=@|mzp(J-j?O^+p_^cQlEegL42BBzQvv#8z<&U{fP( z8-<h?74A{+>AJ=(aS7HoABo1&L(S+pPgCwE zy*~%0-42I&k1ze@@568Uwr@hkCH7Y{cGHgJS}O0)G&^;RauPtrZg&F@A70}2P2jiu zmS2zCcg~lI`EIvPtG2;~de~BFmL+T=Pygn_0U!9_d-3hx@y&R2`5Jb+8w*@;%@a>% zONpG*@7dnV+>mpCRm}5@{r-Tj{<^QgH~)8j1uieIaB{i}K-Q5{=;M+lj0Bb0PLYq$ z?Mc2nJFZaU5KjjS_(naPB!W(ov)ETmOeQ`1otkaF0nBq#s{}(CS*Ev4tvw>5%!iMo zgO^Ob@J?UroIb!=>J6IL>vSz{VHEjg3N37veE&IpEe^S~)1Svbsd2;|0eY`7b`u@@D-5^NZAhB43(f5iKCffG_ zn&d$vX<4%E3EP=HjU} z1IXgKfFMnaUujYCS)jjgikTzwG+kWsifePBofnD)d{VQ>i$wMripsZ}_g~@^8+@-M zH5C=gy{Ia2uQd=LopYM8&n#LF(!kAAVl&C+j{Ad}1sTLBN|%z(aFEZRC-_@j1)hBN zpWmN}5^`x2InQa_rvet2J+X{`Mr*y5v9ZW8QD(|1C`Oi6%a%$Xm&ylr;WNWH#Fq)d>$bCj#oB#a02$`Lqt@eL~)*hqO9-}`a2&^0! z-Be*qv|zE;NfdJ2r%vbkyiT&hxWg{naKsWEnD!pCvWja%MQrdsPifDYQ=>$mBZ^ec zDG^eGklmrDLbwbRSO{fWED`g_2z;xyI5{JcGP>6}1k>$}K(B*E)Y9SJSPe$+#fYzA zBmF4-OjE{@O!v8gW;xp$V_5S$4S>(-#a{si?^~$kr`e}K18Yv zv6#d52b9@|&H$I{yj~t1r!x82p? z6x#u2c<}HZzV2(k7T@um-@JA>E37zK0FJCzHp01%p%ZP5-nu@_c<#BI`0fAHZ@@I& z!o#bFIN6=SHzvmBKB&REPnj#@#jJ;71y`4sxOsYo-}>$U7|LYW@9Q9|-im58)0yX{ zOv}cbCiN;hRsD^9#bvFStV*~R%c37^2lEuOy5 zfm^Gc7eI*$eL%o)JE8ki{GdR;l%e2hfY1w)1}f}H0_Z+s>(?wAlPIMVvgHh8<2n(O z(6u?SawObrcs6zNU#LM_REE!!y$&d2Zu6k$mSZ|Y_VUz~_XtlZAf6^3-U@$ih&VT& z1Ikyu!dFRsQ-2NyW%tB;v4I2CKT8k!eTA(tb|jUzDYzHy={03{7*EvuPcGV&Xeyjy zWJ6(@ccD}dIs>|C(bR@*bv*+}Y@G?2o`Jq5>JuFrXk@#$n4;w$voWqVt9RG?dpTmPY;r1+9VoNwp$UVWVd}zMvb1$E@@Fv<)~_l&!3KKj zf(&f=*)df`wy8g@qY}ezM*IO6Rg6lDiI-;YKmflmCjXXv8!4`eZ#*SWs|lJRGO_5l zyxNiqszOD&C;1us-)XZmP>e|03{rB>c=J_lT5J|R>g#jLNUe!W5Ts&$V5#_T|LraI zXF_>UVV3}Qqg>i83xi3>cg*LTij^($xE5fk#nUrFXMB2pz{6xQN=I3tf)AndTN5KZ z5lM$q3Z`ko$;l4;{S|)oH+>m?_wV_wIMlni-d|xV1&+gdD4Y8<+%YebIa{F$m~{$m z%gp@j>?Yp4`v#tUcETV2!@m>necv;m7ba_ zF*!Tb zCLx8+GBeYKKkrq@Kr|nwyM+#O%fzV=8^OezVrKOGhiWF^x)dvDmQg(^`q32V*OeX1 zebRA3h14UIT+s8ct;iIOISYoes3Te`keSl!QAzQ!O!^nwh@#rM^?a%-E^B4e#q|$U@T9DPHn8KGWPv=c+0%lotk~l-s=qp3h zV^D52M$+1}HzBXb59pRQp^v+&XQ*MM7WLH{)28*P>u0rEKW!srWO$Uy1`~@O=EAHF z>#$8LMYcaqr81S{h=MEBrZrhB0zhrqhG3E4(emUuI+-@qIX}{?AN^4%YGFS}BPWn88GvuU2EgO|%uZeyPdB_q`Hc~2Aq6bh3ZOQMAmq-tda!c*T1p-;Y=e`Kx^G0} zG=b@0@L~3!Wg&Hnja<8gjilP+ZZ^od3$=LJS{14 zTm{pKjGi`bDdrY%)@QB{cv$^NRyYBe+){(!_>;ah8O=P(+JiNG%>&rLr`~Ro^G=9^!eI7+<$ZrFFaH6NB)<;3pei+pc1^8Y4zcarJZrVzq( zWX;!z%P=DOr*WR9tpY?W2U(xkLSsVoHg{1zi^A8F`jnCb%xTS4d~p;A|({#6PN2tzI!6siXqk=F~PMqT4#P3|x z-)o$sZ3E+>=XkHf4xRif{Et-yEg?Zk`3hYNNsuMTqzOr=Pqj%Q=G;hd1&!I7ToCV% zhlE7JcOL+VSf}x=;EPaPir7>aL`-Z+jx8w_ptBj%;!{Dh)K(@=S^inImOPo z8v%PM<$>&NptOy6wkoACIjL}pDir#D;qv#r@@LQ9c%FRIv{)d39;%tdM)0IUi>9?I zANjLIw#QbQn&HwTigBlqQ265qD(^G^KF@v`6ao@iuNE)|H!Ao^cDOkvb&XMS$U-y>DVQqB zl9(n9 zFUP<2$A32-Ufjji^#h!r-dMZVh|P&Eh^Ee0z-Cwh%I*Y@E*_%pFY&MbE58@N@t^#9 zJbHM6v-6W>Qqlo{kNarsEkO>Cey;1Ye6n_bKXK)l&9FO0QDm95{USQTrfP`CJ2S@4 zbN8ngo#2d9ZPR8o#q9B+6u!~qJ_`Oz-Tyq%$8oy}NB!6aTgA7YB8f}GhVOzpxmqQJ zPtV?3MC9m8@a;4eB(Xt>p|FuZblFjtXStje+4^>l@U?Sqlf`30?DC_SURPg@z{*4E;}qkE%9h*~|uhhCH~Vr%_dQcBN>1 zaO9`>jHTeS@WwX;mFvXSY;~r$LwjzPSwM1I(Pw#rBcx{Apl54TW-&XWbumhCaize+ z1|O^^6`B!`!@(>DYCNGVndszw_4X{wsk}riON?fKD@o&dboOCw@ zCA$PNtb28967qXcbG>hPq>c9o#T;%N!?JRN&N|L&pmyInaaAzxTk8baxe#mT-L`&M zqV!r?h>J;lp%24=q<(1kK`DCe5fkh7+2`p!z@GYGq4YJxyl)I}s|+*~$NSwU+h11TBaDiHjg2EgZ$4#2k+02JF4z8+o>ojPLL zsbc>NfT1%kT`VslS0N8~EXFPjfyM-A9sM@>uB(s5D5Zd#7S-!!@gLKDbSaMd*}22$ z0?G28qJZeAdR2~q1NGqyyT1n}>#BkwXim=QDbpNE`ZhQ6F>g()ZBRN72QqiumLp@M@bp3US4E(4HU|pmIm^jmf=l99k{G)}Rfq&8CE@G$2;y_@`wR!z(6poIiB~Po3Yy)!%p>AN%eP z;r@T|Q9L)@#L3Mw9LfQQazM=AW)S;3rs&RD=q4&BnX0m)jV52>+oU|^So>V#pVs6U z*{2+d;7`v`pRh#4;Os6_Bd19ZB$dFVk>RY)<6?xSi`B>2H1jdPz3->)eI2SqK=*-i z9?FF_k&Nk<6PX*U62N4y(L8q(FED>SD19g3wno;$@ZmF|pLkj;tv;fL-EE-x0C+%$ zzuWrfO0ti0g-ekTVqte>AUP#+bbi2ww%kOzPEmwpxMo>-+*=;xbx#z2jy_HeOPS({ zCm7OgQ0fSlW6|8RqMzQ!2s&9Gq3 ztzX{vERb$9ZW3thqA?r6)@i$}Kco^Vg;EZZJs@Bx)U2nW3#S3JM}pZjP1avMz@(LU zYeN1{EHBT*{@4f%8aELM7OEi8L5TN=c981J*@{SKj2z9J&r_b8UvI+>zz%QMUm3)~ zzI>vKvdmvIFtsb&m*QgT?|Qt8I>FYKf;|eYO#kNy8`Bg35VO_8H^gv^#H^1d;njN*BGy_AM7Q;yxvaDBbUmwnlL@V(#rNARcq zqhEvn)4%^cc=2;DVK<%QiU4w-4xTv!M3smOk^J$^=Zu>J1j4@OmvDl_VbKsSMUe^z<1$=r*Gkp z{G0zry!yuLxOx68&QB*y<*ZAPt;bE z^GTu(ju3SbD)875r))FXWQ`#y$8 zf9JEfjWe9yd=`gkM#c4F`Kd0Sv$g3RO|VU3eIVXESMQemBufbAG(j?EIO(r5+X|6UGmvB|S`5=vA|^bG7Q&q!z!ybkG%OMM$Ir-R*|AxjSIW}le)Jzo_kRAnDr0Q%4!{P-Pe>) zS|SF_8=DB~i8cnu@o_>wSN{9#dp80H*FLo?a!+?vkxYzDUoXkS!AU0l-YCfeLu{q4 z9vBBTv8u;3K9khB&~Mx)Uke`DJJawZY!7uv9I;_%kexKJmao&a7y|M(oM~0RT zzTg{_Yvq`Z0k=`e6a-! z#AV=tUb}bA$B0`4Pz%7>;ZEx0?7w9@QD2KV^`(S`7$q8|;?>`Z5tE@?6~C}?@=;lb zh15_(u~FKY*MFl1yT#Wp9bvwo1RE3uc$mzWar zrpp~UU&rb;JN&`CLWn#9b#oDR^wIILRI+h1)!#EILQJzr0AD2ClRJLs-X(tLQ>S>( z$%I1{4*D1^XQ?RK_(R_;WDJi-?t9+=_SXm8ym5*@_^kEq(mQ@S-y3#eCwWq##dD9yDC*ELD;h8Im7v3hwD#V;xm8aMLhWaPvQLDjAw7&LbZZx+b@blAH>Gv9aUohcBkt)f6Xifp z=jf>lu+B$9hfccs?7Pyls1UC`w4T11 zhnNQ@C#LvhIp)oLGl7DYb_|~`?fbXFw}u^p03g&wY`qT)YvMX>k2RsL&CzxZCepj{ z_EpCQp=<45&E#w^!!~dGgs-2MYiu$pPS9)~JZUzgTEnA>!PWp_EI^nc0a_hxCeF+t zn%;Mofo1ilLJRX}#tPmL%Xz}!G9xC9KP=QI%3W*FN>5A>x`%6x!rZho&D{%fjb62=+B(8rh05T@|3kZrQ0{{m-Ir$v>rqw!$y>nj{|2ZG>q$#m> zE3XUuwWS#JvIbupC#!EvmOUkD$pJo1{(Xy3?g9BSr%12xX<9Yh-0kp_m)Cf4u6Xas z0{EHNJ#8|bSSfBAdxNB;Qlz;{3TZTR@l zeHI`72cN(vKm9W5?AV=7c;>06@QvU275K`p{Qyo*4YgM6_t(oqTTEYSuCxkY!f~pb zy?H!YH?yPcc-VUFb`$1##?|FDzUdpk0^j$2e-uCX10TlU{D1#F{Me8EEI#(h&*0+X z0ct&(I6LdG!0QRIvh#Zfy{Won(z;7G<0pCm;@HCZ@so?MfJ%v7z{5k_svj9qSsWyWo zawDPAp`d?U^9(P5-7?%bKf^gDTzvczpZgo1#Or_aGuXW}&DEJWIDy z%*zAHUc@ZDrUO?T3?uO&q_9TGEQzMr5AcO7J?vAD!cVPL)?b4s+I72*47%+0wMND7 z?~N5?(eUC1miAh;GZ*{wX!YX!?(n^`f+EzpmYQ%}TlO#)7aHaqT>{RG=MN#Ph9K{YjeMN+7rE*(Di<4F`KrLl0Y^tj)3dRTvuxullK|O z$meNRoN%H74}T?)$!wId8u!0_du{bG0^1Fmmnu~JLIB9L7i}conE5EZ{f=Ovox>oGyuMUXn+W;jfo_%3sS6kVJr&f7JbtHTaTTkXBu0Y7}(OZukp7F z`OM8Y62mv?o(7Zi0Q+c|kZO{nX)*Bt74rxQ;M!IbFH|r7g@{_?b@CL#yk}~ftv<;z z!Dis_*$bRpI`*p~U`4$RDTGfXwLMOrOj0o0nylt1F3h-HcKF=2@iuTzR?UFX~|yW{%$8ZhAOe21_8hA+m~{K_xc^ttc%2V7n( zpZ~Nooa`nQd#r^;7uZ^o%RdHwCtksHAw7GmyZjZ{VS!N1^Nh>OYn+_y@NM7mEAegL z@hfq8all7^{zbg};$0l-ayWhe`=7&?f6e>)r8^uBxOjAlX}7~vwx_;>MdWd9*sYj7 z+h*CdvqoJTcDo6q|y>~Zjl6BN`W^?ppnyFCo||1X!Vc2IMbZ+i7riGd>u9VD5w z77xQ<{H>5}OaW+?i*!mS07Jn%3o-01hs@db&zSm4-Z)h>pRejNXF&Q%_0(JY^F57QX+G*VbNp>&M zh_*EOd(m1JPb_g6rnNY&O$B1{zRY-mHi4~ms(1msnzs1bw4N`>IKa(>GtdPAV&R(&m6)H}`|GrH6EU}p=F0M7OJBV+Q zfjiO2Q2{R=QtgEv7O%TPNcGhyuB`Go&LShjpUAC=xp7 z0Ah#hL!4UG7H2@Md@pL#!TGG)3%9a9m&@O)RU4_Tk&=OR{pW>0FYK)_J+pZ`7h0m* zn4VjNns4Msuk<;i%jFw00i6+&k_2dnipEooYYlBq+FFSAJ=G#JyQ^fWk=*;Ol~h5! zX}dnN5@%G$#!Y1f7}X~|VZpj$RpkCMm=I2X#DueIkW&uKsZo_!JjGN{+3tnvVui}- zFupQQ;CnV5XS0U<_Jc#oQW8&D+_PyP;zS{1GK)evlBcTSMTONMAJ*5D< zw`Jwu!A?qeHj)!{NoES=>MQB|pt$8bHL00;&i|j}{ zj;9qh>5&t z^hv7}xE}}^SeFIVJgHinDW~qJX85c3F7cc1oa3py4*2Qoiqq+cJtD3BZcYQYIH*L#*(mQfIW&*y z6(hi87zhaT$2dhq2ID3MC=L9|)AP~qsEPAF6#MNB*oO3;H%%!VDT>rKMKwBsmUUp5 zHiQ(G$1a=jK5Q^CEEhM{rT!F8539ZuZswRyC+u$TaCUM6JOr+P;vpXX;LEuC-@F36 zc#Wq|PB7iRje|S(xL%8kDn$NR*UR8ok*Q0T&6^&$ks|nlif%N$@KD3|2^mc}N@6uo zLm?)Ew!sidvMnysFisA)m2#eBpV;uO`d|gHE?f2l=VT8v(X^4Ld?0CV(dP$RblH$& z&dr->vRB0-F6%Et!y2fN!EU`MvS|3H_um=}PEA+6FLcw_33&n~Que!}E~;|3PTOWl zbnR!D66zGo(WW-g$N7737P!XqLQ;?D^1gl zDlsCu!iHn$s37!ASw}R6AbxZoR502Qi}%^q+O8JrYhR7{%HHImrgEAF*m`>|%9XWg z5jTx&ftI&L0Olo{T!jF^S(>ixj^QZB-A53rna(er#KWoF-PqyC>9%^xh2)>*i5C z7BMFolCK(^Dt3akEnZj!^^{Z8!Be(Qm_Q~DR~m1e{T7NPH`c&0rD6> z=kAnSAKsH%aqDD2N6>E@tNH|^NiO~lDt!c7M@~iV}CEh`bgj+Lw2$bfc1aa}A7E;d_RG%ectwNh1@^cd_s4-*= zCIxx1irN|!LgOd-u$$z6cVJ(s%fHu3Q}9*@m*w86F|9Z8P;(MR+6%4dz6O`;S(-@ zZ7uJNtfEDtQFDMECvFIq_4(_yLZNd_vtJa4S6k`*O>yke36y)*G}5j$!yL4h#eGsz za*S~dqeq=%T~M^DEo$rsZErC5!4l2GF<*ypy_w?eJ!VD)AMc7JT(t96wVcvhh`G@2 z9p65+H6_KMypGsGmzdU?xG3xDWA88kz5srCVhMW^0(r#pQ3q&@^Gjq=kf#eK9z`%M z87$i;*dMlirqY$uyd|@k2Igu1~5^(-cRjzc;!&>1NX1+b!RtmV%B37 zyFt|bi1J_kq?^@_aRJRZBj50VlSRLjWY%AHipe1d>4l8nibUS1SS_-V0ALDMGbYYT zQJDOAP84RQE<1*?L|^Ew6&!W-DX$B2Yerc_Q=vrEVBt-JUy~(&cPi?7^TkwBy~UE0 z!W`NJs1BSPaDR6E@cnBXYQ>oq%+=OPaN|9T$K9oo$j36@6uuwn46yQN!(U%|K~dK; zf^F~|FeG8vX5DUsH&lPaH>CJ}40#y-Mb^gQ!D)73wUTUOgDEpy>*7NJHg@g8P`8?iH9G08J9o)3a)eXcmKHrZX+0r}(wtNO4OCn488&sR8=1$kt^90_hW9ebhkj!tE z&x4wG)U0kdNXYAbU5WkNN1yg>WMWT$+3oh`yP2NpoGx1!tBqdi$nP5u1*jkND5UdbB$8dATc&V!C+#o zQs(cd=pNHnvPQv&L4qX}`8y4O&--5$5^UR|TX+GY{LNxY+4t3NR1}39&4b=(YK4Ns zFhv7TEO=`lu@oWWHu;g@xdvaGMwTo^R9%axvIzDnOZ{D&gB&|nh-J{dcWsTe{{Aa< z(c^V^OtVr{#)Ja9TTx%SM)~Sf%q<0_&;)>rwCYEIPMHC~vU(y<4~4#EnNE;ID}Zx% z+$g|bdgBVe?U{3Y@#%!i*;P)Ky+-cGoyzW`w-h<hqbM06~o0~-K5M<&kj6=cNu&d7TTo&>-!DNwa zlXKcig8B+Cp-`ifZN#oIO3t>U$VAWH1f#SF=fCtwyvo=+FDsFD8Jgo+bZ?|w?3Kd zjX0iUG>LPQ9>YTJ>Wk#N6w`=qCr@mfD0c1*P4iv*M!vq*VzrmN;V*a8O4BX}yjU-M z`W$GsaSBD;z<`g%kU8+w;ui`Zax_Q`^_ivaH{$x~#L^yWS><3=vx)bB=k8 z3~q5`yk?NaAc~RFWzd~&-wz-~a^JnHRlHA=yNbX~WV+U;gq0K{>%G&+>o(ff`up(p zT-J+&(i>BrC-fAr=Nu>4h9f;D)01t>21iV93YB&aD=5^kmw}E!<0NYuHjzao*5`oe zwSA2o4>6B6?lcfh97J>`|` zJ+h$GHJ_rAMb#s!(T(n#H6GV1G{K^&6KqkZv~zMs?#X{!SQ=kYiqpnM$rZdw_6c&z3@x5y^|coqqOkWuS-~o+ zDZ3rYMMeGO1ty%~a$5X_;5ZZH7?qezm_&_)bdWcsn37;yw(Q&4pB0YX?6@-(eB|RB3Sqzg zhWfx&?!CnZ;6}_QxUD@$!}W^hO#f(##l(u^CpQJ>*~(xkuFXiLz)Xe5>$12+)dgd*zkrDi9Gt<66*Nj%wSSFswgsdQE&N98-Lzak` zyh?%GyYo5v&Pe5>9UI>axb(;LylpcD@W~{`sPKW&0JJR zJycY$%k+O|m~Nh6cVoiI*$$^(V6W|Q`0xcD{=$7+{LDRE{mgyT&s<=-t~ftCf!#R) z%7khT$2IEgG6`DNt*V4&CmV)&vd^2+l%4GTm5VWp1G=-2-=)Zv=hs-X!B8rmwi{(c zBcz#?p=$}0Dl*M@9xAVag^;@$u*zO7k9|z0XqtmIup36e)ub$eejisUd}6()g0ADx zy-%vzYp8I6sJaLXDT33z4=Eu)Us8{?GPalmnJ1jhY%{OKAmo@uirEI8$^H$YmNYit~i4esk zSfqEQ`%uR>(sn)K`Dl>XKC6fsB$pqbO;e2$N1?C>`T7hyopoedMD<%(@${U?_iUUd zHCEG8$QRy;0sI0n0Jy6VlovfW@iPVh!vq;wET-bVc0etI>W#1&G~JGM*2tFQI^p1= z%RGr_y~9&9!XzlYl3mJ^A%+VebL1nV4YbYM@D-~k4lIP8JLKXr+d zy#W(|Lj&zGA>YPuFiUcUC!_?3w0;{iKK%UymMQQ3-46fRo7eaach2zKRB-Lfjc;$c zRoV+mZBOpRt#9ZftyL2ruO`m5ZqU?cDkRv%c*=V$Xl_}4+kd&<>rLljT4l!9j0lA zG8^W**SP%n1s;6n5#~?aL;cwY*nj*2hgbF}mxi;ld<||E%*9dHX}|Yr&oWhx=duRT z+v(ZwS;%yKx=ho%XPvZS%U)wK5jRSmDLn#gjlHUR&)K>TRgOm@`A}KC;e8-xO0Qm$ zTw{9gd@y6PkVhlU`hL(3s@x-u`?U1w>jYq4i>%lO>ncBP6Gc`Buq>BhS6jeUU&|!p z345R47^Ms-ppzp-8Io02A8qXQ;ZIeziV0sJNc8BQ+=!%$XSq%JYA5iUlGjOLBfvic zO-9k9gIiT;Fo796$?qzsElAcmHJba>x3K&w)(Ydag#;&M`xrGaMGwW&>SoPLyW!%~ zuOyY$h%}y98zxd1peqTYIYgmCZWO7B1b>Wzn=Ky3w#%)(rw7TeHY0{Hvjrv;l<(poG6+|S{3T^kaG*0&7#%xcq>pl_V~)%KAVXklt)5Nf||z+-tvXFa!tVi8+Yay$UJ zgej+qzVY+D?g2H}5X-c_(3wcQOaf#yidY1}06Xxjr~(HvL@d-~G-aj8;?L!5p!@Jrq)tCT(_ul~AC(TE$&-q_F*mtP31}V@jGO$fk|c{f(J_6294;z* zC@BZJlRCTf$3;?)!R5@+KI#$glz$`N-ayzlk};2Dg*rQMvb>l2$qSskKErNL@T-<{ zZ6arM*ztCn5^qxAfYD(pQS3L&zP<6VKERVZ?(7PF}E+Tz1@8gxN8BLYmM=(_X!H>IATVD4GV26;Agjcv))iudj61pBZW?!>0 zowCl;2j+<%pvfIBc9+T|Uug8858OT5j!qN{1s~Lw15+tj8jU-HPt(HoIx*m+z)uRw zWGG%RxnZ|=)cY0nvwK{<@&JbyFHt{xf$NVy!2YujQD3`;-Jdbd1-p|8r{|}zr%u+M z!;D%FtG~bY18XZ}y@gu&9RA?h*Cr}iSlso9FVzS|M)e7zk=lxK;t|yJB@uAF7D#0E z)xOr({&gS9@q&Gt(cUu3PANHFR8g=*riJCE(0=F^M04lY(Iw|a69dvAJYZW3oX(0%LF_FWP(Iu3 z=shb1m0|;h?!7jQOuVv5MQ2$>Wqk_QBK>;t&dTQIG4o{rQrE`RBB8e-9myrfJ57(B z_!BF4qhG(mMOo=XmTniRH425UKx_@~xL2HV(?bqkHkb>g)xI4Z$@iU;iojFkVhnY? zH!T?m=tB%z00Eo2!YIbMH&qK2RlONo*4Mak{nzi*K*t=_$^Xj~dsLSw>8_OOwt82- zf$MOE?|-AuwkHQ9enMjVOCj#WT9O1Pw*PU1bp62kX|6zG3x(ECVA3TCqEj;=2#kK7 zj$*s4_k1;{!3_@0Z7Sb4)7U?f{(0|g*5uoDDU3~EH#MeApl&nC zJ*#iX{+Y_XX5LN{;Gerj`Rq09>rXIW95BrqHWrPPal##vsBNH|CmE^wqcsT8H)gmWJy>^9Pd-D|U+8Hh?NFjki8fZLj=9rL8tl=o>-<-xfHh9VGud)&5=Rzbl9b1Iux85FzwEMprrvq>%bZ;<)$yBC43<|6@rol!j0{UKAGRm@g z;?Q&{ZGh{BI8g?jaeSX?Bg+%Z#--_6=&+ihj#L;~9DL0<4wJL#ZHTn;lzbFP5!M@h z*6k03`8AHWW3Z`p{sY{{2uCQh#i^Jx<_utk=%pB0_T37sRT>E+-Lyr;MZw!_4wj&7 zdIuVEys5AL_hY?jUF%z7gCm{e@`l#-3TjqGmV0=x`#!|fM%CWPGiK?G zlwE-cGO(`J4bFo3z+hs)w^|BQRIX=vy2UxVUFXuCjmA!K-4IA2qfkjBl~7EnyCmOi zu~y`~D%*%=^^J(duP~(c-1^$K6Qvzv~1phhnME zvhtHyQztwZmd*$E^+bNMgTHY={e=sheB%i&OUqZ=pvei@&C^7GYf7AqLOBGPa-DyZU_a9Mu$-KbrS90!yVzsFiu|66IZoZMGsjIpvgr+*GtKk_2ZPj)!$)@fg{ z1(>#511xx?4k?m}l9*OO1r3^yunc|^kIHJMN%MJDs=&P^X-Ih2h?(o$%4Ague4c*J?? zLiit|1lwyXGDWNmPnB3pRi(7(GGtXIH`s>efPxA=4Jas6U`U9tm3>hAv^N86)Bb4; zqKkOL#;A~qITtC=_ati1AsIz!>%AE2(B=Y%kLfFPo%-~*B%d{AfLRC4y-)*WEk$!uzM7#-(Tr}fe%0AKk8Wt{DK4H)&SL4K&x4d*Y}0K; zCvrv*soLZmLANh+U#zM6dILt&N4z?vGsN$4LR1Gt^1Wgd|JU~}+q4jx*}w(THB5nq z4NAs}D$krXPC@dB!!*x_EY}&8r$}bD{wPjLBOy6{QWAu$TUb03guO1!hItRv{W}VP z&wmC$U1hu5BsBNrs-t{V&S&MQ2^+fmn&)f+RNLUaC&^yO1F;cp;S@oc>CZ+o8j}F* zh}ZA|+{D)Fh+nDVi)!PyoM+CF+$Oo@YjW0!!R|WhM=x+%pT^aR!S^}FEtSUR!|yti zbaL^S_eu9B5XGoVx~iOS?F!y7;7`4Jf&cEE6MXHi;Hs)xplr#Xp-qN(3wlU@veVy= zG!@8p;TZjeKH33%OQ>yQ)Embl4g3NNdUjU8ba_<9y^*3N{@jmWqfPOEOF7J)>1|ub z2CN|+)bnKC)7Lf^>FmJy1pL^e8Gq{aOFVtH94ZcTXV#?qlp1EnDGMYq%osyUFJPlf zV4Tlvj;R!sH-O8ZxQ`nj+u_D7!|eOT<6W%#wU@)dI#G0OR`p`5dTVP7FF;ZhEab|L zy1>Y65w*`KD+_^(p72m#HqZ97M1FSKd)w11DJD|!7VD3T)5lFsBNbKeXLsOGVY9(8 ztAwzx4ZO^s3(CBynz9&lLIQneo*j6h#L_!5cI&0syKpVx9Em}BXx zcw?)$*qi(aqd}mrdDz89EfbTR&>5{2`G+GM{Vvd}}lBKGx zZxx!1AnTYhYs6Odh4(-)lu&ALR3efsfQxA7<>466x}D)o@q>YhX^N>5Yp^8%>SH90 zl5Uxzxt$Vkl;NWFPgA2bmJ#zN2Bj37k{wz!tM|q?+tC1mHFGUNKvnC?s=8fQOa}NR zyk5W40QkIb0Mt3M>-jw97APQJsj^)bQa2ZbH7J_oU-5UGCi2n|#u$NBtCNILC7mtL z)xAc?m>#4qn{rp>LdqmpVzEmGx|4s7vp?Mxz~OF^iOg0A_GmS-Y^@c(TfXo9=N{nn z)dQy66Y9mRnd#=q-ftUatPQDjqwLEcKi2jw^8G6Po*mDh7(RMk@qc;w8vovV&vC;H z`v!URjqiPIg3Xab$)4cJ7{IYqPjunih9usOH#hWLs-Aay@=wF#SMsgbo75P`peT}w ze55t<1UWr6r|c#g{g_5zNDbg`SJy`FX)!#k6@TufD}3y_;FsNU?Dr-X9(yeJK6#^$ zbvv?cCf`?;aG&NlJwL(4M=wxcbKHLZ7Rt>E%-2h?d!4=un8SjWX>dans!$zR9mDMYZ(c4 z9RRr?j+^ndhrEu6*|LDV$z&c{a62d3Xb~GRXBzo9*LjiZSN2;gbg#W9Ee@5Q&BaYI zO>>cns_9ySqj)j4Njc;ia`Pnnh|MB@f*D9WeGEPl zhG7La%UqOHDXL5g&($_0O(IpvH6OB3Nri9*Io@=UrV0d^Ce{w^ z6p@5?EO1x;@aWqp<-nQOz@&39t3dtt?Im>8rc6Gq7yfPmG;@OCYFgYf>8{Pgo#^WD zq_S%T8pZZ?GJT#56F9Ivmj)etIgPx7S7Xv}5LK(BnK-3Ptnj)TDh0U@K|?NykR1Vv z%x#G@FHc_kxGJKCH?ry&Ddy!oUSn`T9EbkzbzT&Ejv+ZL6b|iK32RAK5)JTZeqBAz zb4`FKrbz+)dXf60u=OFBK`!>*^x4|>uwR;|>*m)d|Ij3buF$fPwSn9;qZ_>cR);ti zikdVX9Jw!vX!JbM^09u9C-8a&9IoDJ0O0f90&tb|G}b3%5gS!oK$BJ?mZ5)oUjSQP zL4~2!>}m9&eK?Sk*>CB|XT_-lmAtrGNGYtuI0b+nTy98Kp02fRkP2;26)QJ9`thPJ zleB49;Gek$e&K-ftIx5w15V~8n#I8?0vAd;DV090NKfuInM` z42Gw71^6npPdq$b{4^mK0xJX_HTvg=!MN7gdm)LEs58Vbg+#_9%Wn5ST7T%L5}ERrGRJCr5qA+c8k;W zc{azH0e|I{OMK5Om-xV)f;um9=w;wc6Yq4GNrtr)MMob=(Jq043lq1YMr8K+w8NV} z{U)Xd2h6vC{eH&5_q|Ks=)MhFWMZDz7`P5xYdOkt;O#EhMbck_TJI$$k&Bw>K#Bod zfrOT(jNTJMN}~mrC~Vq+r+4r(PDMP+VL=K4eauU|&fOncy=6IjbnPp#CLE)(q%lp% z-_G*pSiQN(jp-#)^Hip9&HrHOP;P8fX+Q*;Qa}ry_<)QC+O|Tff``zX5oro8nf7wK zYB3yZg!*|UZ^d=5s!3VLetsFS5>!mRr`8GBCXSlMizw8X4qvR(>Dq*}jSVhNrfI8h zUYG@-!}JEx#p(3he9BQEIYGv{YZP0o9Mh#u?tjUiml0m22rC;7zH;y;U|WP{R6W*M zc;1-@VaBv^kP3#Tg$1K^QRBub?lSp-bD%AyQ_!44eBv&mhe!!np3bV1P*Y1rV=kaY z(e?cCn)Y6gbd6Fri1JBwDPHQ@Dda3X$qJDExmc(Qy7DSX<@NcmYB>;Q2!`v+;5I(v zjh_Qa)hU`(Y!w#F09FRdasz_9K!)jSqpftc0dA`w@<6Yqzj;-zW5vmG-(0}SZ|D^Mbuks-%nMM1JZP{XY(O=ByzsT|CW z10$AzG~*I~!8>d+{P6v`=iJJwf+JyDAPoz#rPwiPE?WmmB3Wy4gO{MOZ4l{_maBXv zBZeA~8rha=HCAHqYa?4(i+~M6ro^2Cr@;OJP~T|)d;v579>C+kSw7;az~1DZV82GSF+ z!!);T@J7?FcGzx2V*$$48Re`1Z|-3~cMrR7yn#m(FjZfKxy92^)U$R}X~w)9`#4y2 z$t2eGf75GDht=!qc%~R06yQI6=@Gu>WWukzb-Gvq*7rO&FLJUoDFR)|=Yl(AX(?2~ zrHivG!(c-%KZfEZ`v0cnftxCRJTlMUf{?;6MA-2}-zh>%ihJMaYM4g;$yKz`ZyBl+ zB+2NOL_g&{H{9o%J~P8{vlxEr;U5317cWq60H@|SRHGRVl7d4U0&0fPiNbN%VuBgA zP0-^+GL?eewd3-qU&op6VUxlAuoTo5-S)B`4j1VlPwcEuomKrXoD@Iauk_BkE|*hKz4CRNvzvk?*S z8w7QFO_hklRmM>ySC7<@Vr8A=`C-Q~dt-vOy9A}lvCJMF`FszO(Bwy9S!C5sx zDg)~T^(bsu<}FQTXY#$~CWH1hH(T#NRc=VCz#x268yO$Y*^6EJRD5IV+M=>jM5raM zjU4*vfS!w7mpx7ohwWhQ;%F*e4bUdGMlyQiOmWvqv#Ky6B2erANV0Ip26Rtl`f+ZE z=8E%F)^z{OX)vNn?&lv*esqGaAt&2Z6{A_e2lLdGf1o=zuAD561e2xxv8N|7soXbE zJnf!rz&Kx~s9vjD%&df(!WR}w>x@W_eJTnob#eMN7}SD1y(jQiDjx?3E>c8eQ!E}2biWjubL*{nX3f|kA%;g*)HV-Hj}eplP`NDrXkyL1|jrm_rAjc9B*xWG7jp6E@{_or%r+7*^uWl)TTQkunV7`1?Dgesc?G4R` zN9#gfBAq#Pq*Ka=Ks=ny@(!U)xm~Wa%Gws)!U@)&;W| z%^DhYm$smiI>#Yy4qu-}JaLRp&H^Sl;g$rwgR*Ae2H=;D`6Ksma^b+q6azUv6l@eo z(h1!l26SAOZ7*tgOm4gPt!xQ=tnRp;9q&Io!6)W|KlZst_}P8MSuyNulMss?ccXD( zLlmDST77cIABFze5fy$?k1mHRqX~c=`KCT%@_dk_r-SKRwF)?n5gqVY&fpkW%W5~8#3W-E@2_n?ltO@CvgBjOx9qZz-t7z?85=}jGOMw4Vuc_g?5%` zgNW>LZhl}VOP&C%`V`gZa2^V(U^Z;Sblcu9x%YCaupWmRN5(=DZn_UVa<%(wq1W0k zZ;PO0)e@&VfUj}*=Zm%LXz|>bQUk4&B|@(^8)}nLMf0gxSF`kJ=p2`8$8v0muVHk3 zNbOQQ&WPrzh;E->O5kyhBD55REYhh}_P$<=Qljyv-!q-M80u)vNY>7l^c?qp*Ta@v z?_<0h4@Dmvb>bLYy28w$_cCY6!)<;3%Eq%6_p?FO4_Rr;huwI*Exg|r3Ii+2y~ipT z8&jK@J7Sc+BIk!e{I@>mrgen%__=ht=tI1+UX!?P5brt@K%IgtVxQ(`T+ZG`N>DSg z2+6ZMI6%w5Ig5L?NJC`P)@pkH@b&MF2-}z?!R6j#3yMHP8WU%I-nThB>oq9_R;H-y z+CL>M0GO|Vdi{0_0eBlW0Q?H@{o`+MoXt}!X%x}(tLQ#3Ps)Mj#j@(2A`U;&XENddKh8_hx?Hn1@@e-&q!UV&=tU$IWFAs z?|kYZ{@VR(eDLNDbze8aq4+>cS^#)JhrdIrt^0Lo#jAVPp;Doy(*cpPtupZ09A~FH zJpAxI)Ytcz%91PJisxXEcz_w{AN6ADIm~h+#baXW9q8yZ`Sdh#jQ6-G25Qd{cWEXP z22&$Z=$`fNS!`tIkT1GSNx-@Eo!qUHHlypoUCrw8^TwzsEoq$XOzt$Tr_Wn5n@QEs zC4)RwUTtRdcjNC^Z&bA=F*i57uW9Vpj;KGg(htr3;dY%)il>z1@;Q1n-cu}K=B^Qe zWKm-2r0zA|nGl|3sp|x{Wrj1~q1``~a;7_hTT}>CTz92GN@s9S^+z3QfOuAtTd1kp ztPr5mdqLb(HX;9}>uEd@H)R0GeFBm&bGC!aJ!nbc&_09e;lGX|uc)m8pmhPUR;ou>ggOIp>1a0i^EBoJB4Pew zrgKV+q%Z5-L)F+jH#-)lBDRegk->mZTpq<-8kverldqgV~CWlVOvQC9?c zN@F0wUb{b>Qat)FVy)F&=OUtN?&q5t*V<_)X1!@zVY?H@dhY6|>e`Y|m+=aQJxuS`X zW?4q<2@oc<(s3&6IhZ>``Cv)o*}Cf$?la2G9q_q5>QCQA*%i!_sdWC>c-Z+L!5NQD z1K(ilXZgQvg!iTT{V-RQ>lt5i;}n14;Q{~JXCL5o2W}R_p)Rui*|!0Hery_^QggPZ z);@U4?f9hqtv~}eigm7NkI(nnMBwB1c+$^!qWmIH(5)9%Pn2yOy<*siXBs38m;vn9 z!zK1 zyequS2a-88IyqHlW8xIV*iln!0u)h6!b4l`k^Y#e+-5jA>rDQI0mN_+aUKr6#0Hx`>Z* zaDs{Pb#*4U^JLMnz;sb#qUF>m>rMVb6R;x854{GamIRjZh^o(nu3kMsjxk7u-KmkA zg(t|;Uea{g_7cjkulxKvcc5qi?5VlEHbwh2EyQ^;j+jG=Q`TpzwpUzCRF!>h+OYyQ zdY-%GbZtE5(-sx~#-BqmdS)n79!XI_j1azW+I6d5J0KcR<`WmW)k(>UV3kxKjS%l>xeotnw{RC9dF;K{oPVFqOUZaX@GNyS~>RT zMHFJfqTn>6cJH^wf)x{(YUdi2xEF~wq;O1uqBq`7X~z=Xh}-^CqVXN6CUv#5AVCe& zJz{y?-B1X#%3w%F;LgW7R46tmX%}X~1k6{!v$yAW8UXlwX8`O`>-8EZR1?*{b+L(x zgXZCk34rFADv2~7vUp=yEHcgC=rqN4>F&}aL{lgP*|gX&t!KGhw+Il3sd8ipqF{&u zuMA$hSmaFnX{vPKHOFNd+c%>+17`(x&vE$4dnk__I4h`N8n;L0H*$t9lmtw}Yo{{R zb75!-M^SmYZ_C72D*9orIGZcJ>FWplp-(@=OYS%;z+83aO>X4n6SC+&ioTf@ z2^R0S{X^p(np7T{Zu|CsACuoZcnZXFMw6JDW+z}vLHEp(MA4HJZEJFG2Jy)o)p=2l zuosrP94d61SLuIQ57XcC>IMGI&s^e}+XZLv6=v3oO=T(gMns!C zb&}yDTyA*P#^U&77l>Wz0LSiZLV4*5mp}PB&QMUNig~UwInPsfg*2vXB*`6#_zw1< zrueWv`71$KCi$-XY|rsj3^h7my|Fu^nQR!%lF~}6JAFgxK1w~joH$wP4=uB;{ zN>p#hf96M3PvB4MX#f#DS7e&+q-uz?fzgBNmDpLBJFHmgSY2AI(7PlvC|;P!ldre=bdXbP3obuReWtO)Zfjbdi9N zigQwgI5ok7;LptwPfX5GL%6HmgzZv%o@D>9=AiSmA96V67&{>H;koZ5(pS?Y1+g5h zpS8P*pW4U1sAhB1vEAw1(5U+DFdplf56X;C86af1X~5uQYiVZ`SwGi%v`y>zXInb_ zS8kZ4sUV*t0w|ePb0)O(b~E-*NzNszvScVnLE<^t$u?A$ z$zv||A}ebXq{hUb8j%@d+@DcrXjIX&U~8`Quc-+*>;n^sfvrYU*)tDh=VjK0u2#9;!J2R%A0i2Fsb*Gu@kE}Hc$^;x90ay17 z??3=Q-(vu;;eHLn8A)d#z&yPtU8iPF9nUUtwb8?DHbmN)v0M<{qa-wUi*x`k{)HK^ zLZw7_-P_TX7LFp_+9tQI8z%D{Z@tis>-u-o6vq>+#vqYC;5^)w&~4KR?BoRV&)$DUJUzr@&AAD)(QS^cdzg&9Dm}A zZsE&zhHG!pK#}UO*n)gBPR2Ie{ZE>6`{Ty{z!2y1q$$0UeveF`eVpn$>BJ>IW>aC~ z!*pxA4ZHQ$Y^3Yk#_Kcg@7P94gbr#erR!A(P7OFIhX4HD75da9o%Muv?1V1<^l-YQd-Ew zf}wYwo@t42gZRyj8ijn3)tehr`5s@Dqs>I&5j&=; zi_U{u|9iM0k?Xu#~^9E~O?_$B+WS`RxHuwt@Q36nQXp-3(@x3M$CD6blqVa|;#yiGE+85qU;K z1b*)1J#rSg*jMx^EQ4u_OppDs3OkO7OPw4~7M+yx zqRl3rTvy>i^y^60)nK~Oy;xZFm8Y^rPcx=5q|Qe(vk|JC&JdodmBl#AR1%>C#?03N zdR8<}-px@Q1j|O}m8{pI(B7Gz!?uR?Idv($b7xt4Y8WCmWdyi!Ux}k)wU+cUwWvjh z*MRJeXGKB}STrBhN?OaLw1+-0tUbv~RZMZ#U@o%DO0 z;SyPD2=*kEMS-AP9pAQW4#ZU6dM99h2z>g}ct-*7`K|z{`+1(PV*@o$)Nxm6OP{b; zjDbkWr_VCSF5RNT`U*ulxR-{gKJU z?jU(~Y|7DPwC?|PKT@cg!!e`W*r9yt64TGz!)_|@scuYoO}oj%vD7!S*gGb`k`CX~ z_r;*%9S^;~4FR#V>YbDxDAjZ_z^IZ=uETgumBs9`ehl>v4oGxW!wNXiMJ!1y(Guzv~Lz zD9a}Iy)R$jU;gMGCpQY7nHEoS0Yjwdt}yo7_-K8x5u(QaM$V$I*itKu_n;Nv96P-6 zL!ZOh^$gop%&W|i<__z#X_RAIsfhOO1^0DLfQQGn(sbBqVyD@?CeyY){d7{HF#{|eLYH-NiF&Hlds;xP9{hZodUVxI^AOkO-An7}>TM5oPWxCFMS^>b z0@}}&gcstSo;inIkJ>JLIAk5)o;Rf<@T z$5e~K@}8>vL03OCraj%yIcm=s8dYqFmvpaTN8sj?*D#yYfx1>!eN}2)dH2@ybecI7 z!Q)`1VhNtZs@gGDDS*=+mS?D}1*&g>(va9d1Cyc9beIaHy23m`?@OY#iZrO)HNBxJ zerv5_{4z7t)Xa3#O#3ZGg0%XXiP7>o$WG#e#~iBQG~KJskbtYCNQ*S~e;e=&b7yjthUD7@)Fjt0T6J0DX;`&Q9@?$(GSRhgvCgIijGA8c0B* z{AY^`HwP;=hGme{D5FR_Z8pSNCiX~9t_W3Vuqj-o|2&yrQiLi?JBLnL*EP7N*a&zF z*7u&qdvP$EVIqrL5o&O#4ZW2keE`s|tL#xj!>N5;)o5x_z*!mq#Tf$A4%j~g@Tqqi z0H6OBfa~cnKOAL-W)zRt07|^ooA?=}qss9rU21Ab)>i#H>gC$eCjk{ z@d3N`B8FNWNZooD5Yu2Q;K&`f@bs8d=b# zymy7!fU~k3+z#Fj*o#!LcF1uGHb2rt+{{2QBa*NY6?&ZZtMuM9j>gpM1@D2?t)wPEv3G8n-U28N2C7uo6hz#pJW zDjJY7d6Qqaw|UrGOg>R-=_4z=3?}4H`gbFU{NZbVP!b+k=S`oW~oG=-s^^zTGXHwzha|D20a6kZXjt(C@6C^7vhMf27B zHQ@Wm^>F7&MU<2;l_l$9X-yMcfIRNHhFMaGI7xpwj+WO_)-!aUx25<|vSfutD@fs` z+o~w4j-E3o@_KIYH_CWjpI=Uy3j39b8btP~bRU~31Q!G&>p(hw#{2iNkYKl;RZPwr zp4YLImCdRbr>VCr5t16fK4}iJNE>?)aw7(k?s@+EalM&FZ~B?D6+ou!Y8UK2cdG)2 zbee(>^nZfg1tgEsIEtw^oGzYoGoY{MF*vbe=?tfETq#PMBFebw5NE{}hbZf8h7e~i zNrSwa9z#ptzn3(WlIj1JEN3vec>EeUoui*YN@}JX>9~v{UI}__yl(J8vF+uVC*04x z*4NfXg!h$OASx)Mh**ob!tPTq^cfdZZ+m1Shazd6xcA4aP|P;57TmU!n=Yz%H;-q~ zcpo*2D_qybqJi25g;~n8WeHkrI$?JQFa-x^@%3|J%Eog}=llM-l(M|%0#Pkn-*

KLGHK0^sxh*P&j1|Nd~d=QmGn0g^0Y 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 literal 0 HcmV?d00001 diff --git a/cdn/public/ccr-provider-buttons.js b/cdn/public/ccr-provider-buttons.js new file mode 100644 index 0000000..534070c --- /dev/null +++ b/cdn/public/ccr-provider-buttons.js @@ -0,0 +1,302 @@ +(function () { + "use strict"; + + var VERSION = "0.2.0"; + var STYLE_ID = "ccr-provider-buttons-style"; + var OFFICIAL_CCR_ICON_URL = "https://cdn.ccrdesk.top/ccr-icon.png"; + + var LINK_PARAMS = [ + "name", + "base_url", + "api_key", + "protocol", + "icon", + "source", + "payload", + "usage_url", + "usage_method", + "usage_headers", + "usage_body", + "balance", + "balance_unit", + "subscription", + "subscription_limit", + "subscription_reset", + "subscription_unit", + "subscription_window" + ]; + var ELEMENT_ATTRIBUTES = LINK_PARAMS.concat(["manifest", "models", "fetch_usage", "description", "color", "color_2", "color_3"]); + + var CSS = [ + ".ccr-provider-button{--ccrpb-brand:#17201c;--ccrpb-brand-2:#36624d;--ccrpb-brand-3:#dff8eb;position:relative;isolation:isolate;display:grid;grid-template-columns:132px minmax(0,1fr);align-items:center;min-height:86px;width:100%;max-width:420px;padding:13px 16px 13px 12px;overflow:hidden;border:1px solid color-mix(in srgb,var(--ccrpb-brand-2) 42%,transparent);border-radius:12px;background:radial-gradient(circle at 14% 22%,color-mix(in srgb,var(--ccrpb-brand-3) 34%,transparent) 0,transparent 30%),radial-gradient(circle at 96% 96%,color-mix(in srgb,var(--ccrpb-brand-2) 44%,transparent) 0,transparent 46%),linear-gradient(135deg,var(--ccrpb-brand),color-mix(in srgb,var(--ccrpb-brand-2) 88%,#111 12%));box-shadow:0 10px 26px color-mix(in srgb,var(--ccrpb-brand) 22%,transparent),inset 0 1px 0 rgba(255,255,255,.16);box-sizing:border-box;color:#fff;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif;text-decoration:none;transform:translateY(0);transition:border-color 180ms ease,box-shadow 220ms ease,transform 220ms ease;}", + ".ccr-provider-button::before{content:\"\";position:absolute;inset:-40% auto -40% -70%;z-index:-1;width:58%;background:linear-gradient(90deg,transparent,rgba(255,255,255,.26),transparent);transform:skewX(-18deg);transition:transform 620ms ease;}", + ".ccr-provider-button::after{content:\"\";position:absolute;right:-38px;bottom:-52px;z-index:-2;width:132px;height:132px;border-radius:999px;background:radial-gradient(circle,color-mix(in srgb,var(--ccrpb-brand-3) 38%,transparent),transparent 68%);opacity:.82;transform:scale(.96);transition:opacity 220ms ease,transform 220ms ease;}", + ".ccr-provider-button:hover,.ccr-provider-button:focus-visible{border-color:color-mix(in srgb,var(--ccrpb-brand-3) 68%,rgba(255,255,255,.34));box-shadow:0 16px 34px color-mix(in srgb,var(--ccrpb-brand) 30%,transparent),0 0 0 1px color-mix(in srgb,var(--ccrpb-brand-3) 26%,transparent),inset 0 1px 0 rgba(255,255,255,.22);color:#fff;text-decoration:none;transform:translateY(-3px);}", + ".ccr-provider-button:hover::before,.ccr-provider-button:focus-visible::before{transform:translateX(320%) skewX(-18deg);}", + ".ccr-provider-button:hover::after,.ccr-provider-button:focus-visible::after{opacity:1;transform:scale(1.08);}", + ".ccr-provider-button:active{transform:translateY(-1px) scale(.992);}", + ".ccrpb-provider-icon{position:relative;z-index:1;grid-column:1;grid-row:1;justify-self:start;display:grid;place-items:center;width:48px;height:48px;overflow:hidden;border:1px solid rgba(255,255,255,.46);border-radius:12px;background:linear-gradient(135deg,color-mix(in srgb,var(--ccrpb-brand-3) 34%,rgba(255,255,255,.9)),color-mix(in srgb,var(--ccrpb-brand-2) 64%,#111 36%));box-shadow:0 8px 18px rgba(0,0,0,.14),inset 0 1px 0 rgba(255,255,255,.42);color:#fff;font-size:15px;font-weight:820;letter-spacing:0;line-height:1;text-shadow:0 1px 2px rgba(0,0,0,.34);transform:rotate(0) scale(1);transition:border-color 180ms ease,box-shadow 220ms ease,transform 220ms ease;}", + ".ccrpb-provider-icon img{box-sizing:border-box;display:block;width:100%;height:100%;background:color-mix(in srgb,#fff 84%,var(--ccrpb-brand-3));object-fit:contain;filter:drop-shadow(0 1px 1px rgba(0,0,0,.12));}", + ".ccrpb-provider-mark{display:block;max-width:42px;overflow:hidden;text-align:center;text-overflow:ellipsis;white-space:nowrap;}", + ".ccrpb-flow{display:inline-flex;grid-column:1;grid-row:1;align-items:center;justify-self:end;justify-content:flex-end;gap:8px;width:72px;height:48px;padding:0;border:0;background:transparent;color:rgba(255,255,255,.86);font-size:14px;font-weight:760;line-height:1;box-shadow:none;transform:translateX(0);transition:transform 220ms ease;}", + ".ccrpb-ccr-icon{box-sizing:border-box;display:grid;flex:0 0 auto;place-items:center;width:48px;height:48px;overflow:hidden;border:0;border-radius:12px;background:transparent;box-shadow:none;transition:transform 220ms ease;}", + ".ccrpb-ccr-icon img{box-sizing:border-box;display:block;width:48px;height:48px;border-radius:12px;}", + ".ccrpb-arrow{display:inline-block;order:-1;line-height:1;transform:translateX(0);transition:transform 220ms ease;}", + ".ccrpb-copy{display:grid;min-width:0;gap:2px;padding:0 0 0 6px;}", + ".ccrpb-name{display:block;overflow:hidden;color:#fff;font-size:15px;font-weight:780;line-height:1.25;text-overflow:ellipsis;white-space:nowrap;}", + ".ccrpb-description{display:block;overflow:hidden;color:rgba(255,255,255,.74);font-size:11px;font-weight:650;letter-spacing:0;line-height:1.35;text-overflow:ellipsis;white-space:nowrap;}", + ".ccr-provider-button:hover .ccrpb-provider-icon,.ccr-provider-button:focus-visible .ccrpb-provider-icon{border-color:color-mix(in srgb,var(--ccrpb-brand-3) 74%,rgba(255,255,255,.54));box-shadow:0 12px 24px rgba(0,0,0,.18),0 0 0 4px color-mix(in srgb,var(--ccrpb-brand-3) 16%,transparent),inset 0 1px 0 rgba(255,255,255,.52);transform:rotate(-3deg) scale(1.06);}", + ".ccr-provider-button:hover .ccrpb-flow,.ccr-provider-button:focus-visible .ccrpb-flow{transform:translateX(3px);}", + ".ccr-provider-button:hover .ccrpb-ccr-icon,.ccr-provider-button:focus-visible .ccrpb-ccr-icon{transform:scale(1.04);}", + ".ccr-provider-button:hover .ccrpb-arrow,.ccr-provider-button:focus-visible .ccrpb-arrow{transform:translateX(2px);}", + "@media (max-width:520px){.ccr-provider-button{grid-template-columns:118px minmax(0,1fr);min-height:92px;padding:12px}.ccrpb-provider-icon,.ccrpb-provider-icon img,.ccrpb-flow,.ccrpb-ccr-icon,.ccrpb-ccr-icon img{width:44px;height:44px}.ccrpb-provider-icon img,.ccrpb-ccr-icon,.ccrpb-ccr-icon img{border-radius:11px}.ccrpb-flow{width:66px;justify-self:end}}", + "@media (prefers-reduced-motion:reduce){.ccr-provider-button,.ccr-provider-button::before,.ccr-provider-button::after,.ccrpb-provider-icon,.ccrpb-flow,.ccrpb-ccr-icon,.ccrpb-arrow{transition:none}.ccr-provider-button:hover,.ccr-provider-button:focus-visible,.ccr-provider-button:hover .ccrpb-provider-icon,.ccr-provider-button:focus-visible .ccrpb-provider-icon,.ccr-provider-button:hover .ccrpb-flow,.ccr-provider-button:focus-visible .ccrpb-flow,.ccr-provider-button:hover .ccrpb-ccr-icon,.ccr-provider-button:focus-visible .ccrpb-ccr-icon,.ccr-provider-button:hover .ccrpb-arrow,.ccr-provider-button:focus-visible .ccrpb-arrow{transform:none}.ccr-provider-button::before{display:none}}" + ].join(""); + + function injectStyles() { + if (document.getElementById(STYLE_ID)) { + return; + } + var style = document.createElement("style"); + style.id = STYLE_ID; + style.textContent = CSS; + document.head.appendChild(style); + } + + function isPresent(value) { + return value !== undefined && value !== null && String(value).trim() !== ""; + } + + function stringifyValue(value) { + if (value === true) { + return "1"; + } + if (value === false) { + return "0"; + } + if (typeof value === "object") { + return JSON.stringify(value); + } + return String(value); + } + + function appendParam(params, key, value) { + if (!isPresent(value)) { + return; + } + if (Array.isArray(value)) { + value.forEach(function (item) { + if (isPresent(item)) { + params.append(key, stringifyValue(item)); + } + }); + return; + } + params.append(key, stringifyValue(value)); + } + + function providerUrl(options) { + options = options || {}; + var params = new URLSearchParams(); + if (isPresent(options.manifest)) { + params.set("manifest", stringifyValue(options.manifest)); + return "ccr://provider?" + params.toString(); + } + LINK_PARAMS.forEach(function (key) { + appendParam(params, key, options[key]); + }); + appendParam(params, "models", options.models); + appendParam(params, "fetch_usage", options.fetch_usage); + return "ccr://provider?" + params.toString(); + } + + function createImage(src, alt) { + var img = document.createElement("img"); + img.src = src; + img.alt = alt || ""; + img.loading = "lazy"; + img.decoding = "async"; + return img; + } + + function providerInitials(name) { + var words = String(name || "") + .replace(/[^A-Za-z0-9\u4e00-\u9fff]+/g, " ") + .trim() + .split(/\s+/) + .filter(Boolean); + if (words.length === 0) { + return "AI"; + } + if (words.length === 1) { + return words[0].slice(0, 2).toUpperCase(); + } + return (words[0].slice(0, 1) + words[1].slice(0, 1)).toUpperCase(); + } + + function protocolDescription(options) { + if (options.description) { + return options.description; + } + if (options.manifest) { + return "Remote provider manifest"; + } + if (options.protocol) { + return options.protocol.replace(/_/g, " "); + } + return "Import provider to CCR"; + } + + function providerName(options) { + return options.name || (options.manifest ? "Provider manifest" : "Provider"); + } + + function providerId(options) { + return providerName(options).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "provider"; + } + + function applyColors(element, options) { + element.style.setProperty("--ccrpb-brand", options.color || "#17201c"); + element.style.setProperty("--ccrpb-brand-2", options.color_2 || "#36624d"); + element.style.setProperty("--ccrpb-brand-3", options.color_3 || "#dff8eb"); + } + + function createProviderIcon(options) { + var shell = document.createElement("span"); + shell.className = "ccrpb-provider-icon"; + if (options.icon) { + shell.appendChild(createImage(options.icon, providerName(options))); + return shell; + } + var mark = document.createElement("span"); + mark.className = "ccrpb-provider-mark"; + mark.textContent = providerInitials(providerName(options)); + shell.appendChild(mark); + return shell; + } + + function createButton(options) { + options = options || {}; + var button = document.createElement("a"); + button.className = "ccr-provider-button ccr-provider-" + providerId(options); + button.href = providerUrl(options); + button.dataset.ccrProviderName = providerName(options); + button.setAttribute("aria-label", "Import " + providerName(options) + " provider to CCR"); + applyColors(button, options); + + var providerIcon = createProviderIcon(options); + + var flow = document.createElement("span"); + flow.className = "ccrpb-flow"; + + var ccrIconShell = document.createElement("span"); + ccrIconShell.className = "ccrpb-ccr-icon"; + ccrIconShell.appendChild(createImage(OFFICIAL_CCR_ICON_URL, "CCR")); + + var arrow = document.createElement("span"); + arrow.className = "ccrpb-arrow"; + arrow.setAttribute("aria-hidden", "true"); + arrow.textContent = "→"; + + flow.appendChild(ccrIconShell); + flow.appendChild(arrow); + + var copy = document.createElement("span"); + copy.className = "ccrpb-copy"; + + var name = document.createElement("span"); + name.className = "ccrpb-name"; + name.textContent = providerName(options); + + var description = document.createElement("span"); + description.className = "ccrpb-description"; + description.textContent = protocolDescription(options); + + copy.appendChild(name); + copy.appendChild(description); + + button.appendChild(providerIcon); + button.appendChild(flow); + button.appendChild(copy); + return button; + } + + function render(target, options) { + options = options || {}; + var root = typeof target === "string" ? document.querySelector(target) : target; + if (!root) { + throw new Error("CCRProviderButtons.render target not found"); + } + injectStyles(); + var button = createButton(options); + if (options.clear !== false) { + root.textContent = ""; + } + root.appendChild(button); + return { + element: button, + href: button.href, + destroy: function () { + if (button.parentNode) { + button.parentNode.removeChild(button); + } + } + }; + } + + function attributeOptions(element) { + var options = {}; + ELEMENT_ATTRIBUTES.forEach(function (name) { + if (!element.hasAttribute(name)) { + return; + } + var value = element.getAttribute(name); + options[name] = name === "fetch_usage" && value === "" ? true : value; + }); + return options; + } + + function defineElement(name) { + if (!("customElements" in window) || customElements.get(name)) { + return; + } + customElements.define(name, class extends HTMLElement { + static get observedAttributes() { + return ELEMENT_ATTRIBUTES; + } + connectedCallback() { + this.render(); + } + attributeChangedCallback() { + if (this.isConnected) { + this.render(); + } + } + render() { + render(this, attributeOptions(this)); + } + }); + } + + function defineElements() { + defineElement("ccr-provider-button"); + defineElement("ccr-provider-buttons"); + } + + window.CCRProviderButtons = { + version: VERSION, + render: render, + renderButton: render, + createButton: function (options) { + injectStyles(); + return createButton(options || {}); + }, + createProviderUrl: providerUrl + }; + + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", defineElements, { once: true }); + } else { + defineElements(); + } +}()); diff --git a/cdn/wrangler.toml b/cdn/wrangler.toml new file mode 100644 index 0000000..3baae4b --- /dev/null +++ b/cdn/wrangler.toml @@ -0,0 +1,3 @@ +name = "claude-code-router-cdn" +compatibility_date = "2026-06-28" +pages_build_output_dir = "./public" diff --git a/docs/README.md b/docs/README.md index 62ddfbd..5e7095a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -18,10 +18,10 @@ The local development server runs from this `docs` directory. Docs are deployed from `.github/workflows/docs.yml` on pushes to `main` that change `docs/**` or the workflow itself. The default public URL is: ```text -https://musistudio.github.io/claude-code-router/ +https://ccrdesk.top/ ``` -The Astro build reads `ASTRO_SITE` and `ASTRO_BASE`, defaulting to `https://musistudio.github.io` and `/claude-code-router`. +The Astro build reads `ASTRO_SITE` and `ASTRO_BASE`, defaulting to `https://ccrdesk.top` and `/`. ## Content diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs index becf982..e14bd3b 100644 --- a/docs/astro.config.mjs +++ b/docs/astro.config.mjs @@ -1,7 +1,7 @@ import { defineConfig } from "astro/config"; -const site = process.env.ASTRO_SITE ?? "https://musistudio.github.io"; -const base = process.env.ASTRO_BASE ?? "/claude-code-router"; +const site = process.env.ASTRO_SITE ?? "https://ccrdesk.top"; +const base = process.env.ASTRO_BASE ?? "/"; export default defineConfig({ site, @@ -9,7 +9,11 @@ export default defineConfig({ output: "static", markdown: { shikiConfig: { - theme: "github-light", + themes: { + light: "github-light", + dark: "github-dark", + }, + defaultColor: false, }, }, }); diff --git a/docs/public/CNAME b/docs/public/CNAME new file mode 100644 index 0000000..c47ccbf --- /dev/null +++ b/docs/public/CNAME @@ -0,0 +1 @@ +ccrdesk.top diff --git a/docs/public/ccr-icon.png b/docs/public/ccr-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..42cdfc1bddbed24b569abd85b06996326c1482a5 GIT binary patch literal 779110 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
l@Xwy8? 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

FJbe469l~h3*ny4>c0A(j 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 z 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%0AKk8Wt{DK4H)&SL4K&x4d*Y}0K; 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

KLGHK0^sxh*P&j1|Nd~d=QmGn0g^0Y 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 literal 0 HcmV?d00001 diff --git a/docs/public/provider-icons/anthropic.png b/docs/public/provider-icons/anthropic.png new file mode 100644 index 0000000000000000000000000000000000000000..c884fe132f3b1ee440c476a283753f402cc59c12 GIT binary patch literal 1588 zcmV-42Fv-0P)Z zZE#d)6~}+)xx0j<37Fu}(iWWp$u0y%PT(r2c6Wu%;aSgunGfB6o@36EZM#H_+ejI!frOH zoKN>T_niOv-RC*az0VcOSWiofyJpe&@~UL|A4`80pDY(YS2N`TYU(Z@#Bus3=2>S? z?F}p~7Ss30HemXnGgJcO7-Hxyu`=KMKe zEH@TmTW{D`eO;h(Z}#f)4F&j8__Z{u2MSytaZgxVuc;jl8(?vUgT!==!BAlLU?|Wt z81jF6&Hw;ra=cSuC|^+Y`|9&q`2ypKI=}^Bg#NRp8kOokwUy@w>Kt|Zyp7ots*>UXRQ1BbKJI2HScfr%g z>m$*}rx(8tAZ@w72b{<|l&@5%-7^pjtgG*c_bD9Ai>mynH|(2-uoS?EzBAO?RN;vI zB5-qoY=5fK*(#vL0A#vD!(lTx(HBBwAKB&trVwefjVo%|sX?GRo4j425oiVWfi431 zZh_o?4d%1;kt4muiWddQS)$FqUK|dou(|;66%|g4%8sn#9fdxD%?NLx+;fe%$hR6g zqtBJ9F(UxLP-|0#UA6Fsf`+)drVdw^PJ#2lZAGNJJ&~w?y7&F?gY(nL%H=0FuBZQ7)-r zRM1gXGp6EPLOCaDK0tI8&^DWyD6nCK9+0tR?NDWN8SqQM3xeFK=%*;%FMl{+Inq=X zU?BL_8cbzKlpl~;nh6mOsj^j|s(6+jOIK>Znz&Mdx#=!&?iTnw%6edaHt4cZ*Xoi0 z>hoEH%VaCaKHwGrf!D-gmt%MySOt0v4M66$d=FWIyDjM?4U-fiivcFs4e&7VcVU%YOo<#W$U=+>(7X&^d$XhN;HZI$L zWTe!CUbjaF+M2f`=CAqU%5N=~toC+x{~|Hbzav}x0xoBZIJyD=M_vLsn4P7M8F(7> zn4)V_w)5t|Zr>fV0#Fc9c_Op6auJmWJYBJkE83%H2Oe&|ON1^!1$ofZ9zUldUH~I9 zY>d5^Oar2@AD6bO@+h!eEOT-ww5hQyfPkvXCWLKnOBy|0(dQv^>=uVO;6^#n5Q)B$ zMP3Wwq#3F2i2n=NnU$&&Tb}ZE#exXifI0{LQ?X-v*-W?xH0RpvoKP4*s$EDMPM}Zbn^34ySFU%L}Kr%POejx>;5Zd1V{t} z_lnAntl*cukHz25-BU}l05V*dv!XqE)-f00D|&QJ0BXby42W?y zdLprRuf3VwCBpFZJa44C8|86?_bkKNnQF}XRXp7iA^$nxR+MKwU9oK+R;O$alr-fC mV1vB=p*8+rEB2=|SNk8HPEoIj_#xZ?0000ScE&+icrFA$AJ!157;$qJFlU{3EK zE~nUzV~CZ5xOYe{))4D8Og4ft1}ZWLsfon`xW0iHk0Is`;U(jrN(fuPj^`*&uaOK# zh<}tA*b`)DOQ59IPX|Ta&YCClI_yE&4EDT4r=-vKo>IVc2A_4jDQn$5a4nz{>P%SI zB=A=bF~3Vt$7B=smOyJx>0t@~<`zD+MWyE){6PXk9l`sHy-eVLoxo9wIo5%6Wvbq7wD&{dex9z${^TJ)7|9bZ=mU?uiw`Gk!WzOEt`wA685CpZzo(ew{gBr+Q ZO?E$f?FMMs#H8&81X|{K)$G6i+h4J#i~s-t literal 0 HcmV?d00001 diff --git a/docs/public/provider-icons/deepseek.ico b/docs/public/provider-icons/deepseek.ico new file mode 100644 index 0000000000000000000000000000000000000000..8e78c0b7260f0010d3d1635174c1f5199113fc6f GIT binary patch literal 209762 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 BFk=7! literal 0 HcmV?d00001 diff --git a/docs/public/provider-icons/gemini.svg b/docs/public/provider-icons/gemini.svg new file mode 100644 index 0000000..38f5625 --- /dev/null +++ b/docs/public/provider-icons/gemini.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/public/provider-icons/mistral.webp b/docs/public/provider-icons/mistral.webp new file mode 100644 index 0000000000000000000000000000000000000000..74e3817e3b7f3ec727bf5736136bbe1a512ff52a GIT binary patch literal 1214 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 G^!*n?&1#?k literal 0 HcmV?d00001 diff --git a/docs/public/provider-icons/openai.png b/docs/public/provider-icons/openai.png new file mode 100644 index 0000000000000000000000000000000000000000..16896d237ad8005795df81de450e3c5261aa2ffc GIT binary patch literal 6578 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 literal 0 HcmV?d00001 diff --git a/docs/public/provider-icons/openrouter.ico b/docs/public/provider-icons/openrouter.ico new file mode 100644 index 0000000000000000000000000000000000000000..5d47b23fc647bc35c44599d1e90ffb4d913d626b GIT binary patch literal 15406 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 + + + + + + + + + + + + + + + + + + + + diff --git a/docs/public/provider-icons/zai-global-general.svg b/docs/public/provider-icons/zai-global-general.svg new file mode 100644 index 0000000..4f511bd --- /dev/null +++ b/docs/public/provider-icons/zai-global-general.svg @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/public/provider-icons/zhipu-cn-coding.png b/docs/public/provider-icons/zhipu-cn-coding.png new file mode 100644 index 0000000000000000000000000000000000000000..0cc7d3625446818b0e67e6515fb57fedf8863df3 GIT binary patch literal 1099 zcmV-R1ho5!P)o|5t>;Ku@|J2$4y21Z!h5t-#|5$7P zMQZ=3wEvo^|8|Q1XM+D;c>h;(|2}5_6HEUaPyXTK|CgixjG+H}j{k0c|6zXrQ)&M* zVgEK@{~%HRz{UTqxc`)*|A(9ZRc-${WB;+d|6O(d+TBG{0000CbW%=J{s3=a-_PIQ z&(CmR^;ZGb0007#Nkl31~6Dj*(k~|EwThSD zjU+Qn{C@uz_(Vc5+tiOT`MY#4nzY+%s4(2_kKptkmqI~V(^*BYj#E%!)a-WPP{0{x z1sa!^b(}&@twu`iXNMbr@mXD1v|$ zf&Qk+$y95q`o9D_jT*(Gu7X4}`tepg9v>8C{GzI=5935f*Hu*mi7L)2m7S|<$Po-? zHLclHr&kACtq#W7EaXh#;^M+(bkV?$td@CMuT&~yP0PwM_^9G+i#X_ns6?K)%;Q*= zgFy#u`vgAneu5+tiIFGda8&mbCbt+nLU2wM@26{wsVCIkv-YQp7)S8{jVP=^m+pUCv2c`?#s=9e|&g7M4q08XPB9(ljBHW z9Wy>6J{aw`(RCs$$44;|G2>-e*szRqd|PRTEf#_xfFLb`ZE<;SUo|5t>;Ku@|J2$4y21Z!h5t-#|5$7P zMQZ=3wEvo^|8|Q1XM+D;c>h;(|2}5_6HEUaPyXTK|CgixjG+H}j{k0c|6zXrQ)&M* zVgEK@{~%HRz{UTqxc`)*|A(9ZRc-${WB;+d|6O(d+TBG{0000CbW%=J{s3=a-_PIQ z&(CmR^;ZGb0007#Nkl31~6Dj*(k~|EwThSD zjU+Qn{C@uz_(Vc5+tiOT`MY#4nzY+%s4(2_kKptkmqI~V(^*BYj#E%!)a-WPP{0{x z1sa!^b(}&@twu`iXNMbr@mXD1v|$ zf&Qk+$y95q`o9D_jT*(Gu7X4}`tepg9v>8C{GzI=5935f*Hu*mi7L)2m7S|<$Po-? zHLclHr&kACtq#W7EaXh#;^M+(bkV?$td@CMuT&~yP0PwM_^9G+i#X_ns6?K)%;Q*= zgFy#u`vgAneu5+tiIFGda8&mbCbt+nLU2wM@26{wsVCIkv-YQp7)S8{jVP=^m+pUCv2c`?#s=9e|&g7M4q08XPB9(ljBHW z9Wy>6J{aw`(RCs$$44;|G2>-e*szRqd|PRTEf#_xfFLb`ZE<;SU]` | +| `ctx.paths.configDir` | CCR config directory | +| `ctx.paths.dataDir` | CCR data directory | +| `ctx.paths.pluginDataDir` | Private data directory for this plugin | +| `ctx.registerGatewayRoute(route)` | Register a local HTTP route on the CCR gateway | +| `ctx.registerHttpBackend(backend)` | Start a local HTTP backend and return `{ url, host, port }` | +| `ctx.registerProxyRoute(route)` | Route proxy-captured host/path traffic to a plugin backend or another upstream | +| `ctx.registerApp(app)` | Add an entry to the built-in browser app list | +| `ctx.openSqliteStore(options)` | Open a SQLite store under the plugin data directory | +| `ctx.registerProviderAccountConnector(connector)` | Register a provider balance or quota connector | +| `ctx.registerCoreGatewayProviderPlugin(plugin)` | Inject a provider plugin into the core gateway | +| `ctx.registerCoreGatewayVirtualModelProfile(profile)` | Inject a virtual model profile into the core gateway | + +Gateway route handlers also receive helper functions: + +| Helper | Description | +| --- | --- | +| `helpers.readBody(request)` | Read request body as a `Buffer` | +| `helpers.readJson(request)` | Read and parse JSON request body | +| `helpers.sendJson(response, statusCode, body)` | Send a JSON response | + +`registerGatewayRoute` defaults to `auth: "gateway"`. If CCR has API keys configured, requests must include `Authorization: Bearer ` or `x-api-key: `. Use `auth: "none"` only for debugging or local public status routes. + +## Create Your First Extension + +Create a directory such as `~/ccr-extensions/hello-extension`: + +```text +hello-extension/ + plugin.json + index.cjs +``` + +`plugin.json` lets CCR's local extension picker discover the extension ID, name, and entrypoint: + +```json +{ + "id": "hello-extension", + "name": "Hello Extension", + "module": "index.cjs", + "apps": [ + { + "id": "hello-status", + "name": "Hello Status", + "url": "http://127.0.0.1:3456/plugins/hello" + } + ] +} +``` + +`index.cjs` registers a status route, an echo backend, and one proxy route: + +```js +"use strict"; + +module.exports = { + async setup(ctx) { + ctx.registerGatewayRoute({ + auth: "none", + id: "hello-status", + method: "GET", + path: "/plugins/hello", + handler(_request, response, helpers) { + helpers.sendJson(response, 200, { + ok: true, + plugin: ctx.pluginId, + message: ctx.pluginConfig?.message || "hello from CCR" + }); + } + }); + + const backend = await ctx.registerHttpBackend({ + id: "hello-echo", + async handler(request, response, helpers) { + const body = request.method === "POST" + ? (await helpers.readBody(request)).toString("utf8") + : ""; + + helpers.sendJson(response, 200, { + method: request.method, + path: request.url, + body + }); + } + }); + + ctx.registerProxyRoute({ + host: "api.example.local", + id: "hello-example-api", + paths: ["/v1"], + preserveHost: true, + upstream: backend.url + }); + + ctx.logger.info(`hello backend listening at ${backend.url}`); + } +}; +``` + +This exposes: + +- `GET /plugins/hello`: a route mounted directly on the CCR gateway to verify that the plugin loaded. +- A local echo backend: CCR assigns a free port automatically. +- A proxy rule: proxy-captured `api.example.local/v1...` traffic is forwarded to the echo backend. + +## Install The Extension + +The recommended flow is through the desktop UI: + +1. Open **Extensions**. +2. Add an extension and choose a local extension directory. +3. Select the `hello-extension` directory. +4. Save the config. +5. Open **Server** and restart the gateway. + +You can also edit the config file directly: + +```json +{ + "plugins": [ + { + "id": "hello-extension", + "enabled": true, + "module": "/Users/you/ccr-extensions/hello-extension/index.cjs", + "config": { + "message": "hello from my config" + } + } + ] +} +``` + +Restart CCR after editing the config file directly. See [Config File Location](/en/configuration/configuration-file/). + +The local directory picker recognizes entry metadata from: + +- `plugin.json` +- `ccr-plugin.json` +- `.ccr-plugin/plugin.json` +- `.codex-plugin/plugin.json` +- `main`, `ccr.module`, or `ccrPlugin.module` in `package.json` + +If no entrypoint is declared, CCR tries `index.cjs`, `index.mjs`, `index.js`, `plugin.cjs`, `plugin.mjs`, or `plugin.js` in the selected directory. + +## Debug Extensions + +### 1. Check syntax first + +For CommonJS extensions: + +```bash +node --check ~/ccr-extensions/hello-extension/index.cjs +``` + +If the extension depends on npm packages, install them in the extension directory and make sure Node can resolve the entrypoint. + +### 2. Start CCR from source + +From the CCR repository root: + +```bash +npm install +npm run dev +``` + +`ctx.logger.info/warn/error` output appears in the terminal that started CCR, with a prefix such as `[plugin:hello-extension]`. + +### 3. Verify the Gateway route + +After the gateway starts, request the status route: + +```bash +curl http://127.0.0.1:3456/plugins/hello +``` + +If the route uses the default `auth: "gateway"` and CCR has API keys configured: + +```bash +curl -H "Authorization: Bearer " http://127.0.0.1:3456/plugins/hello +``` + +You can also use: + +```bash +curl -H "x-api-key: " http://127.0.0.1:3456/plugins/hello +``` + +### 4. Verify the HTTP backend and proxy route + +`registerHttpBackend` returns `backend.url`, which the example writes to logs. Request that URL directly first, then enable proxy mode and verify whether the target host/path hits `registerProxyRoute`. + +Proxy route matching rules: + +- `host` must match the target hostname. Exact host, `.example.com` suffix, and `*.example.com` wildcard patterns are supported. +- Empty `paths` matches all paths for that host. +- When multiple paths match, CCR chooses the longest path prefix. +- `stripPathPrefix` removes the matched prefix from the forwarded path. +- `rewritePathPrefix` replaces the matched prefix with a configured prefix. + +### 5. Common Issues + +| Symptom | What to check | +| --- | --- | +| Extension does not load | Check `plugins[].enabled`, `plugins[].module`, and terminal errors prefixed with `[plugin:]` | +| `GET /plugins/hello` returns 404 | Restart the gateway and confirm `path` or `pathPrefix` starts with `/` | +| Response is 401 | Routes require gateway API key by default; set `auth: "none"` for debug routes | +| Code changes do not apply | Wrapper plugins are not hot reloaded; restart the gateway or CCR | +| Port is already in use | Omit `port` in `registerHttpBackend` so CCR can allocate one automatically | +| Proxy route is not hit | Confirm proxy mode is enabled, the certificate is installed, and host matches the real request hostname | + +## Security Notes + +- Use `auth: "none"` only for status pages, health checks, or local debugging routes. +- Do not log API keys, OAuth tokens, cookies, or complete request headers. +- Prefer `ctx.paths.pluginDataDir` for files written by your extension. +- Validate all external input returned by `readJson`. +- When proxying to external upstreams, handle headers explicitly so local auth material is not forwarded to untrusted services. diff --git a/docs/src/content/docs/en/configuration/provider-deeplink.md b/docs/src/content/docs/en/configuration/provider-deeplink.md new file mode 100644 index 0000000..449133c --- /dev/null +++ b/docs/src/content/docs/en/configuration/provider-deeplink.md @@ -0,0 +1,271 @@ +--- +title: One click import +pageTitle: One click import +eyebrow: Detailed Configuration +lead: Use ccr://provider links to hand provider configuration to CCR, or open the import confirmation page with preset provider buttons. +--- + +## One-Click Import + +The buttons below open the CCR desktop app's provider import confirmation dialog. Preset buttons do not include API keys; custom provider links may include one. Always confirm the provider name, Base URL, protocol, and models before importing a key. + + + +## Embeddable Button Component + +CCR also ships a framework-free button script that providers can embed on their own webpages so users can import that provider into CCR with one click. The script registers Web Components automatically. + +### HTML + +```html + + + +``` + +For larger configs, pass a manifest: + +```html + + + +``` + +### JavaScript + +```html +
+ + +``` + +### Render Parameters + +`CCRProviderButtons.render(target, options)` and `` support the same parameter set. Parameter names match the `ccr://provider` protocol: + +| Parameter | Description | +| --- | --- | +| `name` | Provider display name | +| `base_url` | Provider API Base URL, required for direct imports | +| `api_key` | Optional provider API key | +| `protocol` | Protocol, one of `openai_chat_completions`, `openai_responses`, `anthropic_messages`, `gemini_generate_content` | +| `models` | Model list. Use comma/newline-separated text in HTML, or a string/array in JavaScript | +| `icon` | Provider icon URL | +| `source` | Provider website or config source | +| `manifest` | Remote manifest URL. When present, the button creates a manifest import link | +| `payload` | JSON or base64url JSON config. JavaScript may pass an object | +| `usage_url` | Optional account usage endpoint | +| `fetch_usage` | Whether account usage fetching is enabled | +| `usage_method` | Usage request method, `GET` or `POST` | +| `usage_headers` | Usage request headers. JavaScript may pass an object; HTML must pass a JSON string | +| `usage_body` | Usage request body. JavaScript may pass an object; HTML must pass a JSON string | +| `balance` | Balance field path | +| `balance_unit` | Balance unit | +| `subscription` | Subscription remaining field path | +| `subscription_limit` | Subscription limit field path | +| `subscription_reset` | Subscription reset time field path | +| `subscription_unit` | Subscription unit | +| `subscription_window` | Subscription window, such as `monthly` | + +## URL Format + +CCR supports two URL shapes. The host form is recommended: + +```text +ccr://provider?name=Example%20AI&base_url=https%3A%2F%2Fapi.example.com%2Fv1&protocol=openai_chat_completions&models=example-chat%2Cexample-coder +``` + +The path form is also recognized: + +```text +ccr:///provider?name=Example%20AI&base_url=https%3A%2F%2Fapi.example.com%2Fv1 +``` + +For larger configs, put JSON in `payload`. The value can be URL-encoded JSON or base64url JSON: + +```text +ccr://provider?payload=%7B%22name%22%3A%22Example%20AI%22%2C%22base_url%22%3A%22https%3A%2F%2Fapi.example.com%2Fv1%22%2C%22models%22%3A%5B%22example-chat%22%5D%7D +``` + +## Manifest Import + +Providers can also pass a manifest URL: + +```text +ccr://provider?manifest=https%3A%2F%2Fexample.com%2Fccr-provider.json +``` + +The manifest must use HTTPS, return JSON, avoid local or private network hosts, and stay under 128 KB. CCR fetches the manifest inside the app, shows a confirmation dialog, and writes config only after user approval. + +The manifest can put provider information in a top-level `provider` object: + +| Field | Description | +| --- | --- | +| `provider.name` | Provider display name | +| `provider.base_url` | Provider API Base URL, required | +| `provider.protocol` | Protocol type | +| `provider.models` | Model list as a string array | +| `provider.icon` | Provider icon URL | +| `provider.source` | Provider website or config source | +| `provider.account.enabled` | Whether account usage fetching is enabled | +| `provider.account.refreshIntervalMs` | Usage refresh interval in milliseconds | +| `provider.account.connectors` | Usage connector list | +| `provider.account.connectors[].type` | Connector type, commonly `http-json` | +| `provider.account.connectors[].auth` | Auth mode, commonly `provider-api-key` | +| `provider.account.connectors[].endpoint` | Usage endpoint URL | +| `provider.account.connectors[].method` | Request method, `GET` or `POST` | +| `provider.account.connectors[].headers` | Request headers, without sensitive auth headers | +| `provider.account.connectors[].body` | Optional request body | +| `provider.account.connectors[].mapping.meters` | Usage meter mappings | + +Complete manifest example: + +```json +{ + "provider": { + "name": "Example AI", + "base_url": "https://api.example.com/v1", + "protocol": "openai_chat_completions", + "models": ["example-chat", "example-coder"], + "icon": "https://example.com/icon.png", + "source": "https://example.com", + "account": { + "enabled": true, + "refreshIntervalMs": 300000, + "connectors": [ + { + "type": "http-json", + "auth": "provider-api-key", + "endpoint": "https://api.example.com/v1/account/usage", + "method": "GET", + "headers": { + "accept": "application/json" + }, + "mapping": { + "meters": [ + { + "id": "balance", + "kind": "balance", + "label": "Balance", + "remaining": "data.balance.remaining", + "unit": "USD" + }, + { + "id": "subscription", + "kind": "subscription", + "label": "Monthly quota", + "remaining": "data.quota.remaining", + "limit": "data.quota.limit", + "resetAt": "data.quota.reset_at", + "unit": "tokens", + "window": "monthly" + } + ] + } + } + ] + } + } +} +``` + +## Supported Parameters + +| Parameter | Description | +| --- | --- | +| `name` | Provider display name | +| `base_url` | Provider API Base URL, required | +| `api_key` | Optional provider API key | +| `protocol` | Protocol, one of `openai_chat_completions`, `openai_responses`, `anthropic_messages`, `gemini_generate_content` | +| `models` | Model list, comma-separated, newline-separated, or repeated | +| `icon` | Provider icon URL | +| `source` | Provider website or config source | +| `manifest` | Remote manifest URL | +| `payload` | JSON or base64url JSON config | +| `usage_url` | Optional account usage endpoint | +| `fetch_usage` | Whether account usage fetching is enabled | +| `usage_method` | Usage request method, `GET` or `POST` | +| `usage_headers` | Usage request headers as a JSON string | +| `usage_body` | Usage request body as a JSON string | +| `balance` | Balance field path | +| `balance_unit` | Balance unit | +| `subscription` | Subscription remaining field path | +| `subscription_limit` | Subscription limit field path | +| `subscription_reset` | Subscription reset time field path | +| `subscription_unit` | Subscription unit | +| `subscription_window` | Subscription window, such as `monthly` | + +Parameter names and protocol values must use the exact names above. Aliases such as `baseUrl`, `apiKey`, `model`, `type`, or `openai` are not accepted. diff --git a/docs/src/content/docs/zh/configuration.md b/docs/src/content/docs/zh/configuration.md index e566930..26b0a78 100644 --- a/docs/src/content/docs/zh/configuration.md +++ b/docs/src/content/docs/zh/configuration.md @@ -12,12 +12,15 @@ lead: 深入配置供应商、路由、Agent配置、Fusion、Bot 和配置文 | 页面 | 内容 | | --- | --- | | 供应商配置 | 上游服务、协议、基础 URL、模型列表和凭据 | +| 一键导入供应商 | Provider deeplink 协议、Manifest 导入、一键导入按钮和安全边界 | | 路由配置 | 默认路由、条件规则、fallback 和请求改写 | +| 日志&观测 | 请求日志、Agent 执行追踪、工具调用和工具结果 | | Fusion 组合模型 | 把基础模型与视觉、搜索、MCP 工具组合成新的可选模型 | | Agent配置 | Agent 启动方式、模型、作用范围、多开和 Bot 绑定 | +| 扩展机制 | Wrapper plugin、Core gateway plugin、自定义扩展创建和调试 | | Bot 与 IM 接力 Agent | Bot 转发、接力模式和平台页面 | | 配置文件位置 | 桌面 App 维护的配置文件位置 | ## 内容关系 -供应商和路由决定模型请求的上游去向;Agent配置页面覆盖 Claude Code、Codex 和 ZCode 的启动、多开与模型选择;Fusion 页面覆盖图像、搜索和 MCP 工具;Bot 页面覆盖 IM 平台接力。 +供应商配置和一键导入供应商页面覆盖上游模型服务如何进入 CCR;路由决定模型请求的上游去向;Agent配置页面覆盖 Claude Code、Codex 和 ZCode 的启动、多开与模型选择;Fusion 页面覆盖图像、搜索和 MCP 工具;扩展机制页面覆盖本地插件的创建、安装和调试;Bot 页面覆盖 IM 平台接力。 diff --git a/docs/src/content/docs/zh/configuration/extensions.md b/docs/src/content/docs/zh/configuration/extensions.md new file mode 100644 index 0000000..5fdf852 --- /dev/null +++ b/docs/src/content/docs/zh/configuration/extensions.md @@ -0,0 +1,281 @@ +--- +title: 扩展机制 +pageTitle: 扩展机制 +eyebrow: 详细配置 +lead: 了解 CCR 扩展如何加载、能注册哪些能力,并从零创建、安装和调试自己的扩展。 +--- + +## 扩展是什么 + +CCR 的扩展分为两层: + +| 类型 | 配置位置 | 运行位置 | 适合做什么 | +| --- | --- | --- | --- | +| Wrapper plugin | `plugins` | CCR Desktop 的 Electron wrapper 进程 | 注册本地 HTTP 路由、启动本地后端、拦截代理流量、添加内置浏览器入口、连接 Provider 账号用量 | +| Core gateway plugin | `providerPlugins` 或 `plugins[].coreGateway.providerPlugins` | core gateway runtime | 扩展上游 Provider、认证方式或 core gateway 内部能力 | + +多数用户自定义扩展应从 Wrapper plugin 开始。它能拿到 CCR 配置、私有数据目录和日志对象,并通过 `ctx` 注册能力。 + +## 加载机制 + +启动网关时,CCR 会读取配置里的 `plugins` 数组,并按顺序处理每个 `enabled !== false` 的扩展: + +1. 先应用配置中声明的 `apps`、`proxy.routes`、`coreGateway.providerPlugins`、`coreGateway.virtualModelProfiles` 和 `coreGateway.config`。 +2. 再加载扩展模块。`module` 可以是绝对路径、`~/` 开头路径、相对配置目录的 `./...` 路径,或 Node 可以解析到的包名。 +3. 如果没有配置 `module`,CCR 会尝试用扩展 `id` 匹配内置市场扩展,例如 `claude-design` 和 `cursor-proxy`。 +4. 模块可以导出函数,也可以导出包含 `setup(ctx)` 或 `activate(ctx)` 的对象。 +5. 扩展停止时,CCR 会反向执行 `stop`、`onStop` 钩子,并关闭该扩展注册的 HTTP 后端和 SQLite store。 + +扩展模块常见导出形式: + +```js +"use strict"; + +module.exports = { + async setup(ctx) { + ctx.logger.info("extension loaded"); + }, + async stop() { + // 可选:释放扩展自己持有的资源。 + } +}; +``` + +也可以直接导出函数: + +```js +"use strict"; + +module.exports = async function setup(ctx) { + ctx.logger.info(`loaded ${ctx.pluginId}`); +}; +``` + +`setup(ctx)` 或 `activate(ctx)` 可以直接调用 `ctx.register...` 方法,也可以返回注册对象。返回对象支持 `apps`、`gatewayRoutes`、`proxyRoutes`、`providerAccountConnectors`、`coreGateway`、`virtualModelProfiles`、`stop` 和 `onStop`。 + +## ctx 能力参考 + +`setup(ctx)` 的 `ctx` 包含这些常用字段和方法: + +| 字段或方法 | 说明 | +| --- | --- | +| `ctx.pluginId` | 当前扩展 ID | +| `ctx.pluginConfig` | `plugins[].config` 中的自定义配置 | +| `ctx.config` | 当前 CCR AppConfig 快照 | +| `ctx.logger` | 带 `[plugin:]` 前缀的 `debug/info/warn/error` 日志 | +| `ctx.paths.configDir` | CCR 配置目录 | +| `ctx.paths.dataDir` | CCR 数据目录 | +| `ctx.paths.pluginDataDir` | 当前扩展专属数据目录 | +| `ctx.registerGatewayRoute(route)` | 在 CCR 网关上注册本地 HTTP 路由 | +| `ctx.registerHttpBackend(backend)` | 启动一个本地 HTTP 后端,返回 `{ url, host, port }` | +| `ctx.registerProxyRoute(route)` | 把代理模式捕获到的某个 host/path 转发到扩展后端或其他 upstream | +| `ctx.registerApp(app)` | 在内置浏览器应用列表里添加入口 | +| `ctx.openSqliteStore(options)` | 在扩展数据目录打开 SQLite store | +| `ctx.registerProviderAccountConnector(connector)` | 注册 Provider 账号余额或额度读取器 | +| `ctx.registerCoreGatewayProviderPlugin(plugin)` | 向 core gateway 注入 provider plugin | +| `ctx.registerCoreGatewayVirtualModelProfile(profile)` | 向 core gateway 注入虚拟模型配置 | + +Gateway route handler 会额外收到 helper: + +| Helper | 说明 | +| --- | --- | +| `helpers.readBody(request)` | 读取请求 body,返回 `Buffer` | +| `helpers.readJson(request)` | 读取并解析 JSON body | +| `helpers.sendJson(response, statusCode, body)` | 返回 JSON 响应 | + +`registerGatewayRoute` 默认使用 `auth: "gateway"`。如果 CCR 配置了 API Key,请求必须带 `Authorization: Bearer ` 或 `x-api-key: `。仅调试或本地公开状态页建议使用 `auth: "none"`。 + +## 创建第一个扩展 + +创建一个目录,例如 `~/ccr-extensions/hello-extension`: + +```text +hello-extension/ + plugin.json + index.cjs +``` + +`plugin.json` 用于让 CCR 的本地扩展选择器识别扩展 ID、名称和入口文件: + +```json +{ + "id": "hello-extension", + "name": "Hello Extension", + "module": "index.cjs", + "apps": [ + { + "id": "hello-status", + "name": "Hello Status", + "url": "http://127.0.0.1:3456/plugins/hello" + } + ] +} +``` + +`index.cjs` 注册一个状态路由、一个 echo 后端,以及一个代理转发规则: + +```js +"use strict"; + +module.exports = { + async setup(ctx) { + ctx.registerGatewayRoute({ + auth: "none", + id: "hello-status", + method: "GET", + path: "/plugins/hello", + handler(_request, response, helpers) { + helpers.sendJson(response, 200, { + ok: true, + plugin: ctx.pluginId, + message: ctx.pluginConfig?.message || "hello from CCR" + }); + } + }); + + const backend = await ctx.registerHttpBackend({ + id: "hello-echo", + async handler(request, response, helpers) { + const body = request.method === "POST" + ? (await helpers.readBody(request)).toString("utf8") + : ""; + + helpers.sendJson(response, 200, { + method: request.method, + path: request.url, + body + }); + } + }); + + ctx.registerProxyRoute({ + host: "api.example.local", + id: "hello-example-api", + paths: ["/v1"], + preserveHost: true, + upstream: backend.url + }); + + ctx.logger.info(`hello backend listening at ${backend.url}`); + } +}; +``` + +这个扩展会暴露: + +- `GET /plugins/hello`:直接挂在 CCR 网关上,用来验证扩展是否加载。 +- 一个本地 echo 后端:由 CCR 自动分配端口。 +- 一个代理规则:当代理模式捕获到 `api.example.local/v1...` 时转发到 echo 后端。 + +## 安装扩展 + +推荐使用桌面 UI 安装本地扩展: + +1. 打开 **Extensions** 页面。 +2. 点击添加扩展,选择本地扩展目录。 +3. 选择刚创建的 `hello-extension` 目录。 +4. 保存配置。 +5. 打开 **Server** 页面,重启网关。 + +也可以直接编辑配置文件: + +```json +{ + "plugins": [ + { + "id": "hello-extension", + "enabled": true, + "module": "/Users/you/ccr-extensions/hello-extension/index.cjs", + "config": { + "message": "hello from my config" + } + } + ] +} +``` + +直接编辑配置文件后需要重启 CCR。配置文件位置见 [配置文件位置](/configuration/config-file/)。 + +本地目录选择器会按顺序识别这些入口信息: + +- `plugin.json` +- `ccr-plugin.json` +- `.ccr-plugin/plugin.json` +- `.codex-plugin/plugin.json` +- `package.json` 里的 `main`、`ccr.module` 或 `ccrPlugin.module` + +如果没有显式入口文件,CCR 会尝试目录里的 `index.cjs`、`index.mjs`、`index.js`、`plugin.cjs`、`plugin.mjs` 或 `plugin.js`。 + +## 调试扩展 + +### 1. 先做语法检查 + +CommonJS 扩展可以运行: + +```bash +node --check ~/ccr-extensions/hello-extension/index.cjs +``` + +如果扩展依赖 npm 包,先在扩展目录安装依赖,并确保入口文件能被 Node 解析。 + +### 2. 用源码模式启动 CCR + +在 CCR 仓库根目录运行: + +```bash +npm install +npm run dev +``` + +扩展里的 `ctx.logger.info/warn/error` 会出现在启动 CCR 的终端中,前缀类似 `[plugin:hello-extension]`。 + +### 3. 验证 Gateway route + +启动网关后,请求状态路由: + +```bash +curl http://127.0.0.1:3456/plugins/hello +``` + +如果路由使用默认的 `auth: "gateway"`,并且 CCR 已配置 API Key: + +```bash +curl -H "Authorization: Bearer " http://127.0.0.1:3456/plugins/hello +``` + +也可以使用: + +```bash +curl -H "x-api-key: " http://127.0.0.1:3456/plugins/hello +``` + +### 4. 验证 HTTP 后端和代理规则 + +`registerHttpBackend` 返回的 `backend.url` 会写入日志。先直接请求这个地址,确认后端工作正常;再开启代理模式,验证目标 host/path 是否被 `registerProxyRoute` 命中。 + +代理规则匹配逻辑: + +- `host` 必须匹配目标 hostname,支持精确 host、`.example.com` 后缀和 `*.example.com` 通配。 +- `paths` 为空时匹配该 host 的所有路径。 +- 多个路径匹配时,CCR 会选最长的 path prefix。 +- `stripPathPrefix` 会从转发路径中移除匹配前缀。 +- `rewritePathPrefix` 会把匹配前缀替换成指定前缀。 + +### 5. 常见问题 + +| 现象 | 排查方向 | +| --- | --- | +| 扩展没有加载 | 检查 `plugins[].enabled`、`plugins[].module` 路径和终端里的 `[plugin:]` 报错 | +| `GET /plugins/hello` 返回 404 | 确认网关已重启,路由 `path` 或 `pathPrefix` 是否以 `/` 开头 | +| 返回 401 | 路由默认需要 gateway API Key;调试路由可显式设置 `auth: "none"` | +| 修改代码不生效 | Wrapper plugin 不会热重载,修改后需要重启网关或重启 CCR | +| 端口被占用 | `registerHttpBackend` 不传 `port` 会自动分配端口;固定端口冲突时改回自动分配 | +| 代理规则不命中 | 检查代理模式是否开启、证书是否安装、host 是否匹配真实请求的 hostname | + +## 安全建议 + +- 只有状态页、健康检查或本机调试路由才使用 `auth: "none"`。 +- 不要在日志里打印 API Key、OAuth token、Cookie 或完整请求头。 +- 扩展写入文件时优先使用 `ctx.paths.pluginDataDir`。 +- 对 `readJson` 得到的外部输入做类型校验。 +- 代理转发到外部 upstream 时,明确处理 header 白名单,避免把本地鉴权信息转发到不可信服务。 diff --git a/docs/src/content/docs/zh/configuration/provider-deeplink.md b/docs/src/content/docs/zh/configuration/provider-deeplink.md new file mode 100644 index 0000000..b0e2f37 --- /dev/null +++ b/docs/src/content/docs/zh/configuration/provider-deeplink.md @@ -0,0 +1,271 @@ +--- +title: 一键导入供应商 +pageTitle: 一键导入供应商 +eyebrow: 详细配置 +lead: 使用 ccr://provider 链接把供应商配置带入 CCR,并通过现有 preset 供应商按钮快速打开导入确认页。 +--- + +## 一键导入 + +下面的按钮会打开 CCR 桌面 App 的供应商导入确认页。预设按钮不会携带 API Key;自定义供应商链接可以携带 Key。导入前始终确认供应商名称、Base URL、协议和模型。 + + + +## 嵌入式按钮组件 + +CCR 也提供了一个无框架的按钮脚本,供应商可以嵌入到自己的网页,让用户一键把该供应商导入 CCR。脚本会自动注册 Web Components。 + +### HTML 写法 + +```html + + + +``` + +如果配置较大,可以只传 manifest: + +```html + + + +``` + +### JS 写法 + +```html +
+ + +``` + +### render 参数 + +`CCRProviderButtons.render(target, options)` 和 `` 支持同一组参数,参数名与 `ccr://provider` 协议保持一致: + +| 参数 | 说明 | +| --- | --- | +| `name` | Provider 展示名称 | +| `base_url` | Provider API Base URL,直链导入时必填 | +| `api_key` | 可选 Provider API Key | +| `protocol` | 协议类型,支持 `openai_chat_completions`、`openai_responses`、`anthropic_messages`、`gemini_generate_content` | +| `models` | 模型列表。HTML 中用逗号或换行分隔,JS 中可传字符串或数组 | +| `icon` | Provider 图标 URL | +| `source` | Provider 官网或配置来源 | +| `manifest` | 远程 manifest URL。传入后按钮会生成 manifest 导入链接 | +| `payload` | JSON 或 base64url JSON 配置。JS 中也可以传对象 | +| `usage_url` | 可选账号用量接口 | +| `fetch_usage` | 是否启用账号用量读取 | +| `usage_method` | 用量接口请求方法,`GET` 或 `POST` | +| `usage_headers` | 用量接口请求头。JS 中可传对象,HTML 中传 JSON 字符串 | +| `usage_body` | 用量接口请求体。JS 中可传对象,HTML 中传 JSON 字符串 | +| `balance` | 余额字段路径 | +| `balance_unit` | 余额单位 | +| `subscription` | 订阅剩余额度字段路径 | +| `subscription_limit` | 订阅总额度字段路径 | +| `subscription_reset` | 订阅重置时间字段路径 | +| `subscription_unit` | 订阅额度单位 | +| `subscription_window` | 订阅窗口,例如 `monthly` | + +## 协议格式 + +CCR 支持两种写法,推荐使用 host 写法: + +```text +ccr://provider?name=Example%20AI&base_url=https%3A%2F%2Fapi.example.com%2Fv1&protocol=openai_chat_completions&models=example-chat%2Cexample-coder +``` + +路径写法也可以被识别: + +```text +ccr:///provider?name=Example%20AI&base_url=https%3A%2F%2Fapi.example.com%2Fv1 +``` + +如果配置较大,可以把 JSON 放入 `payload` 参数。值可以是 URL 编码 JSON,也可以是 base64url JSON: + +```text +ccr://provider?payload=%7B%22name%22%3A%22Example%20AI%22%2C%22base_url%22%3A%22https%3A%2F%2Fapi.example.com%2Fv1%22%2C%22models%22%3A%5B%22example-chat%22%5D%7D +``` + +## Manifest 导入 + +供应商也可以只传一个 manifest URL: + +```text +ccr://provider?manifest=https%3A%2F%2Fexample.com%2Fccr-provider.json +``` + +Manifest 必须使用 HTTPS,返回 JSON,不能指向本地或内网地址,体积不能超过 128 KB。CCR 会在 App 内拉取 manifest,展示确认页,然后再写入配置。 + +Manifest 可以把供应商信息放在顶层 `provider` 对象中: + +| 字段 | 说明 | +| --- | --- | +| `provider.name` | Provider 展示名称 | +| `provider.base_url` | Provider API Base URL,必填 | +| `provider.protocol` | 协议类型 | +| `provider.models` | 模型列表,字符串数组 | +| `provider.icon` | Provider 图标 URL | +| `provider.source` | Provider 官网或配置来源 | +| `provider.account.enabled` | 是否启用账号用量读取 | +| `provider.account.refreshIntervalMs` | 用量刷新间隔,单位毫秒 | +| `provider.account.connectors` | 用量读取 connector 列表 | +| `provider.account.connectors[].type` | Connector 类型,常用 `http-json` | +| `provider.account.connectors[].auth` | 认证方式,常用 `provider-api-key` | +| `provider.account.connectors[].endpoint` | 用量接口 URL | +| `provider.account.connectors[].method` | 请求方法,`GET` 或 `POST` | +| `provider.account.connectors[].headers` | 请求头,不能包含敏感认证头 | +| `provider.account.connectors[].body` | 可选请求体 | +| `provider.account.connectors[].mapping.meters` | 用量指标映射列表 | + +完整 manifest 示例: + +```json +{ + "provider": { + "name": "Example AI", + "base_url": "https://api.example.com/v1", + "protocol": "openai_chat_completions", + "models": ["example-chat", "example-coder"], + "icon": "https://example.com/icon.png", + "source": "https://example.com", + "account": { + "enabled": true, + "refreshIntervalMs": 300000, + "connectors": [ + { + "type": "http-json", + "auth": "provider-api-key", + "endpoint": "https://api.example.com/v1/account/usage", + "method": "GET", + "headers": { + "accept": "application/json" + }, + "mapping": { + "meters": [ + { + "id": "balance", + "kind": "balance", + "label": "Balance", + "remaining": "data.balance.remaining", + "unit": "USD" + }, + { + "id": "subscription", + "kind": "subscription", + "label": "Monthly quota", + "remaining": "data.quota.remaining", + "limit": "data.quota.limit", + "resetAt": "data.quota.reset_at", + "unit": "tokens", + "window": "monthly" + } + ] + } + } + ] + } + } +} +``` + +## 支持的参数 + +| 参数 | 说明 | +| --- | --- | +| `name` | Provider 展示名称 | +| `base_url` | Provider API Base URL,必填 | +| `api_key` | 可选 Provider API Key | +| `protocol` | 协议类型,支持 `openai_chat_completions`、`openai_responses`、`anthropic_messages`、`gemini_generate_content` | +| `models` | 模型列表,支持逗号或换行分隔,也可以重复传入 | +| `icon` | Provider 图标 URL | +| `source` | Provider 官网或配置来源 | +| `manifest` | 远程 manifest URL | +| `payload` | JSON 或 base64url JSON 配置 | +| `usage_url` | 可选账号用量接口 | +| `fetch_usage` | 是否启用账号用量读取 | +| `usage_method` | 用量接口请求方法,`GET` 或 `POST` | +| `usage_headers` | 用量接口请求头,JSON 字符串 | +| `usage_body` | 用量接口请求体,JSON 字符串 | +| `balance` | 余额字段路径 | +| `balance_unit` | 余额单位 | +| `subscription` | 订阅剩余额度字段路径 | +| `subscription_limit` | 订阅总额度字段路径 | +| `subscription_reset` | 订阅重置时间字段路径 | +| `subscription_unit` | 订阅额度单位 | +| `subscription_window` | 订阅窗口,例如 `monthly` | + +参数名和协议值必须使用上表中的完整规范名。不再接受 `baseUrl`、`apiKey`、`model`、`type` 或 `openai` 等别名。 diff --git a/docs/src/i18n/content.ts b/docs/src/i18n/content.ts index 8e1e279..5148abf 100644 --- a/docs/src/i18n/content.ts +++ b/docs/src/i18n/content.ts @@ -64,10 +64,12 @@ export const docsContent = { icon: "wand", items: [ "供应商配置", + "一键导入供应商", "路由配置", "日志&观测", "Fusion 组合模型", "Agent配置", + "扩展机制", "Bot 与 IM 接力 Agent", "配置文件位置", ], @@ -91,6 +93,7 @@ export const docsContent = { }, sidebarLinks: { 供应商配置: "/configuration/provider/", + "一键导入供应商": "/configuration/provider-deeplink/", 路由配置: "/configuration/routing/", "日志&观测": "/configuration/observability/", "Fusion 组合模型": "/configuration/fusion/", @@ -98,6 +101,7 @@ export const docsContent = { 内置联网搜索: "/configuration/fusion-web-search/", "自定义 MCP 工具": "/configuration/fusion-mcp-tool/", Agent配置: "/configuration/profile/", + 扩展机制: "/configuration/extensions/", "Bot 与 IM 接力 Agent": "/configuration/bot-relay/", 配置步骤: "/configuration/bot-setup/", Slack: "/bot-与-im-接力-agent/slack/", @@ -200,10 +204,12 @@ export const docsContent = { icon: "wand", items: [ "Provider Config", + "One click import", "Routing Config", "Logs & Observability", "Fusion Models", "Agent Config", + "Extension Mechanism", "Bots And IM Agent Relay", "Config File Location", ], @@ -217,6 +223,7 @@ export const docsContent = { }, sidebarLinks: { "Provider Config": "/en/configuration/providers/", + "One click import": "/en/configuration/provider-deeplink/", "Routing Config": "/en/configuration/routing/", "Logs & Observability": "/en/configuration/observability/", "Fusion Models": "/en/configuration/fusion-models/", @@ -224,6 +231,7 @@ export const docsContent = { "Built-In Web Search": "/en/configuration/fusion-web-search/", "Custom MCP Tool": "/en/configuration/fusion-mcp-tool/", "Agent Config": "/en/configuration/profiles/", + "Extension Mechanism": "/en/configuration/extensions/", "Bots And IM Agent Relay": "/en/configuration/bots/", Setup: "/en/configuration/bot-setup/", Slack: "/en/relay-agents-in-im-with-bots/slack/", diff --git a/docs/src/pages/configuration/[slug].astro b/docs/src/pages/configuration/[slug].astro index 99b34a7..5e056e9 100644 --- a/docs/src/pages/configuration/[slug].astro +++ b/docs/src/pages/configuration/[slug].astro @@ -5,6 +5,7 @@ import { configurationSlugFromPath, zhConfigurationDocs } from "../../configurat export function getStaticPaths() { const activeLabels: Record = { provider: "供应商配置", + "provider-deeplink": "一键导入供应商", routing: "路由配置", profile: "Agent配置", observability: "日志&观测", @@ -12,6 +13,7 @@ export function getStaticPaths() { "fusion-vision": "内置图像能力", "fusion-web-search": "内置联网搜索", "fusion-mcp-tool": "自定义 MCP 工具", + extensions: "扩展机制", "bot-relay": "Bot 与 IM 接力 Agent", "bot-setup": "配置步骤", "config-file": "配置文件位置", diff --git a/docs/src/pages/en/configuration/[slug].astro b/docs/src/pages/en/configuration/[slug].astro index 974c6ab..3c7bd97 100644 --- a/docs/src/pages/en/configuration/[slug].astro +++ b/docs/src/pages/en/configuration/[slug].astro @@ -5,6 +5,7 @@ import { configurationSlugFromPath, enConfigurationDocs } from "../../../configu export function getStaticPaths() { const activeLabels: Record = { providers: "Provider Config", + "provider-deeplink": "One click import", routing: "Routing Config", profiles: "Agent Config", observability: "Logs & Observability", @@ -12,6 +13,7 @@ export function getStaticPaths() { "fusion-vision": "Built-In Vision", "fusion-web-search": "Built-In Web Search", "fusion-mcp-tool": "Custom MCP Tool", + extensions: "Extension Mechanism", bots: "Bots And IM Agent Relay", "bot-setup": "Setup", "configuration-file": "Config File Location", diff --git a/docs/src/styles/global.css b/docs/src/styles/global.css index 10ae1cb..ab21f4e 100644 --- a/docs/src/styles/global.css +++ b/docs/src/styles/global.css @@ -913,8 +913,8 @@ h1 { text-underline-offset: 3px; } -.doc-article code, -.doc-markdown code { +.doc-article :not(pre) > code, +.doc-markdown :not(pre) > code { padding: 2px 6px; border: 1px solid var(--code-border); border-radius: 6px; @@ -1048,6 +1048,292 @@ h1 { color: var(--quote-text); } +.provider-import-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: 14px; + margin: 24px 0 34px; +} + +.doc-markdown a.provider-import-button { + --provider-brand: #17201c; + --provider-brand-2: #36624d; + --provider-brand-3: #dff8eb; + --provider-accent: rgba(255, 255, 255, 0.72); + position: relative; + isolation: isolate; + display: grid; + grid-template-columns: 48px minmax(0, 1fr); + column-gap: 14px; + align-items: center; + min-height: 86px; + padding: 13px 16px 13px 12px; + overflow: hidden; + border: 1px solid color-mix(in srgb, var(--provider-brand-2) 42%, transparent); + border-radius: 12px; + background: + radial-gradient(circle at 14% 22%, color-mix(in srgb, var(--provider-brand-3) 34%, transparent) 0, transparent 30%), + radial-gradient(circle at 96% 96%, color-mix(in srgb, var(--provider-brand-2) 44%, transparent) 0, transparent 46%), + linear-gradient(135deg, var(--provider-brand), color-mix(in srgb, var(--provider-brand-2) 88%, #111 12%)); + box-shadow: + 0 10px 26px color-mix(in srgb, var(--provider-brand) 22%, transparent), + inset 0 1px 0 rgba(255, 255, 255, 0.16); + color: #fff; + text-decoration: none; + text-decoration-thickness: 0; + transform: translateY(0); + transition: + border-color 180ms ease, + box-shadow 220ms ease, + transform 220ms ease; +} + +.doc-markdown a.provider-import-button::before { + content: ""; + position: absolute; + inset: -40% auto -40% -70%; + z-index: -1; + width: 58%; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.26), transparent); + transform: skewX(-18deg); + transition: transform 620ms ease; +} + +.doc-markdown a.provider-import-button::after { + content: ""; + position: absolute; + right: -38px; + bottom: -52px; + z-index: -2; + width: 132px; + height: 132px; + border-radius: 999px; + background: radial-gradient(circle, color-mix(in srgb, var(--provider-brand-3) 38%, transparent), transparent 68%); + opacity: 0.82; + transform: scale(0.96); + transition: + opacity 220ms ease, + transform 220ms ease; +} + +.doc-markdown a.provider-import-button:hover, +.doc-markdown a.provider-import-button:focus-visible { + border-color: color-mix(in srgb, var(--provider-brand-3) 68%, rgba(255, 255, 255, 0.34)); + box-shadow: + 0 16px 34px color-mix(in srgb, var(--provider-brand) 30%, transparent), + 0 0 0 1px color-mix(in srgb, var(--provider-brand-3) 26%, transparent), + inset 0 1px 0 rgba(255, 255, 255, 0.22); + color: #fff; + transform: translateY(-3px); + text-decoration: none; +} + +.doc-markdown a.provider-import-button:hover::before, +.doc-markdown a.provider-import-button:focus-visible::before { + transform: translateX(320%) skewX(-18deg); +} + +.doc-markdown a.provider-import-button:hover::after, +.doc-markdown a.provider-import-button:focus-visible::after { + opacity: 1; + transform: scale(1.08); +} + +.doc-markdown a.provider-import-button:active { + transform: translateY(-1px) scale(0.992); +} + +.provider-import-icon-shell { + position: relative; + z-index: 1; + grid-column: 1; + grid-row: 1; + justify-self: start; + display: grid; + place-items: center; + width: 48px; + height: 48px; + border: 0; + background: transparent; + box-shadow: none; + transform: rotate(0) scale(1); + transition: transform 220ms ease; +} + +.provider-import-icon-shell::before { + content: none; +} + +.provider-import-icon-shell img { + box-sizing: border-box; + display: block; + width: 48px; + height: 48px; + border: 1px solid rgba(255, 255, 255, 0.46); + border-radius: 12px; + background: color-mix(in srgb, #ffffff 84%, var(--provider-brand-3)); + box-shadow: + 0 8px 18px rgba(0, 0, 0, 0.14), + inset 0 1px 0 rgba(255, 255, 255, 0.6); + object-fit: contain; + filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.12)); + transition: + border-color 180ms ease, + box-shadow 220ms ease; +} + +.provider-import-mark { + box-sizing: border-box; + display: grid; + place-items: center; + width: 48px; + height: 48px; + border: 1px solid rgba(255, 255, 255, 0.46); + border-radius: 12px; + background: linear-gradient( + 135deg, + color-mix(in srgb, var(--provider-brand-3) 36%, rgba(255, 255, 255, 0.88)), + color-mix(in srgb, var(--provider-brand-2) 72%, #111111 28%) + ); + box-shadow: + 0 8px 18px rgba(0, 0, 0, 0.14), + inset 0 1px 0 rgba(255, 255, 255, 0.52); + color: #ffffff; + font-size: 14px; + font-weight: 820; + letter-spacing: 0; + line-height: 1; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.34); + transition: + border-color 180ms ease, + box-shadow 220ms ease; +} + +.doc-markdown a.provider-import-button:hover .provider-import-icon-shell, +.doc-markdown a.provider-import-button:focus-visible .provider-import-icon-shell { + transform: rotate(-3deg) scale(1.06); +} + +.doc-markdown a.provider-import-button:hover .provider-import-icon-shell img, +.doc-markdown a.provider-import-button:focus-visible .provider-import-icon-shell img, +.doc-markdown a.provider-import-button:hover .provider-import-mark, +.doc-markdown a.provider-import-button:focus-visible .provider-import-mark { + border-color: color-mix(in srgb, var(--provider-brand-3) 74%, rgba(255, 255, 255, 0.54)); + box-shadow: + 0 12px 24px rgba(0, 0, 0, 0.18), + 0 0 0 4px color-mix(in srgb, var(--provider-brand-3) 16%, transparent), + inset 0 1px 0 rgba(255, 255, 255, 0.72); +} + +.provider-import-copy { + display: grid; + min-width: 0; + gap: 2px; + padding: 0; +} + +.provider-import-name { + display: block; + overflow: hidden; + color: #fff; + font-size: 15px; + font-weight: 780; + line-height: 1.25; + text-overflow: ellipsis; + white-space: nowrap; +} + +.provider-import-meta { + display: block; + overflow: hidden; + color: rgba(255, 255, 255, 0.74); + font-size: 11px; + font-weight: 650; + letter-spacing: 0; + line-height: 1.35; + text-overflow: ellipsis; + white-space: nowrap; +} + +.doc-markdown a.provider-import-button.provider-openai { + --provider-brand: #0b0f0e; + --provider-brand-2: #39413e; + --provider-brand-3: #f2f5f1; +} + +.doc-markdown a.provider-import-button.provider-anthropic { + --provider-brand: #3f2118; + --provider-brand-2: #c56a4b; + --provider-brand-3: #ffd1bf; +} + +.doc-markdown a.provider-import-button.provider-gemini { + --provider-brand: #1a73e8; + --provider-brand-2: #a142f4; + --provider-brand-3: #fbbc04; +} + +.doc-markdown a.provider-import-button.provider-openrouter { + --provider-brand: #111722; + --provider-brand-2: #66758c; + --provider-brand-3: #d9e3f1; +} + +.doc-markdown a.provider-import-button.provider-deepseek { + --provider-brand: #173aa8; + --provider-brand-2: #4e69ff; + --provider-brand-3: #d6deff; +} + +.doc-markdown a.provider-import-button.provider-zhipu-coding { + --provider-brand: #0d4fd7; + --provider-brand-2: #22b8f0; + --provider-brand-3: #d7f2ff; +} + +.doc-markdown a.provider-import-button.provider-zhipu-general { + --provider-brand: #1728a6; + --provider-brand-2: #5f6dff; + --provider-brand-3: #e0e4ff; +} + +.doc-markdown a.provider-import-button.provider-zai-coding { + --provider-brand: #111111; + --provider-brand-2: #49515d; + --provider-brand-3: #f2f5f8; +} + +.doc-markdown a.provider-import-button.provider-zai-general { + --provider-brand: #0d1424; + --provider-brand-2: #3267ac; + --provider-brand-3: #dce8ff; +} + +.doc-markdown a.provider-import-button.provider-mistral { + --provider-brand: #581900; + --provider-brand-2: #ff8a00; + --provider-brand-3: #ffd84f; +} + +.doc-markdown a.provider-import-button.provider-moonshot { + --provider-brand: #10133a; + --provider-brand-2: #7465ff; + --provider-brand-3: #e4ddff; +} + +.doc-markdown a.provider-import-button.provider-bailian { + --provider-brand: #5a2300; + --provider-brand-2: #ff6a00; + --provider-brand-3: #ffd1a3; +} + +.doc-markdown a.provider-import-button.provider-siliconflow { + --provider-brand: #041f1b; + --provider-brand-2: #00c2a8; + --provider-brand-3: #b4fff4; +} + .doc-markdown > pre, .doc-markdown > .code-panel { margin: 22px 0 32px; @@ -1145,23 +1431,44 @@ pre, line-height: 1.7; } +pre.astro-code, +pre.astro-code span { + color: var(--shiki-light, var(--code-text)); +} + +:root[data-theme="dark"] pre.astro-code, :root[data-theme="dark"] pre.astro-code span { - color: var(--code-text) !important; + color: var(--shiki-dark, var(--code-text)); } @media (prefers-color-scheme: dark) { + :root:not([data-theme]) pre.astro-code, :root:not([data-theme]) pre.astro-code span { - color: var(--code-text) !important; + color: var(--shiki-dark, var(--code-text)); } } .code-content code, pre code { + display: block; padding: 0; + border: 0; border-radius: 0; background: transparent; + box-shadow: none; color: inherit; font-size: inherit; + line-height: inherit; + white-space: pre; +} + +.code-content code span, +pre code span { + padding: 0; + border: 0; + border-radius: 0; + background: transparent; + box-shadow: none; } .steps { @@ -1431,3 +1738,46 @@ pre code { } } + +@media (max-width: 520px) { + .provider-import-grid { + grid-template-columns: 1fr; + gap: 12px; + } + + .doc-markdown a.provider-import-button { + grid-template-columns: 44px minmax(0, 1fr); + column-gap: 12px; + min-height: 92px; + padding: 12px; + } + + .provider-import-icon-shell { + width: 44px; + height: 44px; + } + + .provider-import-icon-shell img, + .provider-import-mark { + width: 44px; + height: 44px; + border-radius: 11px; + } + +} + +@media (prefers-reduced-motion: reduce) { + .doc-markdown a.provider-import-button, + .doc-markdown a.provider-import-button::before, + .doc-markdown a.provider-import-button::after, + .provider-import-icon-shell { + transition: none; + } + + .doc-markdown a.provider-import-button:hover, + .doc-markdown a.provider-import-button:focus-visible, + .doc-markdown a.provider-import-button:hover .provider-import-icon-shell, + .doc-markdown a.provider-import-button:focus-visible .provider-import-icon-shell { + transform: none; + } +} diff --git a/src/main/app-menu.ts b/src/main/app-menu.ts index be1c5d7..de2f219 100644 --- a/src/main/app-menu.ts +++ b/src/main/app-menu.ts @@ -1,8 +1,6 @@ import { app, dialog, Menu, type BrowserWindow, type MenuItemConstructorOptions } from "electron"; import { APP_NAME, IPC_CHANNELS } from "./constants"; -import { appUpdateService } from "./update-service"; import windowsManager from "./windows"; -import type { AppUpdateStatus } from "../shared/app"; export function setupApplicationMenu(): void { Menu.setApplicationMenu(Menu.buildFromTemplate(createMenuTemplate())); @@ -133,121 +131,7 @@ function showAboutPanel(): void { function checkForUpdatesFromMenu(): void { const window = windowsManager.showMainWindow(); - void checkForUpdatesWithDialog(window); -} - -async function checkForUpdatesWithDialog(window: BrowserWindow): Promise { - try { - await presentUpdateStatus(window, await appUpdateService.checkForUpdates()); - } catch (error) { - await showUpdateDialog(window, { - detail: formatError(error), - message: "Update check failed.", - type: "error" - }); - } -} - -async function presentUpdateStatus(window: BrowserWindow, status: AppUpdateStatus): Promise { - if (status.state === "not-available") { - await showUpdateDialog(window, { - detail: `Version ${status.currentVersion} is currently installed.`, - message: `${APP_NAME} is up to date.`, - type: "info" - }); - return; - } - - if (status.state === "available") { - const result = await showUpdateDialog(window, { - buttons: ["Download", "Later"], - cancelId: 1, - detail: status.availableVersion - ? `Version ${status.availableVersion} is available.` - : "An update is available.", - message: "Update available.", - type: "info" - }); - if (result.response === 0) { - await downloadUpdateFromMenu(window); - } - return; - } - - if (status.state === "downloaded") { - await promptInstallUpdate(window); - return; - } - - if (status.state === "error") { - await showUpdateDialog(window, { - detail: status.lastError || "Unable to check for updates.", - message: "Update check failed.", - type: "error" - }); - } -} - -async function downloadUpdateFromMenu(window: BrowserWindow): Promise { - try { - const status = await appUpdateService.downloadUpdate(); - if (status.state === "downloaded" || status.canInstall) { - await promptInstallUpdate(window); - return; - } - if (status.state === "error") { - await showUpdateDialog(window, { - detail: status.lastError || "Unable to download the update.", - message: "Update download failed.", - type: "error" - }); - } - } catch (error) { - await showUpdateDialog(window, { - detail: formatError(error), - message: "Update download failed.", - type: "error" - }); - } -} - -async function promptInstallUpdate(window: BrowserWindow): Promise { - const result = await showUpdateDialog(window, { - buttons: ["Install and Restart", "Later"], - cancelId: 1, - detail: "The update has been downloaded and is ready to install.", - message: "Update ready to install.", - type: "info" - }); - - if (result.response !== 0) { - return; - } - - try { - await appUpdateService.installUpdate(); - } catch (error) { - await showUpdateDialog(window, { - detail: formatError(error), - message: "Update install failed.", - type: "error" - }); - } -} - -function showUpdateDialog( - window: BrowserWindow, - options: { buttons?: string[]; cancelId?: number; detail: string; message: string; type: "error" | "info" } -): Promise { - return dialog.showMessageBox(window, { - buttons: options.buttons ?? ["OK"], - cancelId: options.cancelId, - defaultId: 0, - detail: options.detail, - message: options.message, - title: "Software Update", - type: options.type - }); + sendWhenReady(window, IPC_CHANNELS.appOpenUpdate); } function sendWhenReady(window: BrowserWindow, channel: string): void { @@ -268,7 +152,3 @@ function sendWhenReady(window: BrowserWindow, channel: string): void { send(); } - -function formatError(error: unknown): string { - return error instanceof Error ? error.message : String(error); -} diff --git a/src/main/preload.ts b/src/main/preload.ts index 6e6599b..c7d117d 100644 --- a/src/main/preload.ts +++ b/src/main/preload.ts @@ -157,6 +157,11 @@ contextBridge.exposeInMainWorld("ccr", { ipcRenderer.on(IPC_CHANNELS.appOpenSettings, handler); return () => ipcRenderer.removeListener(IPC_CHANNELS.appOpenSettings, handler); }, + onOpenUpdateRequest: (callback: () => void) => { + const handler = () => callback(); + ipcRenderer.on(IPC_CHANNELS.appOpenUpdate, handler); + return () => ipcRenderer.removeListener(IPC_CHANNELS.appOpenUpdate, handler); + }, onUpdateStatusChanged: (callback: (status: AppUpdateStatus) => void) => { const handler = (_event: Electron.IpcRendererEvent, status: AppUpdateStatus) => callback(status); ipcRenderer.on(IPC_CHANNELS.appUpdateStatusChanged, handler); diff --git a/src/renderer/pages/home/App.tsx b/src/renderer/pages/home/App.tsx index e58dfd1..de581e4 100644 --- a/src/renderer/pages/home/App.tsx +++ b/src/renderer/pages/home/App.tsx @@ -1,6 +1,6 @@ import { AddApiKeyDraft, AddProfileDraft, AddProviderDraft, AddRoutingRuleDraft, AgentAnalysisSessionSelection, AgentAnalysisSnapshot, AgentFilterValue, - ApiKeyConfig, AppConfig, appCopy, AppI18nContext, AppInfo, AppSaveConfigOptions, + ApiKeyConfig, AppConfig, appCopy, AppI18nContext, AppInfo, AppSaveConfigOptions, AppUpdateStatus, AppLanguagePreference, applyProviderProbeResult, AppToast, BotGatewaySavedConfig, buildExtensionList, claudeDesignRoutingConfigFromDraft, buildRouterConditionPath, ClaudeDesignRoutingDraft, ClaudeDesignRoutingRuleDraft, cloneConfig, createApiKeyDraft, createApiKeyEditDraft, @@ -11,7 +11,7 @@ import { enforceSingleEnabledGlobalProfilePerAgent, ExtensionConfigTarget, ExtensionDeleteTarget, ExtensionInstallDraft, ExtensionSource, fallbackAgentAnalysis, fallbackConfig, fallbackGatewayStatus, fallbackInfo, fallbackProxyCertificateStatus, fallbackProxyNetworkSnapshot, fallbackProxyStatus, fallbackRequestLogPage, - fallbackUsageStats, formatAppError, formatJson, formatProxyCertificateInstallMessage, GatewayProviderConfig, + fallbackUpdateStatus, fallbackUsageStats, formatAppError, formatJson, formatProxyCertificateInstallMessage, GatewayProviderConfig, fusionCustomMcpServerFromDraft, fusionCustomToolConfigFromProfile, GatewayProviderProbeResult, gatewayServiceMessage, GatewayStatus, getDefaultOnboardingStep, isClaudeDesignPluginConfig, isClaudeDesignRoutingDraftValid, isCursorProxyPluginConfig, isMacPlatform, isPlainRecord, isProfileDraftSubmittable, isProviderNameDuplicate, isProviderProbeCandidateReady, @@ -168,6 +168,10 @@ function App() { const [proxyNetworkSnapshot, setProxyNetworkSnapshot] = useState(fallbackProxyNetworkSnapshot); const [proxyStatus, setProxyStatus] = useState(fallbackProxyStatus); const [actionBusy, setActionBusy] = useState(""); + const [updateActionBusy, setUpdateActionBusy] = useState<"" | "check" | "download" | "install">(""); + const [updateActionError, setUpdateActionError] = useState(""); + const [updateDialogOpen, setUpdateDialogOpen] = useState(false); + const [updateDialogStatus, setUpdateDialogStatus] = useState(fallbackUpdateStatus); const [gatewayActionBusy, setGatewayActionBusy] = useState(false); const [actionMessage, setActionMessage] = useState(""); const [actionError, setActionError] = useState(""); @@ -249,6 +253,7 @@ function App() { const [usageRange, setUsageRange] = useState("7d"); const [usageStats, setUsageStats] = useState(fallbackUsageStats); const [providerAccountSnapshots, setProviderAccountSnapshots] = useState([]); + const updateActionBusyRef = useRef(false); const resolvedLanguage = languagePreference === "system" ? systemLanguage : languagePreference; const copy = appCopy[resolvedLanguage]; const t = useMemo(() => (value: string) => translateText(copy, value), [copy]); @@ -318,6 +323,7 @@ function App() { void window.ccr.getPluginMarketplace().then(setPluginMarketplace).catch(() => setPluginMarketplace([])); void window.ccr.getProxyCertificateStatus().then(setProxyCertificateStatus); const unsubscribeOpenSettings = window.ccr.onOpenSettingsRequest(openSettingsDialog); + const unsubscribeOpenUpdate = window.ccr.onOpenUpdateRequest(openUpdateDialog); const refreshRuntimeStatus = () => { void window.ccr?.getGatewayStatus().then(setGatewayStatus); void window.ccr?.getProxyStatus().then(setProxyStatus); @@ -328,9 +334,40 @@ function App() { return () => { window.clearInterval(timer); unsubscribeOpenSettings(); + unsubscribeOpenUpdate(); }; }, []); + useEffect(() => { + if (!updateDialogOpen || !window.ccr) { + return; + } + + let disposed = false; + void window.ccr.getUpdateStatus() + .then((status) => { + if (!disposed) { + setUpdateDialogStatus(status); + } + }) + .catch(() => { + if (!disposed) { + setUpdateDialogStatus(fallbackUpdateStatus); + } + }); + + const unsubscribe = window.ccr.onUpdateStatusChanged((status) => { + if (!disposed) { + setUpdateDialogStatus(status); + } + }); + + return () => { + disposed = true; + unsubscribe(); + }; + }, [updateDialogOpen]); + useEffect(() => { if (!window.ccr) { return; @@ -844,6 +881,74 @@ function App() { }, 1800); } + function openUpdateDialog() { + setUpdateDialogOpen(true); + setUpdateActionError(""); + void checkForAppUpdate(); + } + + async function checkForAppUpdate() { + if (updateActionBusyRef.current) { + return; + } + if (!window.ccr?.updateCheck) { + setUpdateDialogStatus({ + ...fallbackUpdateStatus, + currentVersion: appInfo.version, + lastError: t("Updates are only available in packaged builds."), + state: "error" + }); + return; + } + + updateActionBusyRef.current = true; + setUpdateActionBusy("check"); + setUpdateActionError(""); + try { + setUpdateDialogStatus(await window.ccr.updateCheck()); + } catch (error) { + setUpdateActionError(formatError(error)); + } finally { + updateActionBusyRef.current = false; + setUpdateActionBusy(""); + } + } + + async function downloadAppUpdate() { + if (updateActionBusyRef.current || !window.ccr?.updateDownload) { + return; + } + + updateActionBusyRef.current = true; + setUpdateActionBusy("download"); + setUpdateActionError(""); + try { + setUpdateDialogStatus(await window.ccr.updateDownload()); + } catch (error) { + setUpdateActionError(formatError(error)); + } finally { + updateActionBusyRef.current = false; + setUpdateActionBusy(""); + } + } + + async function installAppUpdate() { + if (updateActionBusyRef.current || !window.ccr?.updateInstall) { + return; + } + + updateActionBusyRef.current = true; + setUpdateActionBusy("install"); + setUpdateActionError(""); + try { + await window.ccr.updateInstall(); + } catch (error) { + setUpdateActionError(formatError(error)); + updateActionBusyRef.current = false; + setUpdateActionBusy(""); + } + } + function updateConfig(mutator: (config: AppConfig) => AppConfig) { setDraftConfig((current) => { const next = normalizeConfig(mutator(cloneConfig(current))); @@ -2697,6 +2802,7 @@ function App() { isMac={isMac} needsTrafficLightSafeArea={needsTrafficLightSafeArea} networkCaptureEnabled={networkCaptureEnabled} + onCheckUpdate={openUpdateDialog} onOpenSettings={openSettingsDialog} onSelectNavigationItem={selectNavigationItem} onToggleSidebar={() => setSidebarOpen((current) => !current)} @@ -2704,6 +2810,7 @@ function App() { shouldReduceMotion={shouldReduceMotion} sidebarOpen={sidebarOpen} toggleGatewayService={toggleGatewayService} + updateActionBusy={Boolean(updateActionBusy)} visibleNavigation={visibleNavigation} viewProps={{ apiKeys: { @@ -3017,6 +3124,21 @@ function App() { trayIconPreference: draftConfig.trayIcon || "random", trayWidgets: normalizeTrayWidgets(draftConfig.trayWidgets ?? DEFAULT_TRAY_WIDGETS, draftConfig.trayWindowModules, draftConfig.trayComponentVariants) } : undefined} + update={updateDialogOpen ? { + actionBusy: updateActionBusy, + actionError: updateActionError, + copy, + onCheck: checkForAppUpdate, + onClose: () => { + if (updateActionBusy !== "install") { + setUpdateDialogOpen(false); + setUpdateActionError(""); + } + }, + onDownload: downloadAppUpdate, + onInstall: installAppUpdate, + status: updateDialogStatus + } : undefined} virtualModelUpsert={virtualModelDialogOpen ? { canSubmit: canSubmitVirtualModel, draft: virtualModelDraft, diff --git a/src/renderer/pages/home/components/dialog-stack.tsx b/src/renderer/pages/home/components/dialog-stack.tsx index 4424509..b254698 100644 --- a/src/renderer/pages/home/components/dialog-stack.tsx +++ b/src/renderer/pages/home/components/dialog-stack.tsx @@ -6,6 +6,7 @@ import { AddProfileDialog, ProfileOpenDialog } from "./profiles"; import { AddProviderDialog, DeleteProviderDialog, ProviderDeepLinkDialog } from "./providers"; import { AddRoutingRuleDialog, DeleteRoutingRuleDialog } from "./routing"; import { AppSettingsDialog } from "./settings"; +import { UpdateDialog } from "./update"; import { InstallExtensionDialog, VirtualModelDialog } from "./virtual-models"; export function AppDialogStack({ @@ -26,6 +27,7 @@ export function AppDialogStack({ routingDelete, routingUpsert, settings, + update, virtualModelUpsert }: { apiKeyAdd?: ComponentProps; @@ -45,6 +47,7 @@ export function AppDialogStack({ routingDelete?: ComponentProps; routingUpsert?: ComponentProps; settings?: ComponentProps; + update?: ComponentProps; virtualModelUpsert?: ComponentProps; }) { const dialogs = [ @@ -65,7 +68,8 @@ export function AppDialogStack({ extensionSettings ? { key: "extension-settings", node: } : null, claudeDesignConfig ? { key: "extension-config", node: } : null, cursorProxyConfig ? { key: "cursor-proxy-config", node: } : null, - settings ? { key: "settings", node: } : null + settings ? { key: "settings", node: } : null, + update ? { key: "update", node: } : null ].filter((dialog): dialog is { key: string; node: ReactElement } => Boolean(dialog)); return ( diff --git a/src/renderer/pages/home/components/index.ts b/src/renderer/pages/home/components/index.ts index f7816f4..edd7faa 100644 --- a/src/renderer/pages/home/components/index.ts +++ b/src/renderer/pages/home/components/index.ts @@ -3,6 +3,7 @@ export { LightToast } from "./feedback"; export { AppDialogStack } from "./dialog-stack"; export { MainLayout, OnboardingLayout } from "./layout"; export { AppSettingsDialog } from "./settings"; +export { UpdateDialog } from "./update"; export { OverviewView, AgentAnalysisView } from "./dashboard"; export { ApiKeysView, AddApiKeyDialog, EditApiKeyDialog } from "./api-keys"; export { ServerView } from "./server"; diff --git a/src/renderer/pages/home/components/layout.tsx b/src/renderer/pages/home/components/layout.tsx index 319777c..bbc3fea 100644 --- a/src/renderer/pages/home/components/layout.tsx +++ b/src/renderer/pages/home/components/layout.tsx @@ -2,8 +2,8 @@ import type { ComponentProps } from "react"; import { AnimatedIconSwap, AnimatePresence, AppConfig, AppCopy, Button, cn, EndpointTitleBar, GatewayStatus, listSpringTransition, LucideIcon, motion, motionEase, - NavigationId, PanelLeftClose, PanelLeftOpen, - reducedMotionTransition, ServiceControlButton, Settings, ViewId, + LoaderCircle, NavigationId, PanelLeftClose, PanelLeftOpen, + reducedMotionTransition, RefreshCw, ServiceControlButton, Settings, ViewId, ViewMotionShell, viewUsesInternalScroll } from "../shared"; import { ApiKeysView } from "./api-keys"; @@ -63,12 +63,14 @@ export function MainLayout({ needsTrafficLightSafeArea, agentAnalysisEnabled, networkCaptureEnabled, + onCheckUpdate, onOpenSettings, onSelectNavigationItem, onToggleSidebar, shouldReduceMotion, sidebarOpen, toggleGatewayService, + updateActionBusy, viewProps, requestLogsEnabled, visibleNavigation @@ -83,17 +85,21 @@ export function MainLayout({ isMac: boolean; needsTrafficLightSafeArea: boolean; networkCaptureEnabled: boolean; + onCheckUpdate: () => void; onOpenSettings: () => void; onSelectNavigationItem: (id: NavigationId) => void; onToggleSidebar: () => void; shouldReduceMotion: boolean | null; sidebarOpen: boolean; toggleGatewayService: () => void; + updateActionBusy: boolean; viewProps: MainViewProps; requestLogsEnabled: boolean; visibleNavigation: MainNavigationItem[]; }) { const windowControlSafeAreaWidth = isMac ? 152 : 88; + const checkForUpdatesLabel = copy.text["Check for updates"] ?? "Check for updates"; + const checkingForUpdatesLabel = copy.text["Checking for updates"] ?? "Checking for updates"; return ( <> @@ -175,7 +181,20 @@ export function MainLayout({ ))} -
+
+ + + + +
+
+
{updateStateLabel(status, t)}
+
{updateStateDescription(status, t)}
+
+ +
+ +
+ + + + +
+ + {status.state === "downloading" ? ( +
+
+ {t("Downloading update")} + {progressPercent !== undefined ? `${progressPercent.toFixed(0)}%` : ""} +
+
+
+
+
{formatDownloadProgress(status.progress)}
+
+ ) : null} + + {canInstall ? ( +
+ {t("Update ready to install")} +
+ ) : null} + + {!status.supported ? ( +
+ {t("Updates are only available in packaged builds.")} +
+ ) : null} + + {error ? ( +
+ + {error} +
+ ) : null} + + {status.releaseNotes ? ( +
+
{t("Release notes")}
+
{status.releaseNotes}
+
+ ) : null} + + + + + + + + + + + ); +} + +function UpdateInfoRow({ label, value }: { label: string; value: string }) { + return ( +
+
{label}
+
{value}
+
+ ); +} + +function UpdateStateBadge({ label, status }: { label: string; status: AppUpdateStatus }) { + return ( + + {label} + + ); +} + +function updateStateLabel(status: AppUpdateStatus, t: (value: string) => string): string { + if (!status.supported) return t("Not configured"); + if (status.state === "checking") return t("Checking for updates"); + if (status.state === "available") return t("Update available"); + if (status.state === "not-available") return t("No updates available"); + if (status.state === "downloading") return t("Downloading update"); + if (status.state === "downloaded") return t("Update ready to install"); + if (status.state === "installing") return t("Install and restart"); + if (status.state === "error") return t("Update failed"); + return t("Check for updates"); +} + +function updateStateDescription(status: AppUpdateStatus, t: (value: string) => string): string { + if (!status.supported) return t("Updates are only available in packaged builds."); + if (status.state === "available" && status.availableVersion) return `${t("Available version")}: ${status.availableVersion}`; + if (status.state === "downloaded") return t("Update downloaded"); + if (status.state === "not-available") return t("No updates available"); + if (status.state === "downloading") return t("Downloading update"); + return t("Online updates"); +} + +function clampPercent(value: number | undefined): number | undefined { + if (typeof value !== "number" || !Number.isFinite(value)) { + return undefined; + } + return Math.max(0, Math.min(100, value)); +} + +function formatDownloadProgress(progress: AppUpdateStatus["progress"]): string { + if (!progress) { + return ""; + } + const transferred = formatBytes(progress.transferred); + const total = formatBytes(progress.total); + const speed = formatBytes(progress.bytesPerSecond); + return [ + transferred && total ? `${transferred} / ${total}` : transferred || total, + speed ? `${speed}/s` : "" + ].filter(Boolean).join(" | "); +} + +function formatBytes(value: number | undefined): string { + if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) { + return ""; + } + const units = ["B", "KB", "MB", "GB"]; + let current = value; + let unitIndex = 0; + while (current >= 1024 && unitIndex < units.length - 1) { + current /= 1024; + unitIndex += 1; + } + return `${current >= 10 || unitIndex === 0 ? current.toFixed(0) : current.toFixed(1)} ${units[unitIndex]}`; +} + +function formatUpdateDate(value: string | undefined): string { + if (!value) { + return ""; + } + const date = new Date(value); + if (Number.isNaN(date.getTime())) { + return ""; + } + return date.toLocaleString(); +} diff --git a/src/renderer/pages/home/shared/i18n.tsx b/src/renderer/pages/home/shared/i18n.tsx index 494a410..5012f3a 100644 --- a/src/renderer/pages/home/shared/i18n.tsx +++ b/src/renderer/pages/home/shared/i18n.tsx @@ -872,7 +872,6 @@ export const appCopy: Record = { "Provider credential weight must be a positive number.": "供应商凭据权重必须是正数。", "Provider ID": "供应商 ID", "Provider link failed": "供应商链接失败", - "Provider links cannot include API keys. Add the key manually after verifying the endpoint.": "供应商链接不能包含 API 密钥。请核验端点后手动添加密钥。", "Provider middleware": "供应商中间件", "Provider name": "供应商名称", "Provider name and Base URL are required.": "供应商名称和基础 URL 不能为空。", diff --git a/src/renderer/types/electron.d.ts b/src/renderer/types/electron.d.ts index b7f33e4..4935b74 100644 --- a/src/renderer/types/electron.d.ts +++ b/src/renderer/types/electron.d.ts @@ -131,6 +131,7 @@ declare global { waitBotGatewayQrLogin: (request: BotGatewayQrLoginWaitRequest) => Promise; onBeforeQuit: (callback: () => void) => () => void; onOpenSettingsRequest: (callback: () => void) => () => void; + onOpenUpdateRequest: (callback: () => void) => () => void; onProviderDeepLink: (callback: (request: ProviderDeepLinkRequest) => void) => () => void; onUpdateStatusChanged: (callback: (status: AppUpdateStatus) => void) => () => void; }; diff --git a/src/shared/deep-link.ts b/src/shared/deep-link.ts index 2fb8cba..df2cbef 100644 --- a/src/shared/deep-link.ts +++ b/src/shared/deep-link.ts @@ -21,23 +21,13 @@ const maxManifestUrlLength = 2_048; const maxSourceLength = 2_048; const maxModelLength = 256; const maxModels = 300; -const providerLinkApiKeyError = "Provider links cannot include API keys. Add the key manually after verifying the endpoint."; -const protocolAliases: Record = { - anthropic: "anthropic_messages", - anthropic_messages: "anthropic_messages", - claude: "anthropic_messages", - gemini: "gemini_generate_content", - gemini_generate: "gemini_generate_content", - gemini_generate_content: "gemini_generate_content", - google: "gemini_generate_content", - openai: "openai_chat_completions", - openai_chat: "openai_chat_completions", - openai_chat_completions: "openai_chat_completions", - openai_response: "openai_responses", - openai_responses: "openai_responses", - responses: "openai_responses" -}; +const providerProtocols = new Set([ + "anthropic_messages", + "gemini_generate_content", + "openai_chat_completions", + "openai_responses" +]); export function isAppDeepLinkUrl(value: string): boolean { return value.trim().toLowerCase().startsWith(`${appDeepLinkProtocol}://`); @@ -91,8 +81,8 @@ export function parseProviderManifestDeepLinkPayload(rawUrl: string): ProviderMa const payload = readPayloadRecord(url.searchParams); const manifestUrl = boundedString( - firstStringParam(url.searchParams, ["manifest_url", "manifestUrl", "manifest"]) ?? - firstPayloadString(payload, ["manifest_url", "manifestUrl", "manifest"]), + firstStringParam(url.searchParams, ["manifest"]) ?? + firstPayloadString(payload, ["manifest"]), maxManifestUrlLength, "Manifest URL" ); @@ -125,14 +115,14 @@ export function parseProviderDeepLinkPayload(rawUrl: string): ProviderDeepLinkPa const params = url.searchParams; const payload = readPayloadRecord(params); const name = boundedString( - firstStringParam(params, ["name", "provider_name", "providerName", "title"]) ?? - firstPayloadString(payload, ["name", "provider_name", "providerName", "title"]), + firstStringParam(params, ["name"]) ?? + firstPayloadString(payload, ["name"]), maxNameLength, "Provider name" ); const baseUrl = boundedString( - firstStringParam(params, ["base_url", "baseUrl", "api_base_url", "apiBaseUrl", "url", "endpoint"]) ?? - firstPayloadString(payload, ["base_url", "baseUrl", "api_base_url", "apiBaseUrl", "url", "endpoint"]), + firstStringParam(params, ["base_url"]) ?? + firstPayloadString(payload, ["base_url"]), maxBaseUrlLength, "Base URL" ); @@ -142,33 +132,31 @@ export function parseProviderDeepLinkPayload(rawUrl: string): ProviderDeepLinkPa validateProviderBaseUrl(baseUrl); const apiKey = boundedString( - firstStringParam(params, ["api_key", "apiKey", "apikey", "key", "token"]) ?? - firstPayloadString(payload, ["api_key", "apiKey", "apikey", "key", "token"]), + firstStringParam(params, ["api_key"]) ?? + firstPayloadString(payload, ["api_key"]), maxApiKeyLength, "API key" ); - if (apiKey) { - throw new Error(providerLinkApiKeyError); - } const icon = boundedString( - firstStringParam(params, ["icon", "icon_url", "iconUrl"]) ?? - firstPayloadString(payload, ["icon", "icon_url", "iconUrl"]), + firstStringParam(params, ["icon"]) ?? + firstPayloadString(payload, ["icon"]), maxIconLength, "Provider icon" ); const protocol = normalizeProviderProtocol( - firstStringParam(params, ["protocol", "type"]) ?? firstPayloadString(payload, ["protocol", "type"]) + firstStringParam(params, ["protocol"]) ?? firstPayloadString(payload, ["protocol"]) ); const models = readDeepLinkModels(params, payload); const account = readDeepLinkAccount(params, payload); const source = boundedString( - firstStringParam(params, ["source", "source_url", "sourceUrl"]) ?? - firstPayloadString(payload, ["source", "source_url", "sourceUrl"]), + firstStringParam(params, ["source"]) ?? + firstPayloadString(payload, ["source"]), maxSourceLength, "Source URL" ); return { ...(account ? { account } : {}), + ...(apiKey ? { apiKey } : {}), baseUrl, ...(icon ? { icon } : {}), models, @@ -196,14 +184,14 @@ function parseProviderPayloadFields( sourceFallback?: string ): ProviderDeepLinkPayload { const name = boundedString( - firstStringParam(params, ["name", "provider_name", "providerName", "title"]) ?? - firstPayloadString(payload, ["name", "provider_name", "providerName", "title"]), + firstStringParam(params, ["name"]) ?? + firstPayloadString(payload, ["name"]), maxNameLength, "Provider name" ); const baseUrl = boundedString( - firstStringParam(params, ["base_url", "baseUrl", "api_base_url", "apiBaseUrl", "url", "endpoint"]) ?? - firstPayloadString(payload, ["base_url", "baseUrl", "api_base_url", "apiBaseUrl", "url", "endpoint"]), + firstStringParam(params, ["base_url"]) ?? + firstPayloadString(payload, ["base_url"]), maxBaseUrlLength, "Base URL" ); @@ -213,25 +201,25 @@ function parseProviderPayloadFields( validateProviderBaseUrl(baseUrl); const apiKey = boundedString( - firstStringParam(params, ["api_key", "apiKey", "apikey", "key", "token"]) ?? - firstPayloadString(payload, ["api_key", "apiKey", "apikey", "key", "token"]), + firstStringParam(params, ["api_key"]) ?? + firstPayloadString(payload, ["api_key"]), maxApiKeyLength, "API key" ); const icon = boundedString( - firstStringParam(params, ["icon", "icon_url", "iconUrl"]) ?? - firstPayloadString(payload, ["icon", "icon_url", "iconUrl"]), + firstStringParam(params, ["icon"]) ?? + firstPayloadString(payload, ["icon"]), maxIconLength, "Provider icon" ); const protocol = normalizeProviderProtocol( - firstStringParam(params, ["protocol", "type"]) ?? firstPayloadString(payload, ["protocol", "type"]) + firstStringParam(params, ["protocol"]) ?? firstPayloadString(payload, ["protocol"]) ); const models = readDeepLinkModels(params, payload); const account = readDeepLinkAccount(params, payload); const source = boundedString( - firstStringParam(params, ["source", "source_url", "sourceUrl"]) ?? - firstPayloadString(payload, ["source", "source_url", "sourceUrl"]) ?? + firstStringParam(params, ["source"]) ?? + firstPayloadString(payload, ["source"]) ?? sourceFallback, maxSourceLength, "Source URL" @@ -251,25 +239,20 @@ function parseProviderPayloadFields( function readDeepLinkAccount(params: URLSearchParams, payload: Record | undefined): ProviderAccountConfig | undefined { const fetchUsage = readDeepLinkBoolean(params, payload, [ - "fetch_usage", - "fetchUsage", - "usage_enabled", - "usageEnabled", - "account_enabled", - "accountEnabled" + "fetch_usage" ]); if (fetchUsage === false) { return { enabled: false }; } - const payloadAccount = normalizeProviderAccountConfig(payload?.account ?? payload?.usage); + const payloadAccount = normalizeProviderAccountConfig(payload?.account); if (payloadAccount) { return payloadAccount; } const endpoint = boundedString( - firstStringParam(params, ["usage_url", "usageUrl", "account_url", "accountUrl"]) ?? - firstPayloadString(payload, ["usage_url", "usageUrl", "account_url", "accountUrl"]), + firstStringParam(params, ["usage_url"]) ?? + firstPayloadString(payload, ["usage_url"]), maxBaseUrlLength, "Usage URL" ); @@ -279,23 +262,23 @@ function readDeepLinkAccount(params: URLSearchParams, payload: Record | undefined { - const value = firstStringParam(params, ["payload", "config", "data"]); + const value = firstStringParam(params, ["payload"]); if (!value) { return undefined; } @@ -492,19 +475,16 @@ function normalizeProviderProtocol(value: string | undefined): GatewayProviderPr if (!value) { return undefined; } - const normalized = value.trim().toLowerCase().replace(/[\s-]+/g, "_"); - const protocol = protocolAliases[normalized]; - if (!protocol) { + const protocol = value.trim(); + if (!providerProtocols.has(protocol as GatewayProviderProtocol)) { throw new Error(`Unsupported provider protocol: ${value}`); } - return protocol; + return protocol as GatewayProviderProtocol; } function readDeepLinkModels(params: URLSearchParams, payload: Record | undefined): string[] { const values = [ - ...params.getAll("model"), ...params.getAll("models"), - ...params.getAll("models[]"), ...payloadModels(payload) ]; const seen = new Set(); @@ -533,7 +513,7 @@ function payloadModels(payload: Record | undefined): string[] { if (!payload) { return []; } - const value = payload.models ?? payload.model; + const value = payload.models; if (Array.isArray(value)) { return value.filter((item): item is string => typeof item === "string"); } diff --git a/src/shared/ipc-channels.ts b/src/shared/ipc-channels.ts index e380e8e..f88003a 100644 --- a/src/shared/ipc-channels.ts +++ b/src/shared/ipc-channels.ts @@ -29,6 +29,7 @@ export const IPC_CHANNELS = { appOpenBuiltInBrowser: "ccr:app:open-built-in-browser", appOpenExternal: "ccr:app:open-external", appOpenSettings: "ccr:app:open-settings", + appOpenUpdate: "ccr:app:open-update", appOpenProfile: "ccr:app:open-profile", appApplyClaudeAppGateway: "ccr:app:apply-claude-app-gateway", appApplyProfile: "ccr:app:apply-profile", From ac39e702b3981cc9fa072b04eac2e6f19917c26f Mon Sep 17 00:00:00 2001 From: musistudio Date: Sun, 28 Jun 2026 21:23:02 +0800 Subject: [PATCH 02/94] chore: release 3.0.2 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index f6ab110..036ccac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "claude-code-router", - "version": "3.0.1", + "version": "3.0.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "claude-code-router", - "version": "3.0.1", + "version": "3.0.2", "dependencies": { "@the-next-ai/ai-gateway": "^1.0.1", "@the-next-ai/bot-gateway-sdk": "^0.1.0", diff --git a/package.json b/package.json index 4a01640..6666ca5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "claude-code-router", "private": true, - "version": "3.0.1", + "version": "3.0.2", "description": "Desktop scaffold for Claude Code Router.", "main": "dist/main/main.js", "bin": { From 1b0c4813768028f5ac4d6eb0eb1a1fff7326edff Mon Sep 17 00:00:00 2001 From: musistudio Date: Sun, 28 Jun 2026 21:45:45 +0800 Subject: [PATCH 03/94] Remove CDN workflow and add MIT license info --- .github/workflows/cdn.yml | 33 --------------------------------- LICENSE | 21 +++++++++++++++++++++ README.md | 5 +++++ README_zh.md | 5 +++++ cdn/package.json | 1 + docs/package-lock.json | 1 + docs/package.json | 1 + package-lock.json | 1 + package.json | 1 + 9 files changed, 36 insertions(+), 33 deletions(-) delete mode 100644 .github/workflows/cdn.yml create mode 100644 LICENSE diff --git a/.github/workflows/cdn.yml b/.github/workflows/cdn.yml deleted file mode 100644 index 6223aa6..0000000 --- a/.github/workflows/cdn.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: CDN - -on: - push: - branches: - - main - paths: - - "cdn/**" - - ".github/workflows/cdn.yml" - workflow_dispatch: - -permissions: - contents: read - -concurrency: - group: cdn - cancel-in-progress: false - -jobs: - deploy: - name: Deploy Cloudflare Pages - runs-on: ubuntu-latest - steps: - - name: Check out repository - uses: actions/checkout@v6 - - - name: Deploy CDN assets - uses: cloudflare/wrangler-action@v3 - with: - apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} - accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - workingDirectory: cdn - command: pages deploy public --project-name=claude-code-router-cdn --branch=${{ github.ref_name }} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ea7e8ac --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 musistudio + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 1377e08..e53da3b 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ Chinese README Discord License + Documentation

@@ -281,3 +282,7 @@ A huge thank you to all our sponsors for their generous support. - @x\*g (If your name is masked, please contact me via my homepage email to update it with your GitHub username.) + +## License + +This project is licensed under the [MIT License](LICENSE). diff --git a/README_zh.md b/README_zh.md index dc81f80..dfe6b3a 100644 --- a/README_zh.md +++ b/README_zh.md @@ -4,6 +4,7 @@ English README Discord License + 文档

@@ -281,3 +282,7 @@ macOS 打包 hook 会在产物生成前验证代码签名、公证票据 staplin - @x\*g (如果你的名字被打码,请通过我的主页邮箱联系我更新为 GitHub 用户名。) + +## 许可证 + +本项目基于 [MIT License](LICENSE) 发布。 diff --git a/cdn/package.json b/cdn/package.json index 3968876..f49b77a 100644 --- a/cdn/package.json +++ b/cdn/package.json @@ -2,6 +2,7 @@ "name": "claude-code-router-cdn", "private": true, "version": "0.1.0", + "license": "MIT", "type": "module", "scripts": { "deploy": "wrangler pages deploy public --project-name=claude-code-router-cdn", diff --git a/docs/package-lock.json b/docs/package-lock.json index 02de48c..92adc81 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -7,6 +7,7 @@ "": { "name": "claude-code-router-docs", "version": "0.1.0", + "license": "MIT", "devDependencies": { "astro": "7.0.0", "lucide-astro": "^0.556.0" diff --git a/docs/package.json b/docs/package.json index b2046bf..0f0aa87 100644 --- a/docs/package.json +++ b/docs/package.json @@ -2,6 +2,7 @@ "name": "claude-code-router-docs", "private": true, "version": "0.1.0", + "license": "MIT", "type": "module", "scripts": { "dev": "astro dev", diff --git a/package-lock.json b/package-lock.json index 036ccac..a741305 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "": { "name": "claude-code-router", "version": "3.0.2", + "license": "MIT", "dependencies": { "@the-next-ai/ai-gateway": "^1.0.1", "@the-next-ai/bot-gateway-sdk": "^0.1.0", diff --git a/package.json b/package.json index 6666ca5..1a070ff 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "claude-code-router", "private": true, "version": "3.0.2", + "license": "MIT", "description": "Desktop scaffold for Claude Code Router.", "main": "dist/main/main.js", "bin": { From c899b51b04df560dce7c9852eeece71374f6584d Mon Sep 17 00:00:00 2001 From: musi Date: Mon, 29 Jun 2026 08:44:27 +0800 Subject: [PATCH 04/94] Add remote sync to Claude wrapper and provider IDs --- src/main/codex-cli-middleware-runtime.ts | 218 ++++++- src/main/config.ts | 45 +- src/main/profile-service.ts | 25 +- src/main/request-log-store.ts | 2 + src/main/usage-store.ts | 2 + src/renderer/pages/home/shared/providers.ts | 2 +- src/server/gateway/remote-control-service.ts | 627 +++++++++++++++++++ src/server/gateway/service.ts | 134 +++- src/shared/app.ts | 1 + 9 files changed, 1023 insertions(+), 33 deletions(-) create mode 100644 src/server/gateway/remote-control-service.ts diff --git a/src/main/codex-cli-middleware-runtime.ts b/src/main/codex-cli-middleware-runtime.ts index 6791ffb..fcd0a11 100644 --- a/src/main/codex-cli-middleware-runtime.ts +++ b/src/main/codex-cli-middleware-runtime.ts @@ -58,12 +58,34 @@ async function main() { async function runClaudeCodeCliWrapper(args) { const realCli = expandHome(nonEmptyEnv("CCR_REAL_CLAUDE_CODE_BIN") || nonEmptyEnv("CCR_CLAUDE_CODE_BIN") || nonEmptyEnv("CODEXL_CLAUDE_CODE_BIN") || "claude"); log("claude_code_wrapper_start", { realCli, args }); + const remoteSync = createRemoteSyncClient({ + args, + cwd: process.cwd(), + mode: "claude-cli", + title: nonEmptyEnv("CCR_REMOTE_SYNC_PROFILE_NAME") || "Claude Code" + }); + const injectRemoteStdin = boolEnv("CCR_REMOTE_SYNC_INJECT_STDIN"); const child = childProcess.spawn(realCli, args, { env: withoutKeys(process.env, ["CCR_CLAUDE_CODE_WRAPPER", "CCR_REAL_CLAUDE_CODE_BIN"]), - stdio: ["inherit", "pipe", "inherit"] + stdio: [injectRemoteStdin ? "pipe" : "inherit", "pipe", "inherit"] + }); + if (injectRemoteStdin && child.stdin) { + process.stdin.pipe(child.stdin); + } + remoteSync.start((event) => { + const text = remoteEventText(event); + if (!text) return; + if (injectRemoteStdin && child.stdin && !child.killed) { + child.stdin.write(text + "\n"); + return; + } + if (boolEnv("CCR_REMOTE_SYNC_NOTIFY_INBOUND") || !process.env.CCR_REMOTE_SYNC_NOTIFY_INBOUND) { + process.stdout.write("\n[CCR remote] " + text + "\n"); + } }); child.on("error", (error) => { log("claude_code_wrapper_spawn_error", { error: formatError(error) }); + remoteSync.postEvent("claude.spawn.error", { error: formatError(error) }, { direction: "system" }); }); let pending = ""; child.stdout.on("data", (chunk) => { @@ -73,12 +95,16 @@ async function runClaudeCodeCliWrapper(args) { pending = lines.pop() || ""; for (const line of lines) { botBridge().handleClaudeCliLine(line); + remoteSync.postEvent("claude.stdout", { line }, { text: line }); } }); const code = await waitForChild(child); if (pending.trim()) { botBridge().handleClaudeCliLine(pending); + remoteSync.postEvent("claude.stdout", { line: pending }, { text: pending }); } + await remoteSync.postEvent("claude.exit", { code }, { direction: "system" }); + remoteSync.stop(); log("claude_code_wrapper_exit", { code }); process.exitCode = code; } @@ -2365,6 +2391,196 @@ function writeLine(stream, value) { stream.write(JSON.stringify(value) + "\n"); } +function createRemoteSyncClient(options) { + const endpoint = normalizeRemoteSyncEndpoint(nonEmptyEnv("CCR_REMOTE_SYNC_ENDPOINT")); + const enabled = endpoint && !["0", "false", "no", "off"].includes(String(process.env.CCR_REMOTE_SYNC_ENABLED || "1").trim().toLowerCase()); + if (!enabled || typeof fetch !== "function") { + return { + postEvent: async () => {}, + start() {}, + stop() {} + }; + } + return new RemoteSyncClient({ + args: options.args || [], + cwd: options.cwd || process.cwd(), + endpoint, + mode: options.mode || "agent", + title: options.title || "CCR Remote", + profileId: nonEmptyEnv("CCR_REMOTE_SYNC_PROFILE_ID"), + profileName: nonEmptyEnv("CCR_REMOTE_SYNC_PROFILE_NAME") + }); +} + +class RemoteSyncClient { + constructor(options) { + this.options = options; + this.active = false; + this.apiKey = ""; + this.lastInboundSeq = 0; + this.pollTimer = null; + this.ready = null; + this.seenInbound = new Set(); + this.sessionId = nonEmptyEnv("CCR_REMOTE_SYNC_SESSION_ID") || "ccr-" + safePathSegment(options.profileId || options.profileName || options.mode) + "-" + uuid(); + } + + start(onInbound) { + if (this.active) return; + this.active = true; + this.ready = this.open().catch((error) => { + this.active = false; + log("remote_sync_start_failed", { error: formatError(error) }); + }); + this.ready.then(() => { + if (this.active) this.pollInbound(onInbound); + }).catch(() => {}); + } + + stop() { + this.active = false; + if (this.pollTimer) { + clearTimeout(this.pollTimer); + this.pollTimer = null; + } + } + + async open() { + this.apiKey = await readRemoteSyncApiKey(); + const response = await this.request("POST", "/sessions", { + id: this.sessionId, + title: this.options.title, + metadata: { + args: this.options.args, + cwd: this.options.cwd, + mode: this.options.mode, + pid: process.pid, + profileId: this.options.profileId, + profileName: this.options.profileName, + startedAt: new Date().toISOString() + } + }); + const session = response && response.session; + if (session && session.id) this.sessionId = session.id; + log("remote_sync_started", { sessionId: this.sessionId, endpoint: this.options.endpoint }); + } + + postEvent(type, payload, options) { + if (!this.active) return Promise.resolve(); + const eventOptions = options || {}; + return Promise.resolve(this.ready) + .then(() => { + if (!this.active || !this.sessionId) return undefined; + return this.request("POST", "/sessions/" + encodeURIComponent(this.sessionId) + "/events", { + direction: eventOptions.direction || "local", + payload: payload || {}, + source: "ccr-claude-wrapper", + text: eventOptions.text, + type + }); + }) + .catch((error) => { + log("remote_sync_event_failed", { type, error: formatError(error) }); + }); + } + + pollInbound(onInbound) { + if (!this.active) return; + this.request("GET", "/sessions/" + encodeURIComponent(this.sessionId) + "/inbound?after=" + this.lastInboundSeq) + .then((response) => { + const events = Array.isArray(response && response.events) ? response.events : []; + for (const event of events) { + if (!event || !Number.isFinite(event.seq)) continue; + this.lastInboundSeq = Math.max(this.lastInboundSeq, event.seq); + const key = event.id || event.dedupeKey || String(event.seq); + if (this.seenInbound.has(key)) continue; + this.seenInbound.add(key); + while (this.seenInbound.size > 500) { + const oldest = this.seenInbound.values().next().value; + if (!oldest) break; + this.seenInbound.delete(oldest); + } + try { + onInbound(event); + } catch (error) { + log("remote_sync_inbound_handler_failed", { error: formatError(error) }); + } + } + }) + .catch((error) => { + log("remote_sync_poll_failed", { error: formatError(error) }); + }) + .finally(() => { + if (!this.active) return; + this.pollTimer = setTimeout(() => this.pollInbound(onInbound), numberEnv("CCR_REMOTE_SYNC_POLL_INTERVAL_MS", 2000)); + }); + } + + async request(method, suffix, body) { + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), numberEnv("CCR_REMOTE_SYNC_REQUEST_TIMEOUT_MS", 5000)); + const headers = { "accept": "application/json" }; + if (body !== undefined) headers["content-type"] = "application/json"; + if (this.apiKey) headers.authorization = "Bearer " + this.apiKey; + try { + const response = await fetch(remoteSyncUrl(this.options.endpoint, suffix), { + method, + headers, + body: body === undefined ? undefined : JSON.stringify(body), + signal: controller.signal + }); + if (!response.ok) { + throw new Error("HTTP " + response.status + " from CCR remote sync"); + } + return await response.json(); + } finally { + clearTimeout(timeout); + } + } +} + +async function readRemoteSyncApiKey() { + const direct = nonEmptyEnv("CCR_REMOTE_SYNC_API_KEY"); + if (direct) return direct; + const helper = nonEmptyEnv("CCR_REMOTE_SYNC_API_KEY_HELPER"); + if (!helper) return ""; + return new Promise((resolve) => { + childProcess.execFile(expandHome(helper), { + shell: process.platform === "win32", + timeout: 3000, + windowsHide: true + }, (error, stdout) => { + if (error) { + log("remote_sync_api_key_helper_failed", { error: formatError(error) }); + resolve(""); + return; + } + resolve(String(stdout || "").split(/\r?\n/).map((line) => line.trim()).find(Boolean) || ""); + }); + }); +} + +function normalizeRemoteSyncEndpoint(value) { + const trimmed = String(value || "").trim().replace(/\/+$/, ""); + return trimmed || ""; +} + +function remoteSyncUrl(endpoint, suffix) { + return endpoint + (String(suffix || "").startsWith("/") ? suffix : "/" + suffix); +} + +function remoteEventText(event) { + if (!event || typeof event !== "object") return ""; + if (typeof event.text === "string" && event.text.trim()) return event.text.trim(); + const payload = event.payload; + if (typeof payload === "string" && payload.trim()) return payload.trim(); + if (payload && typeof payload === "object") { + if (typeof payload.text === "string" && payload.text.trim()) return payload.text.trim(); + if (typeof payload.content === "string" && payload.content.trim()) return payload.content.trim(); + if (typeof payload.message === "string" && payload.message.trim()) return payload.message.trim(); + } + return ""; +} + function createBotGatewayBridge() { const config = readBotGatewayBridgeConfig(); if (!config.enabled) { diff --git a/src/main/config.ts b/src/main/config.ts index 8fd494e..8f7f424 100644 --- a/src/main/config.ts +++ b/src/main/config.ts @@ -1,3 +1,4 @@ +import { createHash } from "node:crypto"; import { existsSync, readFileSync } from "node:fs"; import { loadPersistedAppConfig, replacePersistedAppConfig } from "./app-config-store"; import { loadPersistedApiKeys, replacePersistedApiKeys } from "./api-key-store"; @@ -623,6 +624,7 @@ function sanitizeConfigForDisk(config: AppConfig): AppConfig { ...config.gateway, coreHost: INTERNAL_GATEWAY_CORE_HOST }, + Providers: withProviderIds(config.Providers), profile: sanitizeProfileConfigForDisk(config.profile) }; } @@ -1100,6 +1102,7 @@ function parseProviders(value: unknown): GatewayProviderConfig[] | undefined { extraBody: item.extraBody, extraHeaders: item.extraHeaders, icon: readString(item.icon), + id: readString(item.id), models, name, provider: readString(item.provider), @@ -1110,7 +1113,47 @@ function parseProviders(value: unknown): GatewayProviderConfig[] | undefined { }) .filter((item): item is GatewayProviderConfig => Boolean(item)); - return providers; + return withProviderIds(providers); +} + +function withProviderIds(providers: GatewayProviderConfig[]): GatewayProviderConfig[] { + const counts = new Map(); + return providers.map((provider) => { + const baseId = providerRuntimeIdCandidate(provider); + const nextCount = (counts.get(baseId) ?? 0) + 1; + counts.set(baseId, nextCount); + return { + ...provider, + id: nextCount === 1 ? baseId : `${baseId}-${nextCount}` + }; + }); +} + +function providerRuntimeIdCandidate(provider: GatewayProviderConfig): string { + const explicit = sanitizeProviderId(provider.id); + if (explicit) { + return explicit; + } + const slug = sanitizeProviderId(provider.name) || "provider"; + const source = [ + provider.name, + provider.provider ?? "", + providerBaseUrl(provider), + provider.type ?? "" + ].join("\n"); + const hash = createHash("sha256").update(source).digest("hex").slice(0, 10); + return `provider-${slug.slice(0, 48)}-${hash}`; +} + +function sanitizeProviderId(value: string | undefined): string | undefined { + const normalized = value + ?.normalize("NFKD") + .replace(/[\u0300-\u036f]/g, "") + .toLowerCase() + .replace(/[^a-z0-9_.-]+/g, "-") + .replace(/^-+|-+$/g, "") + .slice(0, 72); + return normalized || undefined; } function parseProviderCredentials(value: unknown): ProviderCredentialConfig[] | undefined { diff --git a/src/main/profile-service.ts b/src/main/profile-service.ts index 68b5888..f0df909 100644 --- a/src/main/profile-service.ts +++ b/src/main/profile-service.ts @@ -74,7 +74,7 @@ function applyClaudeCodeProfile(config: AppConfig, profile: ProfileConfig, token } const helperResult = writeClaudeCodeApiKeyHelper(profile, token); - const wrapperResult = writeClaudeCodeWrapper(config, profile); + const wrapperResult = writeClaudeCodeWrapper(config, profile, helperResult.file); const nextSettings = { ...settings, apiKeyHelper: process.platform === "win32" ? `"${helperResult.file}"` : helperResult.file, @@ -459,7 +459,7 @@ function claudeCodeApiKeyHelperCmdScript(token: string): string { ].join("\r\n"); } -function writeClaudeCodeWrapper(config: AppConfig, profile: ProfileConfig): { backupFile?: string; changed: boolean; file: string } { +function writeClaudeCodeWrapper(config: AppConfig, profile: ProfileConfig, apiKeyHelperFile: string): { backupFile?: string; changed: boolean; file: string } { const binDir = path.join(CONFIGDIR, "bin"); mkdirSync(binDir, { mode: privateDirMode, recursive: true }); const runtimeFile = path.join(binDir, codexMiddlewareRuntimeFilename()); @@ -469,8 +469,8 @@ function writeClaudeCodeWrapper(config: AppConfig, profile: ProfileConfig): { ba } const file = path.join(binDir, claudeCodeWrapperFilename(profile)); const content = process.platform === "win32" - ? claudeCodeWrapperCmdScript(config, profile, runtimeFile) - : claudeCodeWrapperShellScript(config, profile, runtimeFile); + ? claudeCodeWrapperCmdScript(config, profile, runtimeFile, apiKeyHelperFile) + : claudeCodeWrapperShellScript(config, profile, runtimeFile, apiKeyHelperFile); const writeResult = writeFileWithBackup(file, content, { mode: privateExecutableMode }); if (process.platform !== "win32") { chmodSync(file, privateExecutableMode); @@ -489,9 +489,10 @@ function claudeCodeWrapperFilename(profile: ProfileConfig): string { : `ccr-claude-code-wrapper-${slug}`; } -function claudeCodeWrapperShellScript(config: AppConfig, profile: ProfileConfig, runtimeFile: string): string { +function claudeCodeWrapperShellScript(config: AppConfig, profile: ProfileConfig, runtimeFile: string, apiKeyHelperFile: string): string { const realClaude = profile.env?.CCR_CLAUDE_CODE_BIN?.trim() || "claude"; const surface = normalizeProfileSurface(profile.surface); + const remoteEndpoint = `${gatewayEndpoint(config)}/__ccr/remote`; const envExports = Object.entries(profileEnv(profile)) .filter(([key]) => key !== "CCR_CLAUDE_CODE_BIN") .map(([key, value]) => `export ${key}=${shellQuote(value)}`); @@ -505,14 +506,21 @@ function claudeCodeWrapperShellScript(config: AppConfig, profile: ProfileConfig, `export CCR_CLAUDE_CODE_WRAPPER=1`, `export CCR_REAL_CLAUDE_CODE_BIN=${shellQuote(realClaude)}`, `export CODEXL_CLAUDE_CODE_BIN=${shellQuote(realClaude)}`, + `if [ -z "\${CCR_REMOTE_SYNC_ENABLED:-}" ]; then CCR_REMOTE_SYNC_ENABLED=1; fi`, + `if [ -z "\${CCR_REMOTE_SYNC_ENDPOINT:-}" ]; then CCR_REMOTE_SYNC_ENDPOINT=${shellQuote(remoteEndpoint)}; fi`, + `if [ -z "\${CCR_REMOTE_SYNC_API_KEY_HELPER:-}" ]; then CCR_REMOTE_SYNC_API_KEY_HELPER=${shellQuote(apiKeyHelperFile)}; fi`, + `if [ -z "\${CCR_REMOTE_SYNC_PROFILE_ID:-}" ]; then CCR_REMOTE_SYNC_PROFILE_ID=${shellQuote(profile.id || profile.name || "claude-code")}; fi`, + `if [ -z "\${CCR_REMOTE_SYNC_PROFILE_NAME:-}" ]; then CCR_REMOTE_SYNC_PROFILE_NAME=${shellQuote(profile.name || profile.id || "Claude Code")}; fi`, + "export CCR_REMOTE_SYNC_ENABLED CCR_REMOTE_SYNC_ENDPOINT CCR_REMOTE_SYNC_API_KEY_HELPER CCR_REMOTE_SYNC_PROFILE_ID CCR_REMOTE_SYNC_PROFILE_NAME", ...nodeRuntimeShellExecLines(runtimeFile), "" ].join("\n"); } -function claudeCodeWrapperCmdScript(config: AppConfig, profile: ProfileConfig, runtimeFile: string): string { +function claudeCodeWrapperCmdScript(config: AppConfig, profile: ProfileConfig, runtimeFile: string, apiKeyHelperFile: string): string { const realClaude = profile.env?.CCR_CLAUDE_CODE_BIN?.trim() || "claude"; const surface = normalizeProfileSurface(profile.surface); + const remoteEndpoint = `${gatewayEndpoint(config)}/__ccr/remote`; const envExports = Object.entries(profileEnv(profile)) .filter(([key]) => key !== "CCR_CLAUDE_CODE_BIN") .map(([key, value]) => cmdSetLine(key, value)); @@ -525,6 +533,11 @@ function claudeCodeWrapperCmdScript(config: AppConfig, profile: ProfileConfig, r cmdSetLine("CCR_CLAUDE_CODE_WRAPPER", "1"), cmdSetLine("CCR_REAL_CLAUDE_CODE_BIN", realClaude), cmdSetLine("CODEXL_CLAUDE_CODE_BIN", realClaude), + `if not defined CCR_REMOTE_SYNC_ENABLED ${cmdSetLine("CCR_REMOTE_SYNC_ENABLED", "1")}`, + `if not defined CCR_REMOTE_SYNC_ENDPOINT ${cmdSetLine("CCR_REMOTE_SYNC_ENDPOINT", remoteEndpoint)}`, + `if not defined CCR_REMOTE_SYNC_API_KEY_HELPER ${cmdSetLine("CCR_REMOTE_SYNC_API_KEY_HELPER", apiKeyHelperFile)}`, + `if not defined CCR_REMOTE_SYNC_PROFILE_ID ${cmdSetLine("CCR_REMOTE_SYNC_PROFILE_ID", profile.id || profile.name || "claude-code")}`, + `if not defined CCR_REMOTE_SYNC_PROFILE_NAME ${cmdSetLine("CCR_REMOTE_SYNC_PROFILE_NAME", profile.name || profile.id || "Claude Code")}`, ...nodeRuntimeCmdExecLines(runtimeFile), "" ].join("\r\n"); diff --git a/src/main/request-log-store.ts b/src/main/request-log-store.ts index b791dd8..1ebfea5 100644 --- a/src/main/request-log-store.ts +++ b/src/main/request-log-store.ts @@ -71,6 +71,7 @@ type RequestLogRecordInput = { fallbackModel?: string; method: string; path: string; + providerName?: string; providerProtocol?: GatewayProviderProtocol; requestBody: Buffer; requestHeaders: HeaderRecord; @@ -250,6 +251,7 @@ class RequestLogStore { const route = splitRouteSelector(input.fallbackModel); const requestModel = extractModelFromBody(input.requestBody.toString("utf8")); const provider = + normalizeFilterValue(input.providerName) ?? readResponseHeader(input.responseHeaders, "x-gateway-target-provider-name") ?? readResponseHeader(input.responseHeaders, "x-gateway-target-provider") ?? route.provider; diff --git a/src/main/usage-store.ts b/src/main/usage-store.ts index da17971..351d8c5 100644 --- a/src/main/usage-store.ts +++ b/src/main/usage-store.ts @@ -48,6 +48,7 @@ type UsageCaptureInput = { fallbackModel?: string; method: string; path: string; + providerName?: string; providerProtocol?: GatewayProviderProtocol; requestId?: string; responseHeaders: Headers; @@ -316,6 +317,7 @@ export async function recordGatewayUsageCapture(input: UsageCaptureInput): Promi }); const route = splitRouteSelector(input.fallbackModel); const provider = + input.providerName ?? readHeader(input.responseHeaders, "x-gateway-target-provider-name") ?? readHeader(input.responseHeaders, "x-gateway-target-provider") ?? route.provider; diff --git a/src/renderer/pages/home/shared/providers.ts b/src/renderer/pages/home/shared/providers.ts index 5fe0451..4694198 100644 --- a/src/renderer/pages/home/shared/providers.ts +++ b/src/renderer/pages/home/shared/providers.ts @@ -1866,7 +1866,7 @@ export function providerCapabilitiesSummary(provider: GatewayProviderConfig, tra } export function providerListItemKey(provider: GatewayProviderConfig, index: number): string { - return `${index}:${provider.name || "provider"}`; + return provider.id || `${index}:${provider.name || "provider"}`; } export function providerMatchesQuery(provider: GatewayProviderConfig, query: string): boolean { diff --git a/src/server/gateway/remote-control-service.ts b/src/server/gateway/remote-control-service.ts new file mode 100644 index 0000000..b3a7e70 --- /dev/null +++ b/src/server/gateway/remote-control-service.ts @@ -0,0 +1,627 @@ +import { randomUUID } from "node:crypto"; +import type { IncomingMessage, ServerResponse } from "node:http"; + +export const ccrRemoteControlPathPrefix = "/__ccr/remote"; + +type RemoteDirection = "inbound" | "local" | "remote" | "system"; + +export type CcrRemoteControlRequestContext = { + endpoint: string; + path: string; + readBody: (request: IncomingMessage) => Promise; + request: IncomingMessage; + response: ServerResponse; + sendJson: (response: ServerResponse, statusCode: number, payload: unknown) => void; +}; + +type RemoteSession = { + archivedAt?: string; + createdAt: string; + events: RemoteEvent[]; + id: string; + inboundEvents: RemoteEvent[]; + lastSeq: number; + metadata: Record; + presence: Record; + seenDedupeKeys: Map; + subscribers: Set; + title: string; + updatedAt: string; +}; + +type RemoteEvent = { + createdAt: string; + dedupeKey?: string; + direction: RemoteDirection; + id: string; + payload: unknown; + role?: string; + seq: number; + sessionId: string; + source?: string; + text?: string; + type: string; +}; + +type RemotePresence = { + lastSeenAt: string; + metadata: Record; + name: string; + role: string; +}; + +type RemoteSubscriber = { + close: () => void; + id: string; + kind: "events" | "inbound"; + response: ServerResponse; +}; + +const maxSessions = 100; +const maxEventsPerSession = 2_000; +const maxInboundEventsPerSession = 500; +const sseHeartbeatMs = 15_000; + +class CcrRemoteControlService { + private readonly sessions = new Map(); + + async handleRequest(context: CcrRemoteControlRequestContext): Promise { + const segments = remotePathSegments(context.path); + const [root, sessionId, resource] = segments; + + if (segments.length === 0 || context.path === ccrRemoteControlPathPrefix) { + this.sendCapabilities(context); + return; + } + + if (root === "capabilities") { + this.sendCapabilities(context); + return; + } + + if (root !== "sessions") { + context.sendJson(context.response, 404, { error: { message: "Remote control endpoint not found." } }); + return; + } + + if (!sessionId) { + await this.handleSessionsRequest(context); + return; + } + + if (!resource) { + await this.handleSessionRequest(context, sessionId); + return; + } + + if (resource === "events") { + await this.handleEventsRequest(context, sessionId); + return; + } + + if (resource === "inbound") { + await this.handleInboundRequest(context, sessionId); + return; + } + + if (resource === "presence") { + await this.handlePresenceRequest(context, sessionId); + return; + } + + context.sendJson(context.response, 404, { error: { message: "Remote control session endpoint not found." } }); + } + + private sendCapabilities(context: CcrRemoteControlRequestContext): void { + context.sendJson(context.response, 200, { + endpoints: { + createSession: `${context.endpoint}${ccrRemoteControlPathPrefix}/sessions`, + inbound: `${context.endpoint}${ccrRemoteControlPathPrefix}/sessions/{sessionId}/inbound`, + sessionEvents: `${context.endpoint}${ccrRemoteControlPathPrefix}/sessions/{sessionId}/events` + }, + name: "ccr-remote-control", + protocol: "ccr.remote.v1", + transport: ["json", "sse"], + capabilities: { + catchupReplay: true, + fanout: true, + inboundQueue: true, + presence: true + } + }); + } + + private async handleSessionsRequest(context: CcrRemoteControlRequestContext): Promise { + if (context.request.method === "GET") { + context.sendJson(context.response, 200, { + sessions: [...this.sessions.values()].map((session) => this.sessionSummary(session, context.endpoint)) + }); + return; + } + + if (context.request.method !== "POST") { + context.sendJson(context.response, 405, { error: { message: "Method not allowed." } }); + return; + } + + const body = await this.readJsonBody(context); + if (!body) { + return; + } + const id = sanitizeSessionId(readString(body.id) || readString(body.sessionId)) || randomUUID(); + const title = readString(body.title) || readString(body.name) || `CCR Remote ${id.slice(0, 8)}`; + const metadata = readRecord(body.metadata) ?? {}; + const session = this.ensureSession(id, title, metadata); + this.appendEvent(session, { + direction: "system", + payload: { title }, + source: "ccr-gateway", + type: "session.created" + }); + + context.sendJson(context.response, 201, { + session: this.sessionSnapshot(session, context.endpoint) + }); + } + + private async handleSessionRequest(context: CcrRemoteControlRequestContext, sessionId: string): Promise { + const session = this.sessions.get(sessionId); + if (!session) { + context.sendJson(context.response, 404, { error: { message: "Remote session not found." } }); + return; + } + + if (context.request.method === "GET") { + context.sendJson(context.response, 200, { session: this.sessionSnapshot(session, context.endpoint) }); + return; + } + + if (context.request.method === "PATCH") { + const body = await this.readJsonBody(context); + if (!body) { + return; + } + const title = readString(body.title) || readString(body.name); + if (title) { + session.title = title; + } + const metadata = readRecord(body.metadata); + if (metadata) { + session.metadata = { ...session.metadata, ...metadata }; + } + session.updatedAt = new Date().toISOString(); + this.appendEvent(session, { + direction: "system", + payload: { metadata: metadata ?? {}, title: title ?? session.title }, + source: "ccr-gateway", + type: "session.updated" + }); + context.sendJson(context.response, 200, { session: this.sessionSnapshot(session, context.endpoint) }); + return; + } + + if (context.request.method === "DELETE") { + session.archivedAt = new Date().toISOString(); + session.updatedAt = session.archivedAt; + this.appendEvent(session, { + direction: "system", + payload: { archivedAt: session.archivedAt }, + source: "ccr-gateway", + type: "session.archived" + }); + context.sendJson(context.response, 200, { archived: true, session: this.sessionSummary(session, context.endpoint) }); + return; + } + + context.sendJson(context.response, 405, { error: { message: "Method not allowed." } }); + } + + private async handleEventsRequest(context: CcrRemoteControlRequestContext, sessionId: string): Promise { + const session = this.sessions.get(sessionId); + if (!session) { + context.sendJson(context.response, 404, { error: { message: "Remote session not found." } }); + return; + } + + if (context.request.method === "GET") { + const after = remoteAfterSeq(context.request); + if (wantsSse(context.request)) { + this.openSse(context, session, "events", after); + return; + } + context.sendJson(context.response, 200, { + events: session.events.filter((event) => event.seq > after), + session: this.sessionSummary(session, context.endpoint) + }); + return; + } + + if (context.request.method !== "POST") { + context.sendJson(context.response, 405, { error: { message: "Method not allowed." } }); + return; + } + + const body = await this.readJsonBody(context); + if (!body) { + return; + } + const events = normalizeEventInputs(body).map((event) => + this.appendEvent(session, { + ...event, + direction: event.direction ?? "local", + type: event.type || "message" + }) + ); + context.sendJson(context.response, 202, { + accepted: events.length, + events, + session: this.sessionSummary(session, context.endpoint) + }); + } + + private async handleInboundRequest(context: CcrRemoteControlRequestContext, sessionId: string): Promise { + const session = this.sessions.get(sessionId); + if (!session) { + context.sendJson(context.response, 404, { error: { message: "Remote session not found." } }); + return; + } + + if (context.request.method === "GET") { + const after = remoteAfterSeq(context.request); + if (wantsSse(context.request)) { + this.openSse(context, session, "inbound", after); + return; + } + context.sendJson(context.response, 200, { + events: session.inboundEvents.filter((event) => event.seq > after), + session: this.sessionSummary(session, context.endpoint) + }); + return; + } + + if (context.request.method !== "POST") { + context.sendJson(context.response, 405, { error: { message: "Method not allowed." } }); + return; + } + + const body = await this.readJsonBody(context); + if (!body) { + return; + } + const events = normalizeEventInputs(body).map((event) => + this.appendEvent(session, { + ...event, + direction: "remote", + type: event.type || "user.message" + }, true) + ); + context.sendJson(context.response, 202, { + accepted: events.length, + events, + session: this.sessionSummary(session, context.endpoint) + }); + } + + private async handlePresenceRequest(context: CcrRemoteControlRequestContext, sessionId: string): Promise { + const session = this.sessions.get(sessionId); + if (!session) { + context.sendJson(context.response, 404, { error: { message: "Remote session not found." } }); + return; + } + + if (context.request.method === "GET") { + context.sendJson(context.response, 200, { presence: session.presence }); + return; + } + + if (context.request.method !== "POST") { + context.sendJson(context.response, 405, { error: { message: "Method not allowed." } }); + return; + } + + const body = await this.readJsonBody(context); + if (!body) { + return; + } + const clientId = sanitizeSessionId(readString(body.clientId) || readString(body.id)) || randomUUID(); + const presence: RemotePresence = { + lastSeenAt: new Date().toISOString(), + metadata: readRecord(body.metadata) ?? {}, + name: readString(body.name) || clientId, + role: readString(body.role) || "client" + }; + session.presence[clientId] = presence; + const event = this.appendEvent(session, { + direction: "system", + payload: { clientId, presence }, + source: "ccr-gateway", + type: "presence.updated" + }); + context.sendJson(context.response, 202, { event, presence: session.presence }); + } + + private ensureSession(id: string, title: string, metadata: Record): RemoteSession { + const existing = this.sessions.get(id); + if (existing) { + existing.metadata = { ...existing.metadata, ...metadata }; + existing.title = title || existing.title; + existing.updatedAt = new Date().toISOString(); + return existing; + } + + this.pruneSessions(); + const now = new Date().toISOString(); + const session: RemoteSession = { + createdAt: now, + events: [], + id, + inboundEvents: [], + lastSeq: 0, + metadata, + presence: {}, + seenDedupeKeys: new Map(), + subscribers: new Set(), + title, + updatedAt: now + }; + this.sessions.set(id, session); + return session; + } + + private appendEvent( + session: RemoteSession, + input: RemoteEventInput, + inbound = false + ): RemoteEvent { + const dedupeKey = input.dedupeKey || input.id; + if (dedupeKey) { + const duplicate = session.seenDedupeKeys.get(dedupeKey); + if (duplicate) { + return duplicate; + } + } + + const event: RemoteEvent = { + createdAt: new Date().toISOString(), + ...(dedupeKey ? { dedupeKey } : {}), + direction: input.direction ?? "local", + id: input.id || randomUUID(), + payload: input.payload ?? {}, + ...(input.role ? { role: input.role } : {}), + seq: ++session.lastSeq, + sessionId: session.id, + ...(input.source ? { source: input.source } : {}), + ...(input.text ? { text: input.text } : {}), + type: input.type || "message" + }; + + session.events.push(event); + trimArray(session.events, maxEventsPerSession); + if (inbound || event.direction === "remote" || event.direction === "inbound") { + session.inboundEvents.push(event); + trimArray(session.inboundEvents, maxInboundEventsPerSession); + } + if (dedupeKey) { + session.seenDedupeKeys.set(dedupeKey, event); + while (session.seenDedupeKeys.size > maxEventsPerSession) { + const oldest = session.seenDedupeKeys.keys().next().value; + if (!oldest) { + break; + } + session.seenDedupeKeys.delete(oldest); + } + } + session.updatedAt = event.createdAt; + this.broadcast(session, event); + return event; + } + + private openSse( + context: CcrRemoteControlRequestContext, + session: RemoteSession, + kind: RemoteSubscriber["kind"], + after: number + ): void { + context.response.writeHead(200, { + "cache-control": "no-cache, no-transform", + "connection": "keep-alive", + "content-type": "text/event-stream; charset=utf-8", + "x-accel-buffering": "no" + }); + context.response.write(": connected\n\n"); + const source = kind === "events" ? session.events : session.inboundEvents; + for (const event of source.filter((item) => item.seq > after)) { + writeSseEvent(context.response, event); + } + + const heartbeat = setInterval(() => { + if (!context.response.destroyed) { + context.response.write(": keepalive\n\n"); + } + }, sseHeartbeatMs); + const subscriber: RemoteSubscriber = { + close: () => clearInterval(heartbeat), + id: randomUUID(), + kind, + response: context.response + }; + session.subscribers.add(subscriber); + context.request.once("close", () => { + subscriber.close(); + session.subscribers.delete(subscriber); + }); + } + + private broadcast(session: RemoteSession, event: RemoteEvent): void { + for (const subscriber of session.subscribers) { + if (subscriber.kind === "inbound" && !(event.direction === "remote" || event.direction === "inbound")) { + continue; + } + if (subscriber.response.destroyed) { + subscriber.close(); + session.subscribers.delete(subscriber); + continue; + } + writeSseEvent(subscriber.response, event); + } + } + + private sessionSnapshot(session: RemoteSession, endpoint: string) { + return { + ...this.sessionSummary(session, endpoint), + events: session.events, + inboundEvents: session.inboundEvents, + metadata: session.metadata, + presence: session.presence + }; + } + + private sessionSummary(session: RemoteSession, endpoint: string) { + return { + ...(session.archivedAt ? { archivedAt: session.archivedAt } : {}), + createdAt: session.createdAt, + endpoints: { + events: `${endpoint}${ccrRemoteControlPathPrefix}/sessions/${encodeURIComponent(session.id)}/events`, + inbound: `${endpoint}${ccrRemoteControlPathPrefix}/sessions/${encodeURIComponent(session.id)}/inbound`, + presence: `${endpoint}${ccrRemoteControlPathPrefix}/sessions/${encodeURIComponent(session.id)}/presence` + }, + eventCount: session.events.length, + id: session.id, + inboundCount: session.inboundEvents.length, + lastSeq: session.lastSeq, + subscriberCount: session.subscribers.size, + title: session.title, + updatedAt: session.updatedAt + }; + } + + private pruneSessions(): void { + while (this.sessions.size >= maxSessions) { + const oldest = [...this.sessions.values()] + .sort((left, right) => Date.parse(left.updatedAt) - Date.parse(right.updatedAt))[0]; + if (!oldest) { + return; + } + for (const subscriber of oldest.subscribers) { + subscriber.close(); + subscriber.response.end(); + } + this.sessions.delete(oldest.id); + } + } + + private async readJsonBody(context: CcrRemoteControlRequestContext): Promise | undefined> { + const body = await context.readBody(context.request); + if (body.length === 0) { + return {}; + } + try { + const parsed = JSON.parse(body.toString("utf8")) as unknown; + if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) { + return parsed as Record; + } + context.sendJson(context.response, 400, { error: { message: "Request body must be a JSON object." } }); + return undefined; + } catch { + context.sendJson(context.response, 400, { error: { message: "Request body must be valid JSON." } }); + return undefined; + } + } +} + +type RemoteEventInput = { + dedupeKey?: string; + direction?: RemoteDirection; + id?: string; + payload?: unknown; + role?: string; + source?: string; + text?: string; + type?: string; +}; + +export const ccrRemoteControlService = new CcrRemoteControlService(); + +function normalizeEventInputs(body: Record): RemoteEventInput[] { + const rawEvents = Array.isArray(body.events) ? body.events : [body]; + return rawEvents + .filter((event): event is Record => typeof event === "object" && event !== null && !Array.isArray(event)) + .map((event) => { + const payload = Object.prototype.hasOwnProperty.call(event, "payload") + ? event.payload + : Object.prototype.hasOwnProperty.call(event, "message") + ? event.message + : {}; + return { + dedupeKey: readString(event.dedupeKey) || readString(event.uuid) || readString(event.requestId), + direction: readDirection(event.direction), + id: readString(event.id), + payload, + role: readString(event.role), + source: readString(event.source), + text: readString(event.text) || readString(event.content), + type: readString(event.type) + }; + }); +} + +function writeSseEvent(response: ServerResponse, event: RemoteEvent): void { + response.write(`id: ${event.seq}\n`); + response.write(`event: ${event.type.replace(/[\r\n]+/g, "") || "message"}\n`); + response.write(`data: ${JSON.stringify(event)}\n\n`); +} + +function wantsSse(request: IncomingMessage): boolean { + const url = new URL(request.url || "/", "http://127.0.0.1"); + return url.searchParams.get("stream") === "1" || + url.searchParams.get("stream") === "true" || + readHeader(request.headers.accept)?.toLowerCase().includes("text/event-stream") === true; +} + +function remoteAfterSeq(request: IncomingMessage): number { + const url = new URL(request.url || "/", "http://127.0.0.1"); + const after = Number(url.searchParams.get("after") ?? readHeader(request.headers["last-event-id"]) ?? 0); + return Number.isFinite(after) && after > 0 ? Math.floor(after) : 0; +} + +function remotePathSegments(path: string): string[] { + const suffix = path.slice(ccrRemoteControlPathPrefix.length).replace(/^\/+|\/+$/g, ""); + if (!suffix) { + return []; + } + return suffix.split("/").map((segment) => decodeURIComponent(segment)).filter(Boolean); +} + +function readDirection(value: unknown): RemoteDirection | undefined { + return value === "inbound" || value === "local" || value === "remote" || value === "system" + ? value + : undefined; +} + +function readHeader(value: string | string[] | undefined): string | undefined { + if (Array.isArray(value)) { + return value[0]?.trim(); + } + return typeof value === "string" && value.trim() ? value.trim() : undefined; +} + +function readRecord(value: unknown): Record | undefined { + return typeof value === "object" && value !== null && !Array.isArray(value) + ? value as Record + : undefined; +} + +function readString(value: unknown): string | undefined { + return typeof value === "string" && value.trim() ? value.trim() : undefined; +} + +function sanitizeSessionId(value: string | undefined): string | undefined { + const sanitized = value?.trim().replace(/[^a-zA-Z0-9:._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 128); + return sanitized || undefined; +} + +function trimArray(items: T[], maxLength: number): void { + if (items.length > maxLength) { + items.splice(0, items.length - maxLength); + } +} diff --git a/src/server/gateway/service.ts b/src/server/gateway/service.ts index 799226d..71c2122 100644 --- a/src/server/gateway/service.ts +++ b/src/server/gateway/service.ts @@ -1,5 +1,5 @@ import { spawn, type ChildProcess } from "node:child_process"; -import { randomBytes, randomUUID } from "node:crypto"; +import { createHash, randomBytes, randomUUID } from "node:crypto"; import { createServer, type IncomingHttpHeaders, type IncomingMessage, type Server, type ServerResponse } from "node:http"; import { createRequire } from "node:module"; import { networkInterfaces } from "node:os"; @@ -47,6 +47,7 @@ import { proxyService } from "../proxy/service"; import { createSseErrorDetector, recordGatewayRequestLog, updateGatewayRequestLogFromRawTrace, type RequestLogRawTraceUpdateInput } from "../../main/request-log-store"; import { recordGatewayUsageCapture } from "../../main/usage-store"; import { ClaudeCodeRouterPlugin, normalizeRouteSelector } from "./claude-code-router-plugin"; +import { ccrRemoteControlPathPrefix, ccrRemoteControlService } from "./remote-control-service"; import { claudeCodeEffectiveMaxInputTokens, findModelCatalogEntry, @@ -428,6 +429,22 @@ class GatewayService { return; } + if (path === ccrRemoteControlPathPrefix || path.startsWith(`${ccrRemoteControlPathPrefix}/`)) { + const authorization = authorize(request, response, this.config); + if (!authorization.ok) { + return; + } + await ccrRemoteControlService.handleRequest({ + endpoint: this.status.endpoint, + path, + readBody: readRequestBody, + request, + response, + sendJson + }); + return; + } + if (isNetworkCaptureMcpPath(path)) { if (!this.config.proxy.captureNetwork) { sendJson(response, 404, { error: { message: "Network capture MCP is disabled." } }); @@ -559,6 +576,7 @@ class GatewayService { fallbackModel: routedModel, method, path, + providerName: resolveProviderLogName(responseHeaders, config, routedModel), providerProtocol: resolveResponseProviderProtocol(responseHeaders, this.config), requestBody: shouldSendBody(method) ? bodyToForward ?? Buffer.alloc(0) : Buffer.alloc(0), requestHeaders: headers, @@ -653,6 +671,7 @@ class GatewayService { fallbackModel: routedModel, method, path, + providerName: resolveProviderLogName(new Headers(), this.config, routedModel), providerProtocol: resolveResponseProviderProtocol(new Headers(), this.config), requestId, responseHeaders: new Headers(), @@ -681,6 +700,7 @@ class GatewayService { fallbackModel: routedModel, method, path, + providerName: resolveProviderLogName(responseHeaders, this.config, routedModel), providerProtocol: resolveResponseProviderProtocol(responseHeaders, this.config), requestId, responseHeaders, @@ -732,6 +752,7 @@ class GatewayService { fallbackModel: routedModel, method, path, + providerName: resolveProviderLogName(responseHeaders, this.config, routedModel), providerProtocol: resolveResponseProviderProtocol(responseHeaders, this.config), requestId, responseHeaders, @@ -1624,7 +1645,7 @@ function rewriteProviderListHeader( function rewriteProviderSelectorForProtocol(value: string, config: AppConfig, protocol: GatewayProviderProtocol): string { const provider = findProviderByPublicOrInternalName(config, value); const capability = provider ? providerCapabilityForClientProtocol(provider, protocol) : undefined; - return provider && capability ? providerCapabilityInternalName(provider.name, capability.type) : value; + return provider && capability ? providerCapabilityInternalName(provider, capability.type) : value; } function rewriteFallbackForProtocol(fallback: RouterFallbackConfig, config: AppConfig, protocol: GatewayProviderProtocol): RouterFallbackConfig { @@ -1669,7 +1690,7 @@ function rewriteModelSelectorForProtocol( const targetModel = publicModel.slice(separator + 1).trim(); const provider = findProviderByPublicOrInternalName(config, providerName); const capability = provider ? providerCapabilityForClientProtocol(provider, protocol) : undefined; - return provider && capability ? `${providerCapabilityInternalName(provider.name, capability.type)}/${targetModel}` : publicModel; + return provider && capability ? `${providerCapabilityInternalName(provider, capability.type)}/${targetModel}` : publicModel; } function providerCapabilityForClientProtocol( @@ -1733,15 +1754,19 @@ function findProviderByPublicOrInternalName(config: AppConfig, name: string): Ga } const credentialInternalName = parseProviderCredentialInternalName(name); if (credentialInternalName) { + const internalProviderId = credentialInternalName.providerId.toLowerCase(); return config.Providers.find((provider) => - provider.name.trim().toLowerCase() === credentialInternalName.providerName.toLowerCase() + provider.name.trim().toLowerCase() === internalProviderId || + providerRuntimeId(provider).toLowerCase() === internalProviderId ); } return config.Providers.find((provider) => provider.name.trim().toLowerCase() === normalized || + provider.id?.trim().toLowerCase() === normalized || provider.provider?.trim().toLowerCase() === normalized || + providerRuntimeId(provider).toLowerCase() === normalized || normalizedProviderCapabilities(provider).some((capability) => - providerCapabilityInternalName(provider.name, capability.type).toLowerCase() === normalized + providerCapabilityNameMatches(provider, capability.type, normalized) ) ); } @@ -1753,27 +1778,27 @@ function rewriteCapabilityResponseHeaders(headers: Headers, config: AppConfig): } const credentialInternalName = parseProviderCredentialInternalName(providerName); if (credentialInternalName) { - const provider = findProviderByPublicOrInternalName(config, credentialInternalName.providerName); + const provider = findProviderByPublicOrInternalName(config, credentialInternalName.providerId); if (!provider) { return headers; } const credential = findProviderCredentialBySlug(provider, credentialInternalName.credentialSlug); const rewritten = new Headers(headers); - rewritten.set("x-gateway-target-provider-name", provider.name); + rewritten.set("x-gateway-target-provider-name", providerRuntimeId(provider)); rewritten.set("x-ccr-provider-protocol", credentialInternalName.protocol); - rewritten.set("x-ccr-provider-credential-provider", provider.name); - rewritten.set("x-ccr-provider-credential-id", credential ? providerCredentialRuntimeId(provider, credential) : credentialInternalName.credentialSlug); + rewritten.set("x-ccr-provider-credential-provider", providerRuntimeId(provider)); + rewritten.set("x-ccr-provider-credential-id", providerCredentialSlug(credential ? providerCredentialRuntimeId(provider, credential) : credentialInternalName.credentialSlug)); return rewritten; } const provider = findProviderByPublicOrInternalName(config, providerName); - if (!provider || provider.name === providerName) { + if (!provider) { return headers; } const capability = normalizedProviderCapabilities(provider).find((item) => - providerCapabilityInternalName(provider.name, item.type).toLowerCase() === providerName.toLowerCase() + providerCapabilityNameMatches(provider, item.type, providerName) ); const rewritten = new Headers(headers); - rewritten.set("x-gateway-target-provider-name", provider.name); + rewritten.set("x-gateway-target-provider-name", providerRuntimeId(provider)); if (capability) { rewritten.set("x-ccr-provider-protocol", capability.type); } @@ -2008,7 +2033,7 @@ function selectProviderCredentials( return { cooldown, credential, - credentialId: providerCredentialRuntimeId(provider, credential, providerIndex), + credentialId: providerCredentialSlug(providerCredentialRuntimeId(provider, credential, providerIndex)), index: providerIndex, internalName: providerCredentialInternalName(provider, protocol, credential), limitState, @@ -2378,8 +2403,8 @@ function toCoreGatewayProvider( name: credential ? providerCredentialInternalName(provider, type, credential) : capability - ? providerCapabilityInternalName(provider.name, type) - : provider.name, + ? providerCapabilityInternalName(provider, type) + : providerRuntimeId(provider), type }; } @@ -2415,21 +2440,56 @@ function normalizedProviderCapabilities(provider: GatewayProviderConfig): Gatewa return normalized; } -function providerCapabilityInternalName(providerName: string, protocol: GatewayProviderProtocol): string { +function providerCapabilityInternalName(provider: GatewayProviderConfig, protocol: GatewayProviderProtocol): string { + return `${providerRuntimeId(provider)}::${protocol}`; +} + +function providerCapabilityLegacyInternalName(providerName: string, protocol: GatewayProviderProtocol): string { return `${providerName}::${protocol}`; } +function providerCapabilityNameMatches(provider: GatewayProviderConfig, protocol: GatewayProviderProtocol, value: string): boolean { + const normalized = value.trim().toLowerCase(); + return providerCapabilityInternalName(provider, protocol).toLowerCase() === normalized || + providerCapabilityLegacyInternalName(provider.name, protocol).toLowerCase() === normalized; +} + +function providerRuntimeId(provider: GatewayProviderConfig): string { + const explicit = sanitizeProviderHeaderId(provider.id); + if (explicit) { + return explicit; + } + const normalized = provider.name + .normalize("NFKD") + .replace(/[\u0300-\u036f]/g, "") + .toLowerCase() + .replace(/[^a-z0-9_.-]+/g, "-") + .replace(/^-+|-+$/g, "") + .slice(0, 48); + const hash = createHash("sha256").update(`${provider.name}\n${readBaseUrl(provider) ?? ""}`).digest("hex").slice(0, 10); + return `provider-${normalized || "provider"}-${hash}`; +} + +function sanitizeProviderHeaderId(value: string | undefined): string | undefined { + const normalized = value + ?.trim() + .toLowerCase() + .replace(/[^a-z0-9_.-]+/g, "-") + .replace(/^-+|-+$/g, ""); + return normalized || undefined; +} + function providerCredentialInternalName( provider: GatewayProviderConfig, protocol: GatewayProviderProtocol, credential: ProviderCredentialConfig ): string { - return `${providerCapabilityInternalName(provider.name, protocol)}::cred:${providerCredentialSlug(providerCredentialRuntimeId(provider, credential))}`; + return `${providerCapabilityInternalName(provider, protocol)}::cred:${providerCredentialSlug(providerCredentialRuntimeId(provider, credential))}`; } function parseProviderCredentialInternalName(value: string | undefined): { credentialSlug: string; - providerName: string; + providerId: string; protocol: GatewayProviderProtocol; } | undefined { const marker = "::cred:"; @@ -2444,8 +2504,8 @@ function parseProviderCredentialInternalName(value: string | undefined): { return undefined; } const protocol = normalizeProviderProtocol(baseName.slice(protocolSeparator + 2)); - const providerName = baseName.slice(0, protocolSeparator).trim(); - return protocol && providerName ? { credentialSlug, providerName, protocol } : undefined; + const providerId = baseName.slice(0, protocolSeparator).trim(); + return protocol && providerId ? { credentialSlug, providerId, protocol } : undefined; } function providerCredentialSlug(value: string | undefined): string { @@ -2550,7 +2610,7 @@ function resolveResponseProviderProtocol(headers: Headers, config: AppConfig | u return normalizeProviderProtocol(providerName); } const capability = normalizedProviderCapabilities(provider).find((item) => - providerCapabilityInternalName(provider.name, item.type).toLowerCase() === providerName.toLowerCase() + providerCapabilityNameMatches(provider, item.type, providerName) ); if (capability) { return capability.type; @@ -2558,9 +2618,27 @@ function resolveResponseProviderProtocol(headers: Headers, config: AppConfig | u return normalizeProviderProtocol(provider.type) ?? normalizeProviderProtocol(provider.provider) ?? inferProtocol(provider); } +function resolveProviderLogName(headers: Headers, config: AppConfig | undefined, fallbackModel?: string): string | undefined { + const providerSelector = + headers.get("x-gateway-target-provider-name")?.trim() || + headers.get("x-gateway-target-provider")?.trim(); + const headerProvider = providerSelector && config + ? findProviderByPublicOrInternalName(config, providerSelector) + : undefined; + if (headerProvider) { + return headerProvider.name; + } + + const routeProvider = parseProviderModelSelector(fallbackModel)?.provider; + const modelProvider = routeProvider && config + ? findProviderByPublicOrInternalName(config, routeProvider) + : undefined; + return modelProvider?.name; +} + function providerMatchesName(provider: GatewayProviderConfig, name: string): boolean { const normalizedName = name.trim().toLowerCase(); - return [provider.name, provider.provider] + return [provider.id, provider.name, provider.provider] .filter((value): value is string => typeof value === "string" && value.trim().length > 0) .some((value) => value.trim().toLowerCase() === normalizedName); } @@ -2862,7 +2940,7 @@ function shouldServeGatewayRequest(config: AppConfig, request: IncomingMessage): function applyCors(response: ServerResponse, config?: AppConfig): void { const origin = config ? endpoint(config.gateway.host, config.gateway.port) : "*"; response.setHeader("Access-Control-Allow-Origin", origin); - response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, X-API-Key, Anthropic-Version, Anthropic-Beta, Mcp-Session-Id, MCP-Protocol-Version"); + response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, X-API-Key, Last-Event-ID, Anthropic-Version, Anthropic-Beta, Mcp-Session-Id, MCP-Protocol-Version"); response.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS"); response.setHeader("Access-Control-Expose-Headers", "Mcp-Session-Id"); } @@ -2873,7 +2951,7 @@ function authorize(request: IncomingMessage, response: ServerResponse, config: A return { ok: true }; } - const token = readAuthToken(request.headers); + const token = readAuthToken(request.headers) || readRemoteControlQueryAuthToken(request); const apiKey = token ? apiKeys.find((item) => item.key === token) : undefined; if (apiKey) { if (isApiKeyExpired(apiKey)) { @@ -3920,6 +3998,14 @@ function readAuthToken(headers: IncomingHttpHeaders): string | undefined { return raw.toLowerCase().startsWith("bearer ") ? raw.slice(7).trim() : raw; } +function readRemoteControlQueryAuthToken(request: IncomingMessage): string | undefined { + const url = new URL(request.url || "/", "http://127.0.0.1"); + if (url.pathname !== ccrRemoteControlPathPrefix && !url.pathname.startsWith(`${ccrRemoteControlPathPrefix}/`)) { + return undefined; + } + return url.searchParams.get("api_key")?.trim() || url.searchParams.get("key")?.trim() || undefined; +} + function forwardHeaders(headers: IncomingHttpHeaders): Record { const forwarded: Record = {}; for (const [key, value] of Object.entries(headers)) { diff --git a/src/shared/app.ts b/src/shared/app.ts index f201679..fd29035 100644 --- a/src/shared/app.ts +++ b/src/shared/app.ts @@ -77,6 +77,7 @@ export type GatewayProviderConfig = { extraBody?: unknown; extraHeaders?: unknown; icon?: string; + id?: string; models: string[]; name: string; provider?: string; From 8d306c562fb9fa77f922c837d8d516aab656f9a4 Mon Sep 17 00:00:00 2001 From: musistudio Date: Mon, 29 Jun 2026 10:28:58 +0800 Subject: [PATCH 05/94] Add CLI web service support and install docs --- README.md | 22 + README_zh.md | 22 + build/build.mjs | 3 +- build/dev.mjs | 28 +- build/esbuild.config.mjs | 58 +- package-lock.json | 3 + package.json | 32 +- src/main/claude-app-gateway-service.ts | 25 +- src/main/cli.ts | 571 +++++++++++-- src/main/electron-node-shim.ts | 14 + src/main/profile-launch-core.ts | 2 +- src/main/web-client-bridge.ts | 112 +++ src/main/web-management-server.ts | 1014 ++++++++++++++++++++++++ src/server/mcp/network-capture-mcp.ts | 9 +- src/server/proxy/service.ts | 21 +- 15 files changed, 1837 insertions(+), 99 deletions(-) create mode 100644 src/main/electron-node-shim.ts create mode 100644 src/main/web-client-bridge.ts create mode 100644 src/main/web-management-server.ts diff --git a/README.md b/README.md index e53da3b..c123f22 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,28 @@ CCR runs on your machine, keeps provider configuration in your local config dire ## Download And Install +### npm CLI + +Install the CLI package when you want to run CCR without the desktop tray or `ccr://` protocol integration: + +```bash +npm install -g claude-code-router +ccr start +``` + +Common commands: + +```bash +ccr start # start the background CCR service and web management UI +ccr stop # stop the background CCR service +ccr cli # launch the saved profile as a CLI +ccr app # launch the saved profile as an app +``` + +The web management UI listens on `http://127.0.0.1:3458` by default. Use `ccr start --host --port ` to change it. + +### Desktop App + 1. Open the [GitHub Releases page](https://github.com/musistudio/claude-code-router/releases). 2. Download the package for your platform: - macOS: `Claude Code Router_.dmg` or `.zip` diff --git a/README_zh.md b/README_zh.md index dfe6b3a..45f98cf 100644 --- a/README_zh.md +++ b/README_zh.md @@ -38,6 +38,28 @@ CCR 在你的本机运行,Provider 配置保存在本地配置目录,并默 ## 下载和安装 +### npm CLI + +如果你只需要 CLI 版本,不需要桌面 Tray 或 `ccr://` 协议集成,可以安装 npm 包: + +```bash +npm install -g claude-code-router +ccr start +``` + +常用命令: + +```bash +ccr start # 后台启动 CCR 服务和 Web 管理端 +ccr stop # 停止后台 CCR 服务 +ccr cli # 以 CLI 方式启动保存的 profile +ccr app # 以 App 方式启动保存的 profile +``` + +Web 管理端默认监听 `http://127.0.0.1:3458`。可以用 `ccr start --host --port ` 修改监听地址。 + +### 桌面应用 + 1. 打开 [GitHub Releases 页面](https://github.com/musistudio/claude-code-router/releases)。 2. 按系统下载对应安装包: - macOS:`Claude Code Router_.dmg` 或 `.zip` diff --git a/build/build.mjs b/build/build.mjs index e0bc9d4..a07412b 100644 --- a/build/build.mjs +++ b/build/build.mjs @@ -1,4 +1,4 @@ -import { buildBrowserRenderer, buildMain, buildRenderer, buildStyles, buildTrayRenderer, cleanDist, copyAppAssets, copyBrowserRendererHtml, copyMarketplacePlugins, copyModelCatalog, copyRendererHtml, copyTrayRendererHtml } from "./esbuild.config.mjs"; +import { buildBrowserRenderer, buildMain, buildRenderer, buildStyles, buildTrayRenderer, buildWebClientBridge, cleanDist, copyAppAssets, copyBrowserRendererHtml, copyMarketplacePlugins, copyModelCatalog, copyRendererHtml, copyTrayRendererHtml } from "./esbuild.config.mjs"; const mode = process.argv.includes("--dev") ? "development" : "production"; @@ -15,6 +15,7 @@ await Promise.all([ buildBrowserRenderer({ mode }), buildRenderer({ mode }), buildTrayRenderer({ mode }), + buildWebClientBridge({ mode }), buildStyles({ minify: mode === "production" }) ]); diff --git a/build/dev.mjs b/build/dev.mjs index 0511592..a7cf8fd 100644 --- a/build/dev.mjs +++ b/build/dev.mjs @@ -16,9 +16,11 @@ import { copyRendererHtml, copyTrayRendererHtml, createBrowserRendererBuildOptions, + createCliBuildOptions, createMainBuildOptions, createRendererBuildOptions, createTrayRendererBuildOptions, + createWebClientBridgeBuildOptions, cssInput, cssOutput, appAssetsInput, @@ -38,9 +40,11 @@ const restartDelayMs = 160; const ignoredSignatureEntries = new Set([".DS_Store"]); const ready = { browser: false, + cli: false, main: false, renderer: false, - tray: false + tray: false, + webBridge: false }; function logDev(message) { @@ -163,11 +167,11 @@ function handleWatchedInput(label, watchedPath, eventType, filename, options, on } function markReady(name, reason = `${name} esbuild completed`) { - if (name === "browser" || name === "main" || name === "renderer" || name === "tray") { + if (name === "browser" || name === "cli" || name === "main" || name === "renderer" || name === "tray" || name === "webBridge") { ready[name] = true; } logDev(`build ready: ${reason}; ${readyState()}`); - if (ready.browser && ready.main && ready.renderer && ready.tray) { + if (ready.browser && ready.cli && ready.main && ready.renderer && ready.tray && ready.webBridge) { scheduleRestart(reason); } } @@ -274,6 +278,13 @@ const mainContext = await esbuild.context( }) ); +const cliContext = await esbuild.context( + createCliBuildOptions({ + mode: "development", + plugins: [watchPlugin("cli", (name) => markReady(name))] + }) +); + const rendererContext = await esbuild.context( createRendererBuildOptions({ mode: "development", @@ -310,7 +321,14 @@ const browserRendererContext = await esbuild.context( }) ); -await Promise.all([mainContext.watch(), rendererContext.watch(), trayRendererContext.watch(), browserRendererContext.watch()]); +const webClientBridgeContext = await esbuild.context( + createWebClientBridgeBuildOptions({ + mode: "development", + plugins: [watchPlugin("webBridge", (name) => markReady(name))] + }) +); + +await Promise.all([mainContext.watch(), cliContext.watch(), rendererContext.watch(), trayRendererContext.watch(), browserRendererContext.watch(), webClientBridgeContext.watch()]); logDev("watchers are active"); async function shutdown() { @@ -328,7 +346,7 @@ async function shutdown() { trayHtmlWatcher.close(); appAssetsWatcher.close(); modelCatalogWatcher.close(); - await Promise.all([mainContext.dispose(), rendererContext.dispose(), trayRendererContext.dispose(), browserRendererContext.dispose()]); + await Promise.all([mainContext.dispose(), cliContext.dispose(), rendererContext.dispose(), trayRendererContext.dispose(), browserRendererContext.dispose(), webClientBridgeContext.dispose()]); process.exit(0); } diff --git a/build/esbuild.config.mjs b/build/esbuild.config.mjs index 3507935..3283c23 100644 --- a/build/esbuild.config.mjs +++ b/build/esbuild.config.mjs @@ -26,6 +26,7 @@ export const trayRendererHtmlInput = path.join(rendererRoot, "pages", "tray", "i export const trayRendererHtmlOutput = path.join(rendererOutDir, "pages", "tray", "index.html"); export const cssInput = path.join(rendererRoot, "styles", "globals.css"); export const cssOutput = path.join(rendererAssetsDir, "main.css"); +export const webClientBridgeOutput = path.join(rendererAssetsDir, "web-client-bridge.js"); const nodeExternals = [ "electron", @@ -110,7 +111,6 @@ export function createMainBuildOptions({ mode = "production", plugins = [] } = { entryPoints: [ path.join(projectRoot, "src", "main", "main.ts"), path.join(projectRoot, "src", "main", "browser-preload.ts"), - path.join(projectRoot, "src", "main", "cli.ts"), path.join(projectRoot, "src", "server", "mcp", "fusion-vision-mcp.ts"), path.join(projectRoot, "src", "main", "preload.ts") ], @@ -127,6 +127,25 @@ export function createMainBuildOptions({ mode = "production", plugins = [] } = { }; } +export function createCliBuildOptions({ mode = "production", plugins = [] } = {}) { + return { + absWorkingDir: projectRoot, + bundle: true, + entryNames: "[name]", + entryPoints: [path.join(projectRoot, "src", "main", "cli.ts")], + external: nodeExternals.filter((moduleName) => moduleName !== "electron"), + format: "cjs", + legalComments: "none", + logLevel: "info", + minify: mode === "production", + outdir: mainOutDir, + platform: "node", + plugins: [electronNodeShimPlugin(), ...plugins], + sourcemap: mode !== "production", + target: "node22" + }; +} + export function createRendererBuildOptions({ mode = "production", plugins = [] } = {}) { return { absWorkingDir: projectRoot, @@ -175,6 +194,23 @@ export function createBrowserRendererBuildOptions({ mode = "production", plugins }; } +export function createWebClientBridgeBuildOptions({ mode = "production", plugins = [] } = {}) { + return { + absWorkingDir: projectRoot, + bundle: true, + entryPoints: [path.join(projectRoot, "src", "main", "web-client-bridge.ts")], + format: "iife", + legalComments: "none", + logLevel: "info", + minify: mode === "production", + outfile: webClientBridgeOutput, + platform: "browser", + plugins, + sourcemap: mode !== "production", + target: "chrome120" + }; +} + export function watchPlugin(name, onEnd) { return { name: `${name}-watch`, @@ -189,7 +225,10 @@ export function watchPlugin(name, onEnd) { } export async function buildMain(options = {}) { - await esbuild.build(createMainBuildOptions(options)); + await Promise.all([ + esbuild.build(createMainBuildOptions(options)), + esbuild.build(createCliBuildOptions(options)) + ]); } export async function buildRenderer(options = {}) { @@ -204,6 +243,10 @@ export async function buildBrowserRenderer(options = {}) { await esbuild.build(createBrowserRendererBuildOptions(options)); } +export async function buildWebClientBridge(options = {}) { + await esbuild.build(createWebClientBridgeBuildOptions(options)); +} + export async function buildStyles({ minify = false } = {}) { ensureDist(); const args = ["-i", cssInput, "-o", cssOutput]; @@ -249,6 +292,17 @@ function rendererAliasPlugin() { }; } +function electronNodeShimPlugin() { + return { + name: "electron-node-shim", + setup(build) { + build.onResolve({ filter: /^electron$/ }, () => { + return { path: path.join(projectRoot, "src", "main", "electron-node-shim.ts") }; + }); + } + }; +} + function resolveRendererImport(importPath) { const basePath = path.resolve(rendererRoot, importPath); const candidates = [ diff --git a/package-lock.json b/package-lock.json index a741305..f3783f2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,6 +44,9 @@ "tailwind-merge": "^3.6.0", "tailwindcss": "^4.3.0", "typescript": "^5.9.3" + }, + "engines": { + "node": ">=22" } }, "node_modules/@babel/runtime": { diff --git a/package.json b/package.json index 1a070ff..8cc0416 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,39 @@ { "name": "claude-code-router", - "private": true, "version": "3.0.2", "license": "MIT", - "description": "Desktop scaffold for Claude Code Router.", + "description": "Local Claude Code Router gateway with CLI and web management UI.", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/musistudio/claude-code-router.git" + }, + "bugs": { + "url": "https://github.com/musistudio/claude-code-router/issues" + }, + "homepage": "https://github.com/musistudio/claude-code-router#readme", + "keywords": [ + "claude-code", + "codex", + "llm", + "gateway", + "router" + ], "main": "dist/main/main.js", "bin": { "ccr": "dist/main/cli.js" }, + "files": [ + "dist", + "LICENSE", + "README.md", + "README_zh.md" + ], + "engines": { + "node": ">=22" + }, + "publishConfig": { + "access": "public" + }, "scripts": { "dev": "node build/dev.mjs", "build": "npm run build:assets && electron-builder", @@ -16,6 +42,8 @@ "build:app:mac:local": "npm run build:assets && electron-builder --config build/electron-builder.local.cjs --mac --publish never", "build:app:mac:release": "node build/macos-release-preflight.mjs && npm run build:assets && electron-builder --mac --publish never", "build:app:win": "npm run build:assets && electron-builder --win", + "prepack": "npm run build:assets", + "prepublishOnly": "npm run typecheck", "preview": "npm run build:assets && electron .", "typecheck": "tsc --noEmit", "rebuild:sqlite3": "electron-rebuild -f -w better-sqlite3" diff --git a/src/main/claude-app-gateway-service.ts b/src/main/claude-app-gateway-service.ts index c9494a8..66da1af 100644 --- a/src/main/claude-app-gateway-service.ts +++ b/src/main/claude-app-gateway-service.ts @@ -1,4 +1,4 @@ -import { app } from "electron"; +import * as electron from "electron"; import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import os from "node:os"; import path from "node:path"; @@ -236,13 +236,30 @@ function getClaudeAppGatewayPaths(dataDir = getClaudeApp3pDataDir()): ClaudeAppG function getClaudeApp3pDataDir(): string { if (process.platform === "darwin") { - return path.join(app.getPath("home"), "Library", "Application Support", "Claude-3p"); + return path.join(appPath("home"), "Library", "Application Support", "Claude-3p"); } if (process.platform === "win32") { - const localAppData = process.env.LOCALAPPDATA || path.join(app.getPath("appData"), "..", "Local"); + const localAppData = process.env.LOCALAPPDATA || path.join(appPath("appData"), "..", "Local"); return path.join(localAppData, "Claude-3p"); } - return path.join(app.getPath("appData") || os.homedir(), "Claude-3p"); + return path.join(appPath("appData") || os.homedir(), "Claude-3p"); +} + +function appPath(name: "appData" | "home"): string { + const electronApp = electronAppOrUndefined(); + if (electronApp) { + return electronApp.getPath(name); + } + if (name === "home") { + return os.homedir(); + } + return process.env.APPDATA || + process.env.LOCALAPPDATA || + (process.env.USERPROFILE ? path.join(process.env.USERPROFILE, "AppData", "Roaming") : path.join(os.homedir(), ".config")); +} + +function electronAppOrUndefined(): Electron.App | undefined { + return typeof electron.app?.getPath === "function" ? electron.app : undefined; } function backupClaudeAppGatewayConfig(paths: ClaudeAppGatewayPaths): void { diff --git a/src/main/cli.ts b/src/main/cli.ts index 9507874..3d745ca 100644 --- a/src/main/cli.ts +++ b/src/main/cli.ts @@ -1,38 +1,100 @@ #!/usr/bin/env node -import { spawn } from "node:child_process"; -import { existsSync, readFileSync } from "node:fs"; -import os from "node:os"; +import { spawn, spawnSync } from "node:child_process"; +import { accessSync, constants as fsConstants, existsSync, mkdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from "node:fs"; import path from "node:path"; -import type { AppConfig, ProfileOpenSurface } from "../shared/app"; +import type { ProfileOpenSurface } from "../shared/app"; import { botGatewayProfileEnv } from "./bot-gateway-env"; import { launchCodexAppProfile, launchZcodeAppProfile } from "./codex-app-launch"; -import { createBetterSqliteDatabase } from "./sqlite-native"; +import { loadAppConfig } from "./config"; +import { CONFIGDIR } from "./constants"; import { buildProfileLaunchPlan, findProfileForOpen, profileLaunchSpawnCommand, resolveProfileOpenSurface } from "./profile-launch-core"; +import { startWebManagementServer } from "./web-management-server"; -type CliOptions = { +type ProfileCliOptions = { agentArgs: string[]; + command: "profile"; help: boolean; profileRef: string; surface?: ProfileOpenSurface; }; +type WebCliOptions = { + command: "start" | "web"; + daemonChild: boolean; + help: boolean; + host?: string; + open: boolean; + port?: number; + startGateway: boolean; +}; + +type StopCliOptions = { + command: "stop"; + help: boolean; +}; + +type CliOptions = ProfileCliOptions | StopCliOptions | WebCliOptions; + +type ServiceState = { + host?: string; + pid: number; + startedAt: string; + startGateway: boolean; + url: string; +}; + +const serviceStateFileName = "service.json"; +const serviceStartTimeoutMs = 30_000; +const serviceStopTimeoutMs = 10_000; + async function main(): Promise { - const options = parseArgs(process.argv.slice(2)); - if (options.help || !options.profileRef) { - printHelp(options.help ? 0 : 2); + const delegatedExitCode = delegateManagedDesktopCliToExternalCli(); + if (delegatedExitCode !== undefined) { + process.exitCode = delegatedExitCode; return; } - const configFile = process.env.CCR_CONFIG_FILE?.trim() || defaultConfigFile(); - const configDir = path.dirname(configFile); - const config = readConfig(configFile, process.env.CCR_CONFIG_DB_FILE?.trim() || defaultConfigDbFile(configFile)); - const profile = findProfileForOpen(config, options.profileRef); - const surface = options.surface ?? (profile.agent === "zcode" || profile.surface === "app" ? "app" : "cli"); + const options = parseArgs(process.argv.slice(2)); + if (options.command === "start") { + if (options.help) { + printStartHelp(0); + return; + } + await startService(options); + return; + } + if (options.command === "stop") { + if (options.help) { + printStopHelp(0); + return; + } + await stopService(); + return; + } + if (options.command === "web") { + if (options.help) { + printWebHelp(0); + return; + } + await runWebServer(options); + return; + } + + const profileOptions = options as ProfileCliOptions; + if (profileOptions.help || !profileOptions.profileRef) { + printHelp(profileOptions.help ? 0 : 2); + return; + } + + const configDir = CONFIGDIR; + const config = await loadAppConfig(); + const profile = findProfileForOpen(config, profileOptions.profileRef); + const surface = profileOptions.surface ?? (profile.agent === "zcode" || profile.surface === "app" ? "app" : "cli"); const resolvedSurface = resolveProfileOpenSurface(profile, surface); - if (profile.agent === "zcode" && options.agentArgs.length > 0) { + if (profile.agent === "zcode" && profileOptions.agentArgs.length > 0) { throw new Error("ZCode profiles can only open the app; agent arguments are not supported."); } - if ((profile.agent === "codex" || profile.agent === "zcode") && resolvedSurface === "app" && options.agentArgs.length === 0) { + if ((profile.agent === "codex" || profile.agent === "zcode") && resolvedSurface === "app" && profileOptions.agentArgs.length === 0) { if (profile.agent === "zcode") { const launch = launchZcodeAppProfile(configDir, profile, config); const spawnError = await waitForImmediateSpawnError(launch.child, 500); @@ -51,7 +113,7 @@ async function main(): Promise { return; } - const plan = buildProfileLaunchPlan(configDir, profile, resolvedSurface, options.agentArgs); + const plan = buildProfileLaunchPlan(configDir, profile, resolvedSurface, profileOptions.agentArgs); if (path.isAbsolute(plan.command) && !existsSync(plan.command)) { throw new Error(`Profile launcher was not found: ${plan.command}. Open CCR once or re-save the profile.`); @@ -75,8 +137,19 @@ async function main(): Promise { } function parseArgs(args: string[]): CliOptions { - const options: CliOptions = { + if (args[0] === "start") { + return parseWebArgs(args.slice(1), "start"); + } + if (args[0] === "stop") { + return parseStopArgs(args.slice(1)); + } + if (args[0] === "serve" || args[0] === "web") { + return parseWebArgs(args.slice(1), "web"); + } + + const options: ProfileCliOptions = { agentArgs: [], + command: "profile", help: false, profileRef: "" }; @@ -98,6 +171,10 @@ function parseArgs(args: string[]): CliOptions { options.surface = "cli"; continue; } + if (options.profileRef && !options.surface && (arg === "cli" || arg === "app")) { + options.surface = arg; + continue; + } if (!options.profileRef) { options.profileRef = arg; continue; @@ -107,85 +184,429 @@ function parseArgs(args: string[]): CliOptions { return options; } -function readConfig(jsonFile: string, dbFile: string): AppConfig { - const sqliteConfig = readSqliteConfig(dbFile); - if (sqliteConfig) { - return normalizeCliConfig(sqliteConfig, dbFile); +function parseStopArgs(args: string[]): StopCliOptions { + const options: StopCliOptions = { + command: "stop", + help: false + }; + for (const arg of args) { + if (arg === "--help" || arg === "-h") { + options.help = true; + continue; + } + throw new Error(`Unknown stop option: ${arg}`); } - if (!existsSync(jsonFile)) { - throw new Error(`CCR config was not found: ${dbFile}`); - } - const parsed = JSON.parse(readFileSync(jsonFile, "utf8")) as Partial; - return normalizeCliConfig(parsed, jsonFile); + return options; } -function readSqliteConfig(file: string): Partial | undefined { - if (!existsSync(file)) { - return undefined; - } - let database: ReturnType | undefined; - try { - database = createBetterSqliteDatabase(file); - const row = database.prepare("SELECT value_json FROM app_config WHERE key = ? LIMIT 1").get("default") as { value_json?: unknown } | undefined; - return typeof row?.value_json === "string" - ? JSON.parse(row.value_json) as Partial - : undefined; - } catch { - return undefined; - } finally { - database?.close(); +function parseWebArgs(args: string[], command: WebCliOptions["command"]): WebCliOptions { + const options: WebCliOptions = { + command, + daemonChild: false, + help: false, + open: false, + startGateway: true + }; + for (let index = 0; index < args.length; index += 1) { + const arg = args[index]; + if (arg === "--help" || arg === "-h") { + options.help = true; + continue; + } + if (arg === "--open") { + options.open = true; + continue; + } + if (arg === "--no-open") { + options.open = false; + continue; + } + if (arg === "--gateway") { + options.startGateway = true; + continue; + } + if (arg === "--no-gateway") { + options.startGateway = false; + continue; + } + if (arg === "--daemon-child") { + options.daemonChild = true; + continue; + } + if (arg === "--host") { + index += 1; + options.host = requiredArg(args[index], "--host"); + continue; + } + if (arg.startsWith("--host=")) { + options.host = requiredArg(arg.slice("--host=".length), "--host"); + continue; + } + if (arg === "--port") { + index += 1; + options.port = parsePort(requiredArg(args[index], "--port")); + continue; + } + if (arg.startsWith("--port=")) { + options.port = parsePort(requiredArg(arg.slice("--port=".length), "--port")); + continue; + } + throw new Error(`Unknown web option: ${arg}`); } + return options; } -function normalizeCliConfig(parsed: Partial, source: string): AppConfig { - if (!parsed.profile || !Array.isArray(parsed.profile.profiles)) { - throw new Error(`CCR config has no profiles: ${source}`); +async function startService(options: WebCliOptions): Promise { + const current = readServiceState(); + if (current && isProcessRunning(current.pid)) { + process.stdout.write(`CCR service is already running at ${current.url} (pid ${current.pid}).\n`); + return; } - return { - ...parsed, - profile: { - ...parsed.profile, - profiles: parsed.profile.profiles - } as AppConfig["profile"] - } as AppConfig; -} + clearServiceState(); -function defaultConfigDbFile(configFile: string): string { - return path.join(path.dirname(configFile), "config.sqlite"); -} - -function defaultConfigFile(): string { - return path.join(defaultConfigDir(), "config.json"); -} - -function defaultConfigDir(): string { - if (process.platform === "win32") { - return path.join( - process.env.APPDATA || - process.env.LOCALAPPDATA || - (process.env.USERPROFILE ? path.join(process.env.USERPROFILE, "AppData", "Roaming") : path.join(os.homedir(), "AppData", "Roaming")), - "Claude Code Router" - ); + const childArgs = [ + currentCliScript(), + "serve", + "--daemon-child", + ...(options.host ? ["--host", options.host] : []), + ...(options.port ? ["--port", String(options.port)] : []), + ...(options.open ? ["--open"] : ["--no-open"]), + ...(options.startGateway ? [] : ["--no-gateway"]) + ]; + const child = spawn(process.execPath, childArgs, { + detached: true, + env: { + ...process.env, + ELECTRON_RUN_AS_NODE: undefined + }, + stdio: "ignore", + windowsHide: true + }); + const spawnError = await waitForImmediateSpawnError(child, 1000); + if (spawnError) { + throw new Error(`Failed to start CCR service: ${spawnError}`); } - return path.join(os.homedir(), ".claude-code-router"); + child.unref(); + + const state = await waitForServiceState(child.pid, serviceStartTimeoutMs); + if (!state) { + throw new Error(`CCR service did not report ready within ${serviceStartTimeoutMs}ms.`); + } + process.stdout.write(`CCR service started at ${state.url} (pid ${state.pid}).\n`); +} + +async function runWebServer(options: WebCliOptions): Promise { + const runtime = await startWebManagementServer({ + host: options.host, + open: options.open, + port: options.port, + startGateway: options.startGateway + }); + if (options.daemonChild) { + writeServiceState({ + host: options.host, + pid: process.pid, + startedAt: new Date().toISOString(), + startGateway: options.startGateway, + url: runtime.url + }); + } + process.stdout.write(`CCR web management is running at ${runtime.url}\n`); + + let closing = false; + const shutdown = (signal: NodeJS.Signals) => { + if (closing) { + return; + } + closing = true; + void runtime.close().finally(() => { + if (options.daemonChild) { + clearServiceState(process.pid); + } + process.exit(signal === "SIGINT" ? 130 : 143); + }); + }; + process.once("SIGINT", shutdown); + process.once("SIGTERM", shutdown); + await new Promise(() => undefined); +} + +async function stopService(): Promise { + const state = readServiceState(); + if (!state) { + process.stdout.write("CCR service is not running.\n"); + return; + } + if (!isProcessRunning(state.pid)) { + clearServiceState(state.pid); + process.stdout.write("CCR service is not running.\n"); + return; + } + process.kill(state.pid, "SIGTERM"); + const stopped = await waitForProcessExit(state.pid, serviceStopTimeoutMs); + if (!stopped && isProcessRunning(state.pid)) { + throw new Error(`CCR service pid ${state.pid} did not stop within ${serviceStopTimeoutMs}ms.`); + } + clearServiceState(state.pid); + process.stdout.write("CCR service stopped.\n"); } function printHelp(exitCode: number): void { const output = [ "Usage:", - " ccr [--cli|--app] [-- ]", + " ccr start [--host ] [--port ] [--open] [--no-gateway]", + " ccr stop", + " ccr [-- ]", "", "Examples:", - " ccr Codex", - " ccr default-codex -- --model gpt-5-codex", - " ccr default-codex --app", - " ccr ZCode" + " ccr start", + " ccr stop", + " ccr Codex cli", + " ccr default-codex cli -- --model gpt-5-codex", + " ccr default-codex app" ].join("\n"); const stream = exitCode === 0 ? process.stdout : process.stderr; stream.write(`${output}\n`); process.exitCode = exitCode; } +function printStartHelp(exitCode: number): void { + const output = [ + "Usage:", + " ccr start [--host ] [--port ] [--open] [--no-gateway]", + "", + "Options:", + " --host Management server host. Defaults to 127.0.0.1.", + " --port Management server port. Defaults to 3458.", + " --open Open the management page in the default browser.", + " --no-open Do not open the management page.", + " --no-gateway Start only the web management server." + ].join("\n"); + const stream = exitCode === 0 ? process.stdout : process.stderr; + stream.write(`${output}\n`); + process.exitCode = exitCode; +} + +function printStopHelp(exitCode: number): void { + const output = [ + "Usage:", + " ccr stop", + "", + "Stops the background CCR service started by `ccr start`." + ].join("\n"); + const stream = exitCode === 0 ? process.stdout : process.stderr; + stream.write(`${output}\n`); + process.exitCode = exitCode; +} + +function printWebHelp(exitCode: number): void { + const output = [ + "Usage:", + " ccr serve [--host ] [--port ] [--open] [--no-gateway]", + "", + "Options:", + " --host Management server host. Defaults to 127.0.0.1.", + " --port Management server port. Defaults to 3458.", + " --open Open the management page in the default browser.", + " --no-gateway Start only the web management server." + ].join("\n"); + const stream = exitCode === 0 ? process.stdout : process.stderr; + stream.write(`${output}\n`); + process.exitCode = exitCode; +} + +function readServiceState(): ServiceState | undefined { + const file = serviceStateFile(); + if (!existsSync(file)) { + return undefined; + } + try { + const parsed = JSON.parse(readFileSync(file, "utf8")) as Partial; + const pid = Number(parsed.pid); + if (!Number.isInteger(pid) || pid <= 0 || typeof parsed.url !== "string") { + return undefined; + } + return { + host: parsed.host, + pid, + startedAt: parsed.startedAt || "", + startGateway: parsed.startGateway !== false, + url: parsed.url + }; + } catch { + return undefined; + } +} + +function writeServiceState(state: ServiceState): void { + const file = serviceStateFile(); + mkdirSync(path.dirname(file), { recursive: true }); + writeFileSync(file, `${JSON.stringify(state, null, 2)}\n`, { encoding: "utf8", mode: 0o600 }); +} + +function clearServiceState(pid?: number): void { + const state = readServiceState(); + if (pid !== undefined && state && state.pid !== pid) { + return; + } + try { + unlinkSync(serviceStateFile()); + } catch { + // Stale state cleanup is best effort. + } +} + +function serviceStateFile(): string { + return path.join(CONFIGDIR, serviceStateFileName); +} + +function currentCliScript(): string { + return __filename; +} + +function delegateManagedDesktopCliToExternalCli(): number | undefined { + if (!isManagedDesktopCliRuntime()) { + return undefined; + } + if (process.env.CCR_MANAGED_CLI_NO_DELEGATE === "1" || process.env.CCR_MANAGED_CLI_DELEGATED === "1") { + return undefined; + } + + const externalCcr = findExternalCcrCommand(); + if (!externalCcr) { + return undefined; + } + + const launch = profileLaunchSpawnCommand({ + args: process.argv.slice(2), + command: externalCcr + }); + const result = spawnSync(launch.command, launch.args, { + env: { + ...process.env, + CCR_MANAGED_CLI_DELEGATED: "1" + }, + stdio: "inherit", + windowsVerbatimArguments: !!launch.windowsVerbatimArguments + }); + if (result.error) { + return undefined; + } + if (typeof result.status === "number") { + return result.status; + } + return result.signal === "SIGINT" ? 130 : 1; +} + +function isManagedDesktopCliRuntime(): boolean { + const script = process.argv[1] || __filename; + return samePath(path.resolve(script), path.join(CONFIGDIR, "bin", "ccr-cli.js")); +} + +function findExternalCcrCommand(): string | undefined { + const pathKey = process.platform === "win32" + ? Object.keys(process.env).find((key) => key.toLowerCase() === "path") || "Path" + : "PATH"; + const pathValue = process.env[pathKey] || ""; + const managedBinDir = path.resolve(CONFIGDIR, "bin"); + const names = process.platform === "win32" + ? ["ccr.cmd", "ccr.exe", "ccr.bat", "ccr"] + : ["ccr"]; + + for (const rawSegment of pathValue.split(path.delimiter)) { + const dir = path.resolve(rawSegment || "."); + if (samePath(dir, managedBinDir)) { + continue; + } + for (const name of names) { + const candidate = path.join(dir, name); + if (isExecutableFile(candidate)) { + return candidate; + } + } + } + return undefined; +} + +function isExecutableFile(file: string): boolean { + try { + const stats = statSync(file); + if (!stats.isFile() && !stats.isSymbolicLink()) { + return false; + } + if (process.platform === "win32") { + return true; + } + accessSync(file, fsConstants.X_OK); + return true; + } catch { + return false; + } +} + +function samePath(left: string, right: string): boolean { + const normalizedLeft = path.normalize(left); + const normalizedRight = path.normalize(right); + return process.platform === "win32" + ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase() + : normalizedLeft === normalizedRight; +} + +async function waitForServiceState(pid: number | undefined, timeoutMs: number): Promise { + const startedAt = Date.now(); + while (Date.now() - startedAt < timeoutMs) { + const state = readServiceState(); + if (state && (!pid || state.pid === pid) && isProcessRunning(state.pid)) { + return state; + } + await delay(150); + } + return undefined; +} + +async function waitForProcessExit(pid: number, timeoutMs: number): Promise { + const startedAt = Date.now(); + while (Date.now() - startedAt < timeoutMs) { + if (!isProcessRunning(pid)) { + return true; + } + await delay(150); + } + return !isProcessRunning(pid); +} + +function isProcessRunning(pid: number | undefined): boolean { + if (!pid || pid <= 0) { + return false; + } + try { + process.kill(pid, 0); + return true; + } catch (error) { + const code = typeof error === "object" && error !== null && "code" in error ? (error as { code?: unknown }).code : undefined; + return code === "EPERM"; + } +} + +function delay(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +function requiredArg(value: string | undefined, option: string): string { + if (!value?.trim()) { + throw new Error(`${option} requires a value.`); + } + return value.trim(); +} + +function parsePort(value: string): number { + const port = Number(value); + if (!Number.isInteger(port) || port <= 0 || port > 65535) { + throw new Error(`Invalid port: ${value}`); + } + return port; +} + function waitForChild(child: ReturnType): Promise { return new Promise((resolve) => { child.on("exit", (code, signal) => resolve(code ?? (signal === "SIGINT" ? 130 : 1))); diff --git a/src/main/electron-node-shim.ts b/src/main/electron-node-shim.ts new file mode 100644 index 0000000..212ed6a --- /dev/null +++ b/src/main/electron-node-shim.ts @@ -0,0 +1,14 @@ +export const app = undefined; +export const BrowserWindow = undefined; +export const clipboard = undefined; +export const contextBridge = undefined; +export const dialog = undefined; +export const ipcMain = undefined; +export const ipcRenderer = undefined; +export const Menu = undefined; +export const nativeImage = undefined; +export const screen = undefined; +export const session = undefined; +export const shell = undefined; +export const Tray = undefined; +export const WebContentsView = undefined; diff --git a/src/main/profile-launch-core.ts b/src/main/profile-launch-core.ts index b2d2139..5f50af2 100644 --- a/src/main/profile-launch-core.ts +++ b/src/main/profile-launch-core.ts @@ -76,7 +76,7 @@ export function profileOpenCommand( profileRef = profile.name?.trim() || profile.id ): string { const quote = process.platform === "win32" ? windowsCommandQuote : shellQuote; - return [quote(command), quote(profileRef), ...(surface === "app" ? ["--app"] : [])].join(" "); + return [quote(command), quote(profileRef), surface].join(" "); } export function buildProfileLaunchPlan( diff --git a/src/main/web-client-bridge.ts b/src/main/web-client-bridge.ts new file mode 100644 index 0000000..f720d17 --- /dev/null +++ b/src/main/web-client-bridge.ts @@ -0,0 +1,112 @@ +const rpcEndpoint = "/api/ccr/rpc"; + +type RpcResponse = + | { ok: true; value: unknown } + | { error: { message: string; stack?: string }; ok: false }; + +async function rpc(method: string, args: unknown[] = []): Promise { + const response = await fetch(rpcEndpoint, { + body: JSON.stringify({ args, method }), + headers: { + "content-type": "application/json" + }, + method: "POST" + }); + let payload: RpcResponse | undefined; + try { + payload = await response.json() as RpcResponse; + } catch { + payload = undefined; + } + if (!response.ok || !payload?.ok) { + const message = payload && !payload.ok + ? payload.error.message + : `CCR web API failed with HTTP ${response.status}`; + throw new Error(message); + } + return payload.value; +} + +function noopSubscription(): () => void { + return () => undefined; +} + +async function selectPluginDirectory(): Promise { + const directory = window.prompt("Plugin directory path"); + if (!directory?.trim()) { + return undefined; + } + return rpc("selectPluginDirectory", [directory.trim()]); +} + +window.ccr = { + applyClaudeAppGateway: (config) => rpc("applyClaudeAppGateway", [config]) as ReturnType["applyClaudeAppGateway"]>, + applyProfile: () => rpc("applyProfile") as ReturnType["applyProfile"]>, + cancelBotGatewayQrLogin: (request) => rpc("cancelBotGatewayQrLogin", [request]) as ReturnType["cancelBotGatewayQrLogin"]>, + checkProviderConnectivity: (request) => rpc("checkProviderConnectivity", [request]) as ReturnType["checkProviderConnectivity"]>, + clearProxyNetworkCaptures: () => rpc("clearProxyNetworkCaptures") as ReturnType["clearProxyNetworkCaptures"]>, + closeBotGatewayQrWindow: (request) => rpc("closeBotGatewayQrWindow", [request]) as ReturnType["closeBotGatewayQrWindow"]>, + closeTray: () => Promise.resolve(), + detectProviderIcon: (request) => rpc("detectProviderIcon", [request]) as ReturnType["detectProviderIcon"]>, + exportData: () => rpc("exportData") as ReturnType["exportData"]>, + fetchProviderManifest: (request) => rpc("fetchProviderManifest", [request]) as ReturnType["fetchProviderManifest"]>, + getAgentAnalysis: (filter) => rpc("getAgentAnalysis", [filter]) as ReturnType["getAgentAnalysis"]>, + getAgentTracePayload: (request) => rpc("getAgentTracePayload", [request]) as ReturnType["getAgentTracePayload"]>, + getAppInfo: () => rpc("getAppInfo") as ReturnType["getAppInfo"]>, + getConfig: () => rpc("getConfig") as ReturnType["getConfig"]>, + getGatewayStatus: () => rpc("getGatewayStatus") as ReturnType["getGatewayStatus"]>, + getLocalAgentProviderCandidates: () => rpc("getLocalAgentProviderCandidates") as ReturnType["getLocalAgentProviderCandidates"]>, + getOnboardingFinished: () => rpc("getOnboardingFinished") as ReturnType["getOnboardingFinished"]>, + getPendingProviderDeepLinks: () => Promise.resolve([]), + getPluginMarketplace: () => rpc("getPluginMarketplace") as ReturnType["getPluginMarketplace"]>, + getProfileOpenCommand: (request) => rpc("getProfileOpenCommand", [request]) as ReturnType["getProfileOpenCommand"]>, + getProfileRuntimeStatus: () => rpc("getProfileRuntimeStatus") as ReturnType["getProfileRuntimeStatus"]>, + getProviderAccountSnapshots: (provider, options) => rpc("getProviderAccountSnapshots", [provider, options]) as ReturnType["getProviderAccountSnapshots"]>, + getProviderCatalogModels: (request) => rpc("getProviderCatalogModels", [request]) as ReturnType["getProviderCatalogModels"]>, + getProviderPresets: () => rpc("getProviderPresets") as ReturnType["getProviderPresets"]>, + getProxyCertificateStatus: () => rpc("getProxyCertificateStatus") as ReturnType["getProxyCertificateStatus"]>, + getProxyNetworkCaptures: () => rpc("getProxyNetworkCaptures") as ReturnType["getProxyNetworkCaptures"]>, + getProxyStatus: () => rpc("getProxyStatus") as ReturnType["getProxyStatus"]>, + getRequestLogs: (filter) => rpc("getRequestLogs", [filter]) as ReturnType["getRequestLogs"]>, + getUpdateStatus: () => rpc("getUpdateStatus") as ReturnType["getUpdateStatus"]>, + getUsageStats: (range, filter) => rpc("getUsageStats", [range, filter]) as ReturnType["getUsageStats"]>, + importLocalAgentProvider: (request) => rpc("importLocalAgentProvider", [request]) as ReturnType["importLocalAgentProvider"]>, + installProxyCertificate: () => rpc("installProxyCertificate") as ReturnType["installProxyCertificate"]>, + listMcpServerTools: (serverName) => rpc("listMcpServerTools", [serverName]) as ReturnType["listMcpServerTools"]>, + onBeforeQuit: noopSubscription, + onOpenSettingsRequest: noopSubscription, + onOpenUpdateRequest: noopSubscription, + onProviderDeepLink: noopSubscription, + onUpdateStatusChanged: noopSubscription, + openBotGatewayQrWindow: (request) => rpc("openBotGatewayQrWindow", [request]) as ReturnType["openBotGatewayQrWindow"]>, + openBuiltInBrowser: () => rpc("openBuiltInBrowser") as ReturnType["openBuiltInBrowser"]>, + openExternal: (url) => { + window.open(url, "_blank", "noopener,noreferrer"); + return Promise.resolve(); + }, + openProfile: (request) => rpc("openProfile", [request]) as ReturnType["openProfile"]>, + probeProvider: (request) => rpc("probeProvider", [request]) as ReturnType["probeProvider"]>, + probeProviderCandidates: (request) => rpc("probeProviderCandidates", [request]) as ReturnType["probeProviderCandidates"]>, + quitApp: () => rpc("quitApp") as ReturnType["quitApp"]>, + restartGateway: () => rpc("restartGateway") as ReturnType["restartGateway"]>, + restartProxy: () => rpc("restartProxy") as ReturnType["restartProxy"]>, + revealProxyCertificate: () => rpc("revealProxyCertificate") as ReturnType["revealProxyCertificate"]>, + saveApiKeys: (apiKeys) => rpc("saveApiKeys", [apiKeys]) as ReturnType["saveApiKeys"]>, + saveConfig: (config, options) => rpc("saveConfig", [config, options]) as ReturnType["saveConfig"]>, + scanBotHandoffBluetoothTargets: () => rpc("scanBotHandoffBluetoothTargets") as ReturnType["scanBotHandoffBluetoothTargets"]>, + scanBotHandoffWifiTargets: () => rpc("scanBotHandoffWifiTargets") as ReturnType["scanBotHandoffWifiTargets"]>, + selectPluginDirectory: () => selectPluginDirectory() as ReturnType["selectPluginDirectory"]>, + setOnboardingFinished: () => rpc("setOnboardingFinished") as ReturnType["setOnboardingFinished"]>, + setProxyNetworkCaptureEnabled: (enabled) => rpc("setProxyNetworkCaptureEnabled", [enabled]) as ReturnType["setProxyNetworkCaptureEnabled"]>, + setTrayDetailOpen: () => Promise.resolve(), + showMainWindow: () => Promise.resolve(), + startBotGatewayQrLogin: (request) => rpc("startBotGatewayQrLogin", [request]) as ReturnType["startBotGatewayQrLogin"]>, + startGateway: () => rpc("startGateway") as ReturnType["startGateway"]>, + stopGateway: () => rpc("stopGateway") as ReturnType["stopGateway"]>, + stopProfile: (request) => rpc("stopProfile", [request]) as ReturnType["stopProfile"]>, + testProviderAccountConnector: (request) => rpc("testProviderAccountConnector", [request]) as ReturnType["testProviderAccountConnector"]>, + updateCheck: () => rpc("updateCheck") as ReturnType["updateCheck"]>, + updateDownload: () => rpc("updateDownload") as ReturnType["updateDownload"]>, + updateInstall: () => rpc("updateInstall") as ReturnType["updateInstall"]>, + waitBotGatewayQrLogin: (request) => rpc("waitBotGatewayQrLogin", [request]) as ReturnType["waitBotGatewayQrLogin"]> +}; diff --git a/src/main/web-management-server.ts b/src/main/web-management-server.ts new file mode 100644 index 0000000..c22697f --- /dev/null +++ b/src/main/web-management-server.ts @@ -0,0 +1,1014 @@ +import { spawn } from "node:child_process"; +import { createServer, type IncomingMessage, type Server, type ServerResponse } from "node:http"; +import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import { pathToFileURL } from "node:url"; +import packageJson from "../../package.json"; +import { loadPersistedAppSetting, replacePersistedAppSetting } from "./app-config-store"; +import { scanBotHandoffBluetoothTargets, scanBotHandoffWifiTargets } from "./bot-handoff-scan-service"; +import { cancelBotGatewayQrLogin, startBotGatewayQrLogin, waitBotGatewayQrLogin } from "./bot-gateway-qr-login-service"; +import { syncClaudeAppGatewayConfig, restoreClaudeAppGatewayConfig } from "./claude-app-gateway-service"; +import { loadAppConfig, saveApiKeysConfig, saveAppConfig } from "./config"; +import { API_KEYS_DB_FILE, APP_CONFIG_DB_FILE, APP_NAME, CONFIGDIR, CONFIG_FILE, DATADIR, GATEWAY_CONFIG_FILE, LEGACY_CONFIG_FILE, ONBOARDING_FINISHED_FILE, PROXY_CA_CERT_FILE, REQUEST_LOGS_DB_FILE, USAGE_DB_FILE } from "./constants"; +import { detectProviderIcon } from "./provider-icons"; +import { fetchProviderManifest } from "./provider-manifest-service"; +import { getLocalAgentProviderCandidates, importLocalAgentProvider } from "./local-agent-provider-service"; +import { getProviderCatalogModels } from "./provider-model-catalog"; +import { getProviderPresets } from "./presets"; +import { checkGatewayProviderConnectivity, probeGatewayProvider, probeGatewayProviderCandidates } from "./provider-probe"; +import { applyProfileConfig } from "./profile-service"; +import { getProfileOpenCommand, getProfileRuntimeStatus, openProfileFromCcr, stopProfileFromCcr } from "./profile-launch-service"; +import { ensureProxyCertificateAuthority } from "../server/proxy/certificates"; +import { proxyService } from "../server/proxy/service"; +import { listMcpServerTools } from "../server/mcp/tool-discovery"; +import { getAgentAnalysis, getAgentTracePayload, getRequestLogs } from "./request-log-store"; +import { getUsageStats } from "./usage-store"; +import { gatewayService } from "../server/gateway/service"; +import { getProviderAccountSnapshots, invalidateProviderAccountSnapshotCache, testProviderAccountConnector } from "./provider-account-service"; +import type { + AgentAnalysisFilter, + AgentAnalysisTracePayloadRequest, + ApiKeyConfig, + AppConfig, + AppDataExportResult, + AppInfo, + AppSaveConfigOptions, + AppUpdateStatus, + BotGatewayQrLoginCancelRequest, + BotGatewayQrLoginStartRequest, + BotGatewayQrLoginWaitRequest, + BotGatewayQrWindowOpenRequest, + GatewayPluginAppConfig, + GatewayProviderConnectivityCheckRequest, + GatewayProviderProbeCandidatesRequest, + GatewayProviderProbeRequest, + GatewayStatus, + LocalAgentProviderImportRequest, + PluginDependency, + PluginDirectorySelection, + PluginMarketplaceEntry, + ProfileApplyResult, + ProfileOpenRequest, + ProviderAccountSnapshotRequestOptions, + ProviderAccountTestRequest, + ProviderCatalogModelsRequest, + ProviderIconDetectionRequest, + ProviderManifestFetchRequest, + RequestLogListFilter, + UsageStatsFilter, + UsageStatsRange +} from "../shared/app"; + +export type WebManagementServerOptions = { + host?: string; + open?: boolean; + port?: number; + startGateway?: boolean; +}; + +export type WebManagementServerRuntime = { + close: () => Promise; + server: Server; + url: string; +}; + +type RpcRequest = { + args?: unknown[]; + method?: string; +}; + +type RpcHandler = (...args: unknown[]) => Promise | unknown; + +const defaultWebHost = "127.0.0.1"; +const defaultWebPort = 3458; +const onboardingFinishedAtSettingKey = "onboardingFinishedAt"; +const maxRpcBodyBytes = 8 * 1024 * 1024; +const staticRoot = path.resolve(__dirname, "..", "renderer"); +const homeHtmlFile = path.join(staticRoot, "pages", "home", "index.html"); +const rendererAssetsRoot = path.join(staticRoot, "assets"); +const webBridgeScriptTag = ' '; + +const pluginMarketplace: PluginMarketplaceEntry[] = [ + { + capabilities: ["Wrapper runtime", "Claude App proxy", "Claude Design", "Model routing"], + dependencies: [], + description: "Routes Claude App Design traffic through the local CCR wrapper backend with configurable model routing.", + id: "claude-design", + modulePath: path.join(__dirname, "..", "marketplace", "plugins", "claude-design-plugin.cjs"), + name: "Claude Design" + }, + { + capabilities: ["Wrapper runtime", "Proxy mode", "Cursor", "Model routing", "OpenAI/Anthropic/Gemini forwarding"], + dependencies: [], + description: "Routes Cursor-compatible LLM traffic captured by proxy mode into the local CCR gateway.", + id: "cursor-proxy", + modulePath: path.join(__dirname, "..", "marketplace", "plugins", "cursor-proxy-plugin.cjs"), + name: "Cursor Proxy" + } +]; + +export async function startWebManagementServer(options: WebManagementServerOptions = {}): Promise { + const host = options.host?.trim() || readEnvString("CCR_WEB_HOST") || defaultWebHost; + const requestedPort = options.port ?? readEnvPort("CCR_WEB_PORT") ?? defaultWebPort; + const server = createServer((request, response) => { + void handleRequest(request, response).catch((error) => { + sendJson(response, 500, { error: { message: formatError(error) }, ok: false }); + }); + }); + + const listenedPort = await listenWithFallback(server, requestedPort, host); + const address = server.address(); + const port = typeof address === "object" && address ? address.port : listenedPort; + const url = `http://${formatListenHost(host)}:${port}/`; + + if (options.startGateway !== false) { + await startConfiguredServices("web startup"); + } + if (options.open) { + await openSystemExternal(url).catch((error) => { + console.warn(`[web] Failed to open ${url}: ${formatError(error)}`); + }); + } + + return { + close: async () => { + await closeServer(server); + await stopConfiguredServices(); + }, + server, + url + }; +} + +async function handleRequest(request: IncomingMessage, response: ServerResponse): Promise { + const url = requestUrl(request); + if (url.pathname === "/api/ccr/rpc") { + await handleRpcRequest(request, response); + return; + } + if (request.method !== "GET" && request.method !== "HEAD") { + sendText(response, 405, "Method not allowed"); + return; + } + if (url.pathname === "/" || url.pathname === "/pages/home/index.html") { + sendHomeHtml(response, request.method === "HEAD"); + return; + } + if (url.pathname.startsWith("/assets/")) { + sendStaticFile(response, rendererAssetsRoot, decodeURIComponent(url.pathname.slice("/assets/".length)), request.method === "HEAD"); + return; + } + sendText(response, 404, "Not found"); +} + +async function handleRpcRequest(request: IncomingMessage, response: ServerResponse): Promise { + if (request.method !== "POST") { + sendJson(response, 405, { error: { message: "RPC only supports POST." }, ok: false }); + return; + } + + let payload: RpcRequest; + try { + payload = JSON.parse((await readRequestBody(request, maxRpcBodyBytes)).toString("utf8")) as RpcRequest; + } catch (error) { + sendJson(response, 400, { error: { message: `Invalid JSON: ${formatError(error)}` }, ok: false }); + return; + } + + const method = typeof payload.method === "string" ? payload.method.trim() : ""; + const handler = rpcHandlers[method]; + if (!method || !handler) { + sendJson(response, 404, { error: { message: `Unknown CCR web RPC method: ${method || "(empty)"}` }, ok: false }); + return; + } + + try { + const value = await handler(...(Array.isArray(payload.args) ? payload.args : [])); + sendJson(response, 200, { ok: true, value }); + } catch (error) { + sendJson(response, 500, { error: { message: formatError(error) }, ok: false }); + } +} + +const unsupportedUpdateStatus: AppUpdateStatus = { + canCheck: false, + canDownload: false, + canInstall: false, + currentVersion: packageJson.version, + lastError: "Updates are only available in the desktop app.", + state: "idle", + supported: false +}; + +const rpcHandlers: Record = { + applyClaudeAppGateway: async (config?: unknown) => { + const previousConfig = await loadAppConfig(); + const baseConfig = config ? await saveAppConfig(config as AppConfig) : previousConfig; + const synced = await syncClaudeAppGatewayConfig(baseConfig); + const savedConfig = synced.config; + let runtimeStatus = gatewayService.getStatus(); + if (synced.configChanged || shouldRestartForRuntimeChange(previousConfig, savedConfig) || runtimeStatus.state !== "running") { + runtimeStatus = await gatewayService.start(savedConfig); + } else { + gatewayService.updateConfig(savedConfig); + } + if (config || synced.configChanged) { + invalidateProviderAccountSnapshotCache(); + } + const gatewayDetail = runtimeStatus.state === "running" + ? "CCR gateway is running." + : `CCR gateway did not start: ${runtimeStatus.lastError || "unknown error"}`; + const apiKeyDetail = synced.result.apiKeyGenerated ? "Generated a Claude App API key." : "Reused an existing CCR API key."; + return { + ...synced.result, + message: `${synced.result.message}\n${gatewayDetail}\n${apiKeyDetail}` + }; + }, + applyProfile: async () => applyProfileConfig(await loadAppConfig()), + cancelBotGatewayQrLogin: (request) => cancelBotGatewayQrLogin(request as BotGatewayQrLoginCancelRequest), + checkProviderConnectivity: (request) => checkGatewayProviderConnectivity(request as GatewayProviderConnectivityCheckRequest), + clearProxyNetworkCaptures: () => proxyService.clearNetworkCaptures(), + closeBotGatewayQrWindow: (_request) => ({ closed: false }), + detectProviderIcon: (request) => detectProviderIcon(request as ProviderIconDetectionRequest), + exportData: () => exportAppData(), + fetchProviderManifest: (request) => fetchProviderManifest(request as ProviderManifestFetchRequest), + getAgentAnalysis: (filter) => getAgentAnalysis(filter as AgentAnalysisFilter | undefined), + getAgentTracePayload: (request) => getAgentTracePayload(request as AgentAnalysisTracePayloadRequest), + getAppInfo: () => getCliAppInfo(), + getConfig: () => loadAppConfig(), + getGatewayStatus: () => gatewayService.getStatus(), + getLocalAgentProviderCandidates: () => getLocalAgentProviderCandidates(), + getOnboardingFinished: async () => Boolean(readString(await loadPersistedAppSetting(onboardingFinishedAtSettingKey)) || existsSync(ONBOARDING_FINISHED_FILE)), + getPluginMarketplace: () => pluginMarketplace, + getProfileOpenCommand: async (request) => getProfileOpenCommand(await loadAppConfig(), request as ProfileOpenRequest), + getProfileRuntimeStatus: () => getProfileRuntimeStatus(), + getProviderAccountSnapshots: (provider, options) => getProviderAccountSnapshots(provider as string | undefined, options as ProviderAccountSnapshotRequestOptions | undefined), + getProviderCatalogModels: (request) => getProviderCatalogModels(request as ProviderCatalogModelsRequest), + getProviderPresets: () => getProviderPresets(), + getProxyCertificateStatus: () => proxyService.getCertificateStatus(), + getProxyNetworkCaptures: () => proxyService.getNetworkCaptures(), + getProxyStatus: () => proxyService.getStatus(), + getRequestLogs: (filter) => getRequestLogs(filter as RequestLogListFilter | undefined), + getUpdateStatus: () => unsupportedUpdateStatus, + getUsageStats: (range, filter) => getUsageStats(range as UsageStatsRange | undefined, filter as UsageStatsFilter | undefined), + importLocalAgentProvider: (request) => importLocalAgentProvider(request as LocalAgentProviderImportRequest), + installProxyCertificate: () => proxyService.installCertificate(), + listMcpServerTools: async (serverName) => { + const name = typeof serverName === "string" ? serverName.trim() : ""; + if (!name) { + throw new Error("MCP server name is required."); + } + const config = await loadAppConfig(); + const server = config.agent.mcpServers.find((candidate) => candidate.name === name); + if (!server) { + throw new Error("MCP server must be saved before tool discovery."); + } + return listMcpServerTools(server); + }, + openBotGatewayQrWindow: async (request) => { + const qrRequest = request as BotGatewayQrWindowOpenRequest; + await openSystemExternal(qrRequest.url); + return { + message: "Opened the QR login URL in your default browser.", + observed: false, + opened: true + }; + }, + openBuiltInBrowser: async () => { + const config = await loadAppConfig(); + const appUrl = firstConfiguredBrowserAppUrl(config) || "about:blank"; + if (appUrl === "about:blank") { + throw new Error("No browser app is configured."); + } + await openSystemExternal(appUrl); + }, + openProfile: async (request) => { + const syncedClaudeAppConfig = await syncClaudeAppGatewayConfig(await loadAppConfig()); + const config = syncedClaudeAppConfig.config; + const status = await gatewayService.start(config); + if (status.state !== "running") { + throw new Error(status.lastError || "CCR gateway did not start."); + } + logProfileApplyResult(await applyProfileConfig(config)); + return openProfileFromCcr(config, request as ProfileOpenRequest); + }, + probeProvider: (request) => probeGatewayProvider(request as GatewayProviderProbeRequest), + probeProviderCandidates: (request) => probeGatewayProviderCandidates(request as GatewayProviderProbeCandidatesRequest), + quitApp: async () => { + setTimeout(() => process.kill(process.pid, "SIGTERM"), 50).unref(); + }, + restartGateway: async () => { + const syncedClaudeAppConfig = await syncClaudeAppGatewayConfig(await loadAppConfig()); + const config = syncedClaudeAppConfig.config; + const status = await gatewayService.start(config); + await applyProfileIfServiceRunning(config, status); + return status; + }, + restartProxy: async () => { + const syncedClaudeAppConfig = await syncClaudeAppGatewayConfig(await loadAppConfig()); + const config = syncedClaudeAppConfig.config; + const status = await gatewayService.start(config); + await applyProfileIfServiceRunning(config, status); + return proxyService.getStatus(); + }, + revealProxyCertificate: async () => { + ensureProxyCertificateAuthority(); + await revealFile(PROXY_CA_CERT_FILE); + }, + saveApiKeys: async (apiKeys) => { + const savedConfig = await saveApiKeysConfig(apiKeys as ApiKeyConfig[]); + const syncedClaudeAppConfig = await syncClaudeAppGatewayConfig(savedConfig); + const nextConfig = syncedClaudeAppConfig.config; + gatewayService.updateConfig(nextConfig); + logProfileApplyResult(await applyProfileConfig(nextConfig)); + invalidateProviderAccountSnapshotCache(); + return nextConfig; + }, + saveConfig: async (config, options) => { + const previousConfig = await loadAppConfig(); + const nextInput = config as AppConfig; + if (nextInput.proxy.enabled) { + const certificateStatus = await proxyService.getCertificateStatus(); + if (!certificateStatus.trusted) { + throw new Error(certificateStatus.message); + } + } + let savedConfig = await saveAppConfig(nextInput); + const syncedClaudeAppConfig = await syncClaudeAppGatewayConfig(savedConfig); + savedConfig = syncedClaudeAppConfig.config; + let runtimeStatus = gatewayService.getStatus(); + if (syncedClaudeAppConfig.configChanged || shouldRestartForRuntimeChange(previousConfig, savedConfig)) { + runtimeStatus = await gatewayService.start(savedConfig); + } else { + gatewayService.updateConfig(savedConfig); + } + if ((options as AppSaveConfigOptions | undefined)?.applyProfile !== false) { + await applyProfileIfServiceRunning(savedConfig, runtimeStatus); + } + invalidateProviderAccountSnapshotCache(); + return savedConfig; + }, + scanBotHandoffBluetoothTargets: () => scanBotHandoffBluetoothTargets(), + scanBotHandoffWifiTargets: () => scanBotHandoffWifiTargets(), + selectPluginDirectory: (directory) => inspectPluginDirectory(readRequiredString(directory, "Plugin directory path is required.")), + setOnboardingFinished: async () => { + await replacePersistedAppSetting(onboardingFinishedAtSettingKey, new Date().toISOString()); + return true; + }, + setProxyNetworkCaptureEnabled: (enabled) => proxyService.setNetworkCaptureEnabled(Boolean(enabled)), + startBotGatewayQrLogin: (request) => startBotGatewayQrLogin(request as BotGatewayQrLoginStartRequest), + startGateway: async () => { + const syncedClaudeAppConfig = await syncClaudeAppGatewayConfig(await loadAppConfig()); + const config = syncedClaudeAppConfig.config; + const status = await gatewayService.start(config); + await applyProfileIfServiceRunning(config, status); + return status; + }, + stopGateway: () => gatewayService.stop(), + stopProfile: async (request) => stopProfileFromCcr(await loadAppConfig(), request as ProfileOpenRequest), + testProviderAccountConnector: (request) => testProviderAccountConnector(request as ProviderAccountTestRequest), + updateCheck: () => unsupportedUpdateStatus, + updateDownload: () => unsupportedUpdateStatus, + updateInstall: () => { + throw new Error("Updates are only available in the desktop app."); + }, + waitBotGatewayQrLogin: (request) => waitBotGatewayQrLogin(request as BotGatewayQrLoginWaitRequest) +}; + +async function startConfiguredServices(reason: string): Promise { + try { + let config = await loadAppConfig(); + try { + config = (await syncClaudeAppGatewayConfig(config)).config; + } catch (error) { + console.error(`Failed to sync Claude App gateway config during ${reason}: ${formatError(error)}`); + } + const status = await gatewayService.start(config); + if (status.state === "error") { + console.error(`Failed to start gateway during ${reason}: ${status.lastError}`); + } + if (status.state === "running") { + const profileResult = await applyProfileConfig(config); + logProfileApplyResult(profileResult); + } + if (config.proxy.enabled && config.proxy.systemProxy) { + const proxyStatus = await proxyService.ensureSystemProxyActive(); + if (proxyStatus.systemProxy.state !== "active") { + const details = proxyStatus.systemProxy.lastError ? `: ${proxyStatus.systemProxy.lastError}` : ""; + console.error(`Proxy mode is enabled, but system proxy is ${proxyStatus.systemProxy.state} during ${reason}${details}`); + } + } + } catch (error) { + console.error(`Failed to start configured services during ${reason}: ${formatError(error)}`); + } +} + +async function stopConfiguredServices(): Promise { + await gatewayService.stop({ proxyRestoreTimeoutMs: 30_000 }).catch((error) => { + console.error(`Failed to stop gateway: ${formatError(error)}`); + }); + try { + restoreClaudeAppGatewayConfig(); + } catch (error) { + console.error(`Failed to restore Claude App gateway config: ${formatError(error)}`); + } +} + +async function applyProfileIfServiceRunning(config: AppConfig, status: GatewayStatus): Promise { + if (status.state !== "running") { + return; + } + logProfileApplyResult(await applyProfileConfig(config)); +} + +function logProfileApplyResult(result: ProfileApplyResult): void { + for (const client of result.clients) { + if (!client.ok) { + console.warn(`[profile:${client.client}] ${client.message}`); + } + } +} + +function shouldRestartForRuntimeChange(previousConfig: AppConfig, nextConfig: AppConfig): boolean { + return ( + previousConfig.gateway.enabled !== nextConfig.gateway.enabled || + previousConfig.gateway.host !== nextConfig.gateway.host || + previousConfig.gateway.port !== nextConfig.gateway.port || + previousConfig.gateway.coreHost !== nextConfig.gateway.coreHost || + previousConfig.gateway.corePort !== nextConfig.gateway.corePort || + previousConfig.proxy.enabled !== nextConfig.proxy.enabled || + previousConfig.proxy.host !== nextConfig.proxy.host || + previousConfig.proxy.mode !== nextConfig.proxy.mode || + previousConfig.proxy.port !== nextConfig.proxy.port || + previousConfig.proxy.systemProxy !== nextConfig.proxy.systemProxy || + JSON.stringify(previousConfig.proxy.targets) !== JSON.stringify(nextConfig.proxy.targets) || + JSON.stringify(previousConfig.agent) !== JSON.stringify(nextConfig.agent) || + JSON.stringify(previousConfig.Providers) !== JSON.stringify(nextConfig.Providers) || + JSON.stringify(previousConfig.plugins) !== JSON.stringify(nextConfig.plugins) || + JSON.stringify(previousConfig.providerPlugins) !== JSON.stringify(nextConfig.providerPlugins) || + JSON.stringify(previousConfig.virtualModelProfiles) !== JSON.stringify(nextConfig.virtualModelProfiles) + ); +} + +function getCliAppInfo(): AppInfo { + return { + appConfigDbFile: APP_CONFIG_DB_FILE, + apiKeysDbFile: API_KEYS_DB_FILE, + configDir: CONFIGDIR, + configFile: CONFIG_FILE, + dataDir: DATADIR, + gatewayConfigFile: GATEWAY_CONFIG_FILE, + name: APP_NAME, + platform: process.platform, + requestLogsDbFile: REQUEST_LOGS_DB_FILE, + usageDbFile: USAGE_DB_FILE, + version: packageJson.version + }; +} + +function sendHomeHtml(response: ServerResponse, headOnly: boolean): void { + if (!existsSync(homeHtmlFile)) { + sendText(response, 500, "CCR renderer assets were not found. Run npm run build:assets first."); + return; + } + let html = readFileSync(homeHtmlFile, "utf8"); + if (!html.includes("web-client-bridge.js")) { + html = html.replace(' ', `${webBridgeScriptTag}\n `); + } + sendBuffer(response, 200, Buffer.from(html, "utf8"), "text/html; charset=utf-8", headOnly); +} + +function sendStaticFile(response: ServerResponse, root: string, relativePath: string, headOnly: boolean): void { + const normalizedRelativePath = relativePath.replace(/^[/\\]+/, ""); + const file = path.resolve(root, normalizedRelativePath); + const resolvedRoot = path.resolve(root); + if (!file.startsWith(`${resolvedRoot}${path.sep}`) && file !== resolvedRoot) { + sendText(response, 403, "Forbidden"); + return; + } + if (!isFile(file)) { + sendText(response, 404, "Not found"); + return; + } + sendBuffer(response, 200, readFileSync(file), contentTypeForFile(file), headOnly); +} + +function sendBuffer(response: ServerResponse, status: number, body: Buffer, contentType: string, headOnly: boolean): void { + response.writeHead(status, { + "cache-control": "no-store", + "content-length": body.length, + "content-type": contentType + }); + if (headOnly) { + response.end(); + return; + } + response.end(body); +} + +function sendJson(response: ServerResponse, status: number, payload: unknown): void { + const body = Buffer.from(JSON.stringify(payload), "utf8"); + response.writeHead(status, { + "cache-control": "no-store", + "content-length": body.length, + "content-type": "application/json; charset=utf-8" + }); + response.end(body); +} + +function sendText(response: ServerResponse, status: number, text: string): void { + sendBuffer(response, status, Buffer.from(`${text}\n`, "utf8"), "text/plain; charset=utf-8", false); +} + +function requestUrl(request: IncomingMessage): URL { + return new URL(request.url || "/", `http://${request.headers.host || "127.0.0.1"}`); +} + +function readRequestBody(request: IncomingMessage, maxBytes: number): Promise { + return new Promise((resolve, reject) => { + const chunks: Buffer[] = []; + let totalBytes = 0; + request.on("data", (chunk: Buffer) => { + totalBytes += chunk.length; + if (totalBytes > maxBytes) { + reject(new Error(`Request body exceeds ${maxBytes} bytes.`)); + request.destroy(); + return; + } + chunks.push(chunk); + }); + request.once("end", () => resolve(Buffer.concat(chunks))); + request.once("error", reject); + }); +} + +async function listenWithFallback(server: Server, port: number, host: string): Promise { + let candidate = port; + for (let attempt = 0; attempt < 20; attempt += 1) { + try { + await listen(server, candidate, host); + return candidate; + } catch (error) { + if (!isAddressInUseError(error) || candidate >= 65535) { + throw error; + } + candidate += 1; + } + } + throw new Error(`No available CCR web management port found starting at ${port}.`); +} + +function listen(server: Server, port: number, host: string): Promise { + return new Promise((resolve, reject) => { + const onError = (error: Error) => { + server.off("listening", onListening); + reject(error); + }; + const onListening = () => { + server.off("error", onError); + resolve(); + }; + server.once("error", onError); + server.once("listening", onListening); + server.listen(port, host); + }); +} + +function closeServer(server: Server): Promise { + return new Promise((resolve, reject) => { + server.close((error) => error ? reject(error) : resolve()); + }); +} + +async function exportAppData(): Promise { + const exportedAt = new Date().toISOString(); + const exportDir = defaultExportDir(); + mkdirSync(exportDir, { recursive: true }); + const file = path.join(exportDir, `claude-code-router-data-${fileSafeTimestamp(exportedAt)}.json`); + assertExportTargetIsNotInternalDataFile(file); + const payload = { + app: { + name: APP_NAME, + platform: process.platform, + version: packageJson.version + }, + appState: { + onboardingFinished: Boolean(readString(await loadPersistedAppSetting(onboardingFinishedAtSettingKey)) || existsSync(ONBOARDING_FINISHED_FILE)) + }, + config: await loadAppConfig(), + exportedAt, + files: readDataExportFiles(), + kind: "claude-code-router-data-export", + version: 1 + }; + + writeFileSync(file, `${JSON.stringify(payload, null, 2)}\n`, { encoding: "utf8", mode: 0o600 }); + return { + canceled: false, + exportedAt, + file + }; +} + +function defaultExportDir(): string { + const downloads = path.join(os.homedir(), "Downloads"); + return existsSync(downloads) ? downloads : CONFIGDIR; +} + +function readDataExportFiles(): Array<{ base64: string; name: string; path: string; sizeBytes: number }> { + const files: Array<{ base64: string; name: string; path: string; sizeBytes: number }> = []; + for (const file of dataExportCandidateFiles()) { + try { + if (!isFile(file)) { + continue; + } + const stat = statSync(file); + files.push({ + base64: readFileSync(file).toString("base64"), + name: path.basename(file), + path: file, + sizeBytes: stat.size + }); + } catch (error) { + console.warn(`[export] Failed to include ${file}: ${formatError(error)}`); + } + } + return files; +} + +function dataExportCandidateFiles(): string[] { + return uniqueStrings([ + ...sqliteDataFiles(APP_CONFIG_DB_FILE), + ...sqliteDataFiles(API_KEYS_DB_FILE), + ...sqliteDataFiles(REQUEST_LOGS_DB_FILE), + ...sqliteDataFiles(USAGE_DB_FILE) + ]); +} + +function sqliteDataFiles(file: string): string[] { + return [file, `${file}-wal`, `${file}-shm`]; +} + +function assertExportTargetIsNotInternalDataFile(file: string): void { + const target = path.resolve(file); + const reserved = new Set([ + CONFIG_FILE, + LEGACY_CONFIG_FILE, + APP_CONFIG_DB_FILE, + API_KEYS_DB_FILE, + REQUEST_LOGS_DB_FILE, + USAGE_DB_FILE, + ...dataExportCandidateFiles() + ].map((item) => path.resolve(item))); + if (reserved.has(target)) { + throw new Error("Choose a different export path. Internal CCR data files cannot be overwritten."); + } +} + +function inspectPluginDirectory(directory: string): PluginDirectorySelection { + const manifest = readFirstJson([ + path.join(directory, "plugin.json"), + path.join(directory, "ccr-plugin.json"), + path.join(directory, ".ccr-plugin", "plugin.json"), + path.join(directory, ".codex-plugin", "plugin.json") + ]); + const packageJsonManifest = readFirstJson([path.join(directory, "package.json")]); + const moduleValue = + readString(manifest?.module) || + readString(manifest?.main) || + readString(manifest?.path) || + readString(readRecord(packageJsonManifest?.ccr)?.module) || + readString(readRecord(packageJsonManifest?.ccrPlugin)?.module) || + readString(packageJsonManifest?.main); + const id = + pluginIdValue(readString(manifest?.id) || readString(manifest?.key) || readString(packageJsonManifest?.name)) || + pluginIdValue(path.basename(directory)) || + "plugin"; + const name = readString(manifest?.name) || readString(packageJsonManifest?.displayName) || readString(packageJsonManifest?.name); + const apps = readPluginApps(manifest, packageJsonManifest); + return { + ...(apps.length ? { apps } : {}), + dependencies: readPluginDependencies(directory, manifest, packageJsonManifest), + directory, + id, + modulePath: resolvePluginDirectoryModule(directory, moduleValue), + ...(name ? { name } : {}) + }; +} + +function readPluginApps( + manifest: Record | undefined, + packageJsonManifest: Record | undefined +): GatewayPluginAppConfig[] { + const values = [ + manifest?.apps, + readRecord(manifest?.ccr)?.apps, + readRecord(manifest?.ccrPlugin)?.apps, + readRecord(packageJsonManifest?.ccr)?.apps, + readRecord(packageJsonManifest?.ccrPlugin)?.apps + ]; + const apps = values.flatMap(parsePluginApps); + const byId = new Map(); + for (const app of apps) { + const key = app.id || `${app.name}:${app.url}`; + if (!byId.has(key)) { + byId.set(key, app); + } + } + return [...byId.values()]; +} + +function parsePluginApps(value: unknown): GatewayPluginAppConfig[] { + if (!Array.isArray(value)) { + return []; + } + return value.map(parsePluginAppItem).filter((item): item is GatewayPluginAppConfig => Boolean(item)); +} + +function parsePluginAppItem(value: unknown): GatewayPluginAppConfig | undefined { + if (!value || typeof value !== "object" || Array.isArray(value)) { + return undefined; + } + + const record = value as Record; + const name = readString(record.name) || readString(record.title); + const url = readString(record.url) || readString(record.href) || readString(record.target); + if (!name || !url) { + return undefined; + } + const id = pluginIdValue(readString(record.id) || name); + const description = readString(record.description); + const icon = readString(record.icon); + return { + ...(description ? { description } : {}), + ...(icon ? { icon } : {}), + ...(id ? { id } : {}), + name, + url + }; +} + +function readPluginDependencies( + directory: string, + manifest: Record | undefined, + packageJsonManifest: Record | undefined +): PluginDependency[] { + const values = [ + manifest?.dependencies, + manifest?.pluginDependencies, + readRecord(manifest?.ccr)?.dependencies, + readRecord(manifest?.ccrPlugin)?.dependencies, + readRecord(packageJsonManifest?.ccr)?.dependencies, + readRecord(packageJsonManifest?.ccrPlugin)?.dependencies + ]; + const dependencies = values.flatMap((value) => parsePluginDependencies(value, directory)); + const byId = new Map(); + for (const dependency of dependencies) { + if (dependency.id && !byId.has(dependency.id)) { + byId.set(dependency.id, dependency); + } + } + return [...byId.values()]; +} + +function parsePluginDependencies(value: unknown, directory: string): PluginDependency[] { + if (Array.isArray(value)) { + return value.map((item) => parsePluginDependencyItem(item, directory)).filter((item): item is PluginDependency => Boolean(item)); + } + if (value && typeof value === "object" && !Array.isArray(value)) { + return Object.entries(value as Record) + .map(([id, item]) => parsePluginDependencyEntry(id, item, directory)) + .filter((item): item is PluginDependency => Boolean(item)); + } + return []; +} + +function parsePluginDependencyEntry(idValue: string, value: unknown, directory: string): PluginDependency | undefined { + if (value && typeof value === "object" && !Array.isArray(value)) { + return parsePluginDependencyItem({ id: idValue, ...(value as Record) }, directory); + } + + const id = pluginIdValue(idValue); + if (!id) { + return undefined; + } + const specifier = readString(value); + const modulePath = specifier && looksLikeDependencyModulePath(specifier) ? resolveDependencyModulePath(directory, specifier) : undefined; + return { + id, + ...(modulePath ? { modulePath } : {}) + }; +} + +function parsePluginDependencyItem(value: unknown, directory: string): PluginDependency | undefined { + if (typeof value === "string") { + const id = pluginIdValue(value); + return id ? { id } : undefined; + } + if (!value || typeof value !== "object" || Array.isArray(value)) { + return undefined; + } + + const record = value as Record; + const id = pluginIdValue(readString(record.id) || readString(record.key) || readString(record.name)); + if (!id) { + return undefined; + } + const moduleValue = readString(record.module) || readString(record.path) || readString(record.modulePath); + const modulePath = moduleValue ? resolveDependencyModulePath(directory, moduleValue) : undefined; + const name = readString(record.name); + return { + id, + ...(modulePath ? { modulePath } : {}), + ...(name ? { name } : {}) + }; +} + +function resolveDependencyModulePath(directory: string, value: string): string { + if (value === "~" || value.startsWith("~/")) { + return path.join(os.homedir(), value.slice(2)); + } + return path.isAbsolute(value) ? value : path.join(directory, value); +} + +function looksLikeDependencyModulePath(value: string): boolean { + return value.startsWith(".") || value.startsWith("/") || value.startsWith("~"); +} + +function resolvePluginDirectoryModule(directory: string, moduleValue: string | undefined): string { + if (moduleValue) { + return path.isAbsolute(moduleValue) ? moduleValue : path.join(directory, moduleValue); + } + + for (const filename of ["index.cjs", "index.mjs", "index.js", "plugin.cjs", "plugin.mjs", "plugin.js"]) { + const candidate = path.join(directory, filename); + if (isFile(candidate)) { + return candidate; + } + } + + return directory; +} + +function readFirstJson(files: string[]): Record | undefined { + for (const file of files) { + if (!isFile(file)) { + continue; + } + try { + const parsed = JSON.parse(readFileSync(file, "utf8")) as unknown; + if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) { + return parsed as Record; + } + } catch { + // Ignore invalid plugin metadata and fall back to directory inference. + } + } + return undefined; +} + +function readRecord(value: unknown): Record | undefined { + return value && typeof value === "object" && !Array.isArray(value) ? value as Record : undefined; +} + +function firstConfiguredBrowserAppUrl(config: AppConfig): string | undefined { + for (const plugin of config.plugins) { + if (plugin.enabled === false) { + continue; + } + const app = plugin.apps?.find((candidate) => readString(candidate.url)); + if (app?.url) { + return app.url; + } + } + return undefined; +} + +async function revealFile(file: string): Promise { + if (process.platform === "darwin") { + await execDetached("/usr/bin/open", ["-R", file]); + return; + } + if (process.platform === "win32") { + await execDetached("explorer.exe", ["/select,", file]); + return; + } + await openSystemExternal(pathToFileURL(path.dirname(file)).toString()); +} + +function openSystemExternal(target: string): Promise { + if (!target || target === "about:blank") { + return Promise.resolve(); + } + if (process.platform === "darwin") { + return execDetached("/usr/bin/open", [target]); + } + if (process.platform === "win32") { + return execDetached("cmd.exe", ["/d", "/s", "/c", "start", "", target]); + } + return execDetached("xdg-open", [target]); +} + +function execDetached(command: string, args: string[]): Promise { + return new Promise((resolve, reject) => { + const child = spawn(command, args, { + detached: true, + stdio: "ignore", + windowsHide: true + }); + child.once("error", reject); + child.once("spawn", () => { + child.unref(); + resolve(); + }); + }); +} + +function contentTypeForFile(file: string): string { + switch (path.extname(file).toLowerCase()) { + case ".css": + return "text/css; charset=utf-8"; + case ".gif": + return "image/gif"; + case ".html": + return "text/html; charset=utf-8"; + case ".ico": + return "image/x-icon"; + case ".js": + return "text/javascript; charset=utf-8"; + case ".jpg": + case ".jpeg": + return "image/jpeg"; + case ".png": + return "image/png"; + case ".svg": + return "image/svg+xml"; + case ".webp": + return "image/webp"; + default: + return "application/octet-stream"; + } +} + +function readRequiredString(value: unknown, message: string): string { + const text = readString(value); + if (!text) { + throw new Error(message); + } + return text; +} + +function readString(value: unknown): string | undefined { + return typeof value === "string" && value.trim() ? value.trim() : undefined; +} + +function readEnvString(key: string): string | undefined { + return readString(process.env[key]); +} + +function readEnvPort(key: string): number | undefined { + const value = Number(process.env[key]); + return Number.isInteger(value) && value > 0 && value < 65536 ? value : undefined; +} + +function pluginIdValue(value: string | undefined): string { + return value?.toLowerCase().replace(/^@/, "").replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || ""; +} + +function isFile(file: string): boolean { + try { + return existsSync(file) && statSync(file).isFile(); + } catch { + return false; + } +} + +function uniqueStrings(values: string[]): string[] { + const seen = new Set(); + const result: string[] = []; + for (const value of values) { + if (!value || seen.has(value)) { + continue; + } + seen.add(value); + result.push(value); + } + return result; +} + +function fileSafeTimestamp(value: string): string { + return value.replace(/[:.]/g, "-"); +} + +function formatListenHost(host: string): string { + return host.includes(":") && !host.startsWith("[") ? `[${host}]` : host; +} + +function isAddressInUseError(error: unknown): boolean { + return typeof error === "object" && error !== null && "code" in error && (error as { code?: unknown }).code === "EADDRINUSE"; +} + +function formatError(error: unknown): string { + return error instanceof Error ? error.message : String(error); +} diff --git a/src/server/mcp/network-capture-mcp.ts b/src/server/mcp/network-capture-mcp.ts index bbc3601..21759ce 100644 --- a/src/server/mcp/network-capture-mcp.ts +++ b/src/server/mcp/network-capture-mcp.ts @@ -1,4 +1,5 @@ -import { app } from "electron"; +import * as electron from "electron"; +import packageJson from "../../../package.json"; import type { IncomingMessage, ServerResponse } from "node:http"; import type { ProxyNetworkExchange } from "../../shared/app"; import { proxyService } from "../proxy/service"; @@ -151,7 +152,7 @@ async function handleJsonRpcRequest(payload: unknown): Promise { if (!proxyService.isNetworkCaptureEnabled()) { throw new Error("Network capture MCP is disabled."); diff --git a/src/server/proxy/service.ts b/src/server/proxy/service.ts index 2af626f..eb8f031 100644 --- a/src/server/proxy/service.ts +++ b/src/server/proxy/service.ts @@ -1,6 +1,6 @@ import { execFile } from "node:child_process"; import { randomUUID } from "node:crypto"; -import { BrowserWindow, dialog, shell } from "electron"; +import * as electron from "electron"; import { chmodSync, writeFileSync } from "node:fs"; import http, { type ClientRequest, type IncomingHttpHeaders, type IncomingMessage, type Server, type ServerResponse } from "node:http"; import https from "node:https"; @@ -398,7 +398,7 @@ class ProxyService { const installerFile = await openMacosTerminalCertificateInstaller(); terminalMessage = ` Opened Terminal installer: ${installerFile}`; } catch (terminalError) { - shell.showItemInFolder(PROXY_CA_CERT_FILE); + electron.shell?.showItemInFolder?.(PROXY_CA_CERT_FILE); terminalMessage = ` Could not open Terminal installer: ${formatError(terminalError)}`; } const status = await this.getCertificateStatus(); @@ -1470,7 +1470,10 @@ function execFilePromise(file: string, args: string[]): Promise { } async function requestMacosCertificateInstallPermission(): Promise { - const window = BrowserWindow.getFocusedWindow() ?? BrowserWindow.getAllWindows()[0]; + if (!electron.dialog || !electron.BrowserWindow) { + return true; + } + const window = electron.BrowserWindow.getFocusedWindow() ?? electron.BrowserWindow.getAllWindows()[0]; const options = { buttons: ["Continue", "Cancel"], cancelId: 1, @@ -1481,7 +1484,7 @@ async function requestMacosCertificateInstallPermission(): Promise { noLink: true, type: "warning" as const }; - const result = window ? await dialog.showMessageBox(window, options) : await dialog.showMessageBox(options); + const result = window ? await electron.dialog.showMessageBox(window, options) : await electron.dialog.showMessageBox(options); return result.response === 0; } @@ -1552,10 +1555,14 @@ async function openMacosTerminalCertificateInstaller(): Promise { const installerFile = path.join(os.tmpdir(), `ccr-install-proxy-ca-${randomUUID()}.command`); writeFileSync(installerFile, `${macosTerminalCertificateInstallScript()}\n`, "utf8"); chmodSync(installerFile, 0o700); - const errorMessage = await shell.openPath(installerFile); - if (errorMessage) { - throw new Error(errorMessage); + if (electron.shell?.openPath) { + const errorMessage = await electron.shell.openPath(installerFile); + if (errorMessage) { + throw new Error(errorMessage); + } + return installerFile; } + await execFilePromise("/usr/bin/open", [installerFile]); return installerFile; } From 1534059fd6becc8880f6f43b16018d2837e3a5bf Mon Sep 17 00:00:00 2001 From: musistudio Date: Mon, 29 Jun 2026 16:45:47 +0800 Subject: [PATCH 06/94] Refactor router config and provider handling --- .github/workflows/release.yml | 106 ++++++- README.md | 3 +- README_zh.md | 3 +- build/esbuild.config.mjs | 14 +- build/merge-macos-update-metadata.mjs | 78 +++++ src/main/app-paths.ts | 65 ++++ src/main/claude-app-gateway-service.ts | 44 ++- src/main/cli.ts | 29 +- src/main/codex-cli-middleware-runtime.ts | 47 ++- src/main/constants.ts | 28 +- src/main/electron-node-shim.ts | 14 - src/main/main-app.ts | 246 +++++++++++++++ src/main/main.ts | 219 +------------ src/main/profile-launch-service.ts | 374 +++++++++++++++++++++-- src/main/profile-service.ts | 9 + src/server/gateway/service.ts | 174 +++++++++-- src/server/mcp/network-capture-mcp.ts | 3 +- src/server/proxy/service.ts | 66 ++-- 18 files changed, 1148 insertions(+), 374 deletions(-) create mode 100644 build/merge-macos-update-metadata.mjs create mode 100644 src/main/app-paths.ts delete mode 100644 src/main/electron-node-shim.ts create mode 100644 src/main/main-app.ts diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5f2cbbd..409612d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,8 +14,24 @@ concurrency: jobs: macos: - name: macOS - runs-on: macos-14 + name: macOS (${{ matrix.display_name }}) + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: false + matrix: + include: + - arch: arm64 + arch_flag: --arm64 + runner: macos-14 + display_name: Apple Silicon + release_label: Apple-Silicon + artifact_name: macos-apple-silicon-arm64 + - arch: x64 + arch_flag: --x64 + runner: macos-15-intel + display_name: Intel + release_label: Intel + artifact_name: macos-intel-x64 steps: - name: Check out repository uses: actions/checkout@v4 @@ -39,16 +55,92 @@ jobs: exit 1 fi - - name: Build and publish ad-hoc macOS artifacts + - name: Build ad-hoc macOS artifacts + run: | + npm run build:assets + npx electron-builder \ + --config build/electron-builder.local.cjs \ + --mac ${{ matrix.arch_flag }} \ + --publish never \ + -c.mac.artifactName='Claude-Code-Router_${version}-mac-${{ matrix.release_label }}-${arch}.${ext}' + + - name: Upload macOS release files + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.artifact_name }} + path: | + release-local/Claude-Code-Router_*-mac-${{ matrix.release_label }}-${{ matrix.arch }}.* + release-local/latest-mac.yml + if-no-files-found: error + + publish-macos: + name: Publish macOS + needs: macos + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + + - name: Install metadata merge dependencies + run: npm ci --ignore-scripts + + - name: Download macOS release files + uses: actions/download-artifact@v4 + with: + pattern: macos-* + path: macos-release-artifacts + + - name: Merge macOS update metadata + run: node build/merge-macos-update-metadata.mjs latest-mac.yml macos-release-artifacts/*/latest-mac.yml + + - name: Create GitHub release env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - npm run build:assets - npx electron-builder --config build/electron-builder.local.cjs --mac --publish always + gh release view "$GITHUB_REF_NAME" >/dev/null 2>&1 || \ + gh release create "$GITHUB_REF_NAME" --title "$GITHUB_REF_NAME" --generate-notes + + - name: Upload macOS release assets + shell: bash + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + shopt -s nullglob + assets=(macos-release-artifacts/*/Claude-Code-Router_*-mac-*.*) + if (( ${#assets[@]} == 0 )); then + echo "No macOS release assets were found." + exit 1 + fi + + upload_args=() + for asset in "${assets[@]}"; do + name="$(basename "$asset")" + case "$name" in + *Apple-Silicon-arm64.dmg) label="macOS Apple Silicon DMG (arm64)" ;; + *Apple-Silicon-arm64.zip) label="macOS Apple Silicon ZIP (arm64)" ;; + *Apple-Silicon-arm64.dmg.blockmap) label="macOS Apple Silicon DMG blockmap (arm64)" ;; + *Apple-Silicon-arm64.zip.blockmap) label="macOS Apple Silicon ZIP blockmap (arm64)" ;; + *Intel-x64.dmg) label="macOS Intel DMG (x64)" ;; + *Intel-x64.zip) label="macOS Intel ZIP (x64)" ;; + *Intel-x64.dmg.blockmap) label="macOS Intel DMG blockmap (x64)" ;; + *Intel-x64.zip.blockmap) label="macOS Intel ZIP blockmap (x64)" ;; + *) label="$name" ;; + esac + upload_args+=("${asset}#${label}") + done + upload_args+=("latest-mac.yml#macOS update metadata") + + gh release upload "$GITHUB_REF_NAME" "${upload_args[@]}" --clobber windows: name: Windows - needs: macos + needs: publish-macos runs-on: windows-latest steps: - name: Check out repository @@ -82,7 +174,7 @@ jobs: linux: name: Linux - needs: macos + needs: publish-macos runs-on: ubuntu-latest steps: - name: Check out repository diff --git a/README.md b/README.md index c123f22..1e4bde0 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,8 @@ The web management UI listens on `http://127.0.0.1:3458` by default. Use `ccr st 1. Open the [GitHub Releases page](https://github.com/musistudio/claude-code-router/releases). 2. Download the package for your platform: - - macOS: `Claude Code Router_.dmg` or `.zip` + - macOS Apple Silicon: `Claude-Code-Router_-mac-Apple-Silicon-arm64.dmg` or `.zip` + - macOS Intel: `Claude-Code-Router_-mac-Intel-x64.dmg` or `.zip` - Windows: `Claude Code Router_.exe` - Linux: `Claude Code Router_.AppImage` 3. Install and launch **Claude Code Router**. diff --git a/README_zh.md b/README_zh.md index 45f98cf..7c94376 100644 --- a/README_zh.md +++ b/README_zh.md @@ -62,7 +62,8 @@ Web 管理端默认监听 `http://127.0.0.1:3458`。可以用 `ccr start --host 1. 打开 [GitHub Releases 页面](https://github.com/musistudio/claude-code-router/releases)。 2. 按系统下载对应安装包: - - macOS:`Claude Code Router_.dmg` 或 `.zip` + - macOS Apple 芯片:`Claude-Code-Router_-mac-Apple-Silicon-arm64.dmg` 或 `.zip` + - macOS Intel 芯片:`Claude-Code-Router_-mac-Intel-x64.dmg` 或 `.zip` - Windows:`Claude Code Router_.exe` - Linux:`Claude Code Router_.AppImage` 3. 安装并启动 **Claude Code Router**。 diff --git a/build/esbuild.config.mjs b/build/esbuild.config.mjs index 3283c23..9fc975f 100644 --- a/build/esbuild.config.mjs +++ b/build/esbuild.config.mjs @@ -140,7 +140,7 @@ export function createCliBuildOptions({ mode = "production", plugins = [] } = {} minify: mode === "production", outdir: mainOutDir, platform: "node", - plugins: [electronNodeShimPlugin(), ...plugins], + plugins: [forbidCliElectronPlugin(), ...plugins], sourcemap: mode !== "production", target: "node22" }; @@ -292,12 +292,18 @@ function rendererAliasPlugin() { }; } -function electronNodeShimPlugin() { +function forbidCliElectronPlugin() { return { - name: "electron-node-shim", + name: "forbid-cli-electron", setup(build) { build.onResolve({ filter: /^electron$/ }, () => { - return { path: path.join(projectRoot, "src", "main", "electron-node-shim.ts") }; + return { + errors: [ + { + text: "CLI bundle must not import electron. Move the dependency behind a desktop-only boundary." + } + ] + }; }); } }; diff --git a/build/merge-macos-update-metadata.mjs b/build/merge-macos-update-metadata.mjs new file mode 100644 index 0000000..d1e2da0 --- /dev/null +++ b/build/merge-macos-update-metadata.mjs @@ -0,0 +1,78 @@ +import { readFileSync, writeFileSync } from "node:fs"; +import path from "node:path"; +import yaml from "js-yaml"; + +const [outputFile, ...inputFiles] = process.argv.slice(2); + +if (!outputFile || inputFiles.length < 2) { + console.error("Usage: node build/merge-macos-update-metadata.mjs [...]"); + process.exit(1); +} + +const updateInfos = inputFiles.map((file) => { + const info = yaml.load(readFileSync(file, "utf8")); + if (!info || typeof info !== "object") { + throw new Error(`${file} is not a valid update metadata object`); + } + if (!Array.isArray(info.files) || info.files.length === 0) { + throw new Error(`${file} does not contain any update files`); + } + return { file, info }; +}); + +const version = updateInfos[0].info.version; +if (!version || updateInfos.some(({ info }) => info.version !== version)) { + throw new Error("All macOS update metadata files must have the same version"); +} + +const files = uniqueByUrl( + updateInfos + .flatMap(({ info }) => info.files) + .filter((file) => file && typeof file.url === "string") + .sort(compareMacUpdateFile) +); + +const defaultZip = files.find((file) => file.url.endsWith(".zip") && file.url.includes("arm64")) ?? files.find((file) => file.url.endsWith(".zip")); +if (!defaultZip?.sha512) { + throw new Error("Merged macOS update metadata must include at least one ZIP with sha512"); +} + +const releaseDate = updateInfos + .map(({ info }) => info.releaseDate) + .filter(Boolean) + .sort() + .at(-1); + +const { files: _files, path: _path, sha512: _sha512, releaseDate: _releaseDate, ...baseInfo } = updateInfos[0].info; +const mergedInfo = { + ...baseInfo, + version, + files, + path: defaultZip.url, + sha512: defaultZip.sha512, + ...(releaseDate ? { releaseDate } : {}) +}; + +writeFileSync(outputFile, yaml.dump(mergedInfo, { lineWidth: 120, noRefs: true }), "utf8"); +console.log(`Wrote ${path.relative(process.cwd(), outputFile)} with ${files.length} macOS artifacts.`); + +function uniqueByUrl(files) { + const seen = new Set(); + return files.filter((file) => { + if (seen.has(file.url)) { + return false; + } + seen.add(file.url); + return true; + }); +} + +function compareMacUpdateFile(left, right) { + return fileRank(left) - fileRank(right) || left.url.localeCompare(right.url); +} + +function fileRank(file) { + const isZip = file.url.endsWith(".zip") ? 0 : 10; + const isAppleSilicon = file.url.includes("arm64") ? 0 : 1; + return isZip + isAppleSilicon; +} diff --git a/src/main/app-paths.ts b/src/main/app-paths.ts new file mode 100644 index 0000000..1b0a299 --- /dev/null +++ b/src/main/app-paths.ts @@ -0,0 +1,65 @@ +import os from "node:os"; +import path from "node:path"; + +export const APP_NAME = "Claude Code Router"; +export const LEGACY_CONFIGDIR = path.join(os.homedir(), ".claude-code-router"); + +const homeDirEnv = "CCR_INTERNAL_HOME_DIR"; +const appDataDirEnv = "CCR_INTERNAL_APP_DATA_DIR"; +const userDataDirEnv = "CCR_INTERNAL_USER_DATA_DIR"; + +type RuntimePathName = "appData" | "home" | "userData"; + +export type RuntimeAppPaths = Partial>; + +export function setRuntimeAppPaths(paths: RuntimeAppPaths): void { + setPathEnv(homeDirEnv, paths.home); + setPathEnv(appDataDirEnv, paths.appData); + setPathEnv(userDataDirEnv, paths.userData); +} + +export function resolveRuntimeAppPath(name: RuntimePathName): string { + const configured = readConfiguredPath(name); + if (configured) { + return configured; + } + if (name === "home") { + return os.homedir(); + } + if (name === "appData") { + return fallbackAppDataDir(); + } + return fallbackUserDataDir(); +} + +function readConfiguredPath(name: RuntimePathName): string | undefined { + const key = name === "home" + ? homeDirEnv + : name === "appData" + ? appDataDirEnv + : userDataDirEnv; + const value = process.env[key]?.trim(); + return value || undefined; +} + +function setPathEnv(key: string, value: string | undefined): void { + if (value?.trim()) { + process.env[key] = value; + } +} + +function fallbackAppDataDir(): string { + if (process.platform === "win32") { + return process.env.APPDATA || + process.env.LOCALAPPDATA || + (process.env.USERPROFILE ? path.join(process.env.USERPROFILE, "AppData", "Roaming") : path.join(os.homedir(), "AppData", "Roaming")); + } + return process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config"); +} + +function fallbackUserDataDir(): string { + if (process.platform === "win32") { + return path.join(fallbackAppDataDir(), APP_NAME); + } + return path.join(LEGACY_CONFIGDIR, "app-data"); +} diff --git a/src/main/claude-app-gateway-service.ts b/src/main/claude-app-gateway-service.ts index 66da1af..3a56652 100644 --- a/src/main/claude-app-gateway-service.ts +++ b/src/main/claude-app-gateway-service.ts @@ -1,8 +1,8 @@ -import * as electron from "electron"; import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import os from "node:os"; import path from "node:path"; import { createHash, randomBytes, randomUUID } from "node:crypto"; +import { resolveRuntimeAppPath } from "./app-paths"; import { saveAppConfig } from "./config"; import { CONFIGDIR } from "./constants"; import { @@ -174,6 +174,33 @@ export function restoreClaudeAppGatewayConfig(): void { rmSync(CLAUDE_APP_GATEWAY_BACKUP_FILE, { force: true }); } +export function readClaudeAppGatewayApiKeyCandidates(): string[] { + return uniqueStrings([ + readClaudeAppGatewayApiKey(getClaudeAppGatewayPaths().configLibraryFile) + ]); +} + +function readClaudeAppGatewayApiKey(file: string): string { + const config = readJsonRecord(file); + return typeof config?.inferenceGatewayApiKey === "string" + ? config.inferenceGatewayApiKey.trim() + : ""; +} + +function uniqueStrings(values: string[]): string[] { + const seen = new Set(); + const result: string[] = []; + for (const value of values) { + const normalized = value.trim(); + if (!normalized || seen.has(normalized)) { + continue; + } + seen.add(normalized); + result.push(normalized); + } + return result; +} + function ensureClaudeAppGatewayState(config: AppConfig): ClaudeAppApplyState { const currentApiKey = findReusableApiKey(config); const gatewayEnabledConfig = config.gateway.enabled @@ -246,20 +273,7 @@ function getClaudeApp3pDataDir(): string { } function appPath(name: "appData" | "home"): string { - const electronApp = electronAppOrUndefined(); - if (electronApp) { - return electronApp.getPath(name); - } - if (name === "home") { - return os.homedir(); - } - return process.env.APPDATA || - process.env.LOCALAPPDATA || - (process.env.USERPROFILE ? path.join(process.env.USERPROFILE, "AppData", "Roaming") : path.join(os.homedir(), ".config")); -} - -function electronAppOrUndefined(): Electron.App | undefined { - return typeof electron.app?.getPath === "function" ? electron.app : undefined; + return resolveRuntimeAppPath(name); } function backupClaudeAppGatewayConfig(paths: ClaudeAppGatewayPaths): void { diff --git a/src/main/cli.ts b/src/main/cli.ts index 3d745ca..225b667 100644 --- a/src/main/cli.ts +++ b/src/main/cli.ts @@ -7,6 +7,8 @@ import { botGatewayProfileEnv } from "./bot-gateway-env"; import { launchCodexAppProfile, launchZcodeAppProfile } from "./codex-app-launch"; import { loadAppConfig } from "./config"; import { CONFIGDIR } from "./constants"; +import { applyProfileConfig, applyProfileRuntimeConfig } from "./profile-service"; +import { ensureProfileGateway } from "./profile-launch-service"; import { buildProfileLaunchPlan, findProfileForOpen, profileLaunchSpawnCommand, resolveProfileOpenSurface } from "./profile-launch-core"; import { startWebManagementServer } from "./web-management-server"; @@ -88,9 +90,19 @@ async function main(): Promise { const configDir = CONFIGDIR; const config = await loadAppConfig(); + await applyProfileConfig(config); const profile = findProfileForOpen(config, profileOptions.profileRef); const surface = profileOptions.surface ?? (profile.agent === "zcode" || profile.surface === "app" ? "app" : "cli"); const resolvedSurface = resolveProfileOpenSurface(profile, surface); + const launchConfig = resolvedSurface === "cli" + ? await ensureProfileGateway(config, profile, profile.name || profile.id || "profile", { reuseExisting: true, startIfMissing: false }) + : config; + if (resolvedSurface === "cli") { + const runtimeResult = applyProfileRuntimeConfig(launchConfig, profile, launchConfig.APIKEY); + if (!runtimeResult.ok) { + throw new Error(runtimeResult.message); + } + } if (profile.agent === "zcode" && profileOptions.agentArgs.length > 0) { throw new Error("ZCode profiles can only open the app; agent arguments are not supported."); } @@ -122,7 +134,7 @@ async function main(): Promise { const childEnv = { ...process.env, ...plan.env, - ...botGatewayProfileEnv(config, profile, resolvedSurface) + ...botGatewayProfileEnv(launchConfig, profile, resolvedSurface) }; delete childEnv.ELECTRON_RUN_AS_NODE; @@ -275,10 +287,7 @@ async function startService(options: WebCliOptions): Promise { ]; const child = spawn(process.execPath, childArgs, { detached: true, - env: { - ...process.env, - ELECTRON_RUN_AS_NODE: undefined - }, + env: serviceChildEnv(), stdio: "ignore", windowsHide: true }); @@ -295,6 +304,16 @@ async function startService(options: WebCliOptions): Promise { process.stdout.write(`CCR service started at ${state.url} (pid ${state.pid}).\n`); } +function serviceChildEnv(): NodeJS.ProcessEnv { + const env = { ...process.env }; + if (process.versions.electron) { + env.ELECTRON_RUN_AS_NODE = "1"; + } else { + delete env.ELECTRON_RUN_AS_NODE; + } + return env; +} + async function runWebServer(options: WebCliOptions): Promise { const runtime = await startWebManagementServer({ host: options.host, diff --git a/src/main/codex-cli-middleware-runtime.ts b/src/main/codex-cli-middleware-runtime.ts index fcd0a11..0813c76 100644 --- a/src/main/codex-cli-middleware-runtime.ts +++ b/src/main/codex-cli-middleware-runtime.ts @@ -58,6 +58,7 @@ async function main() { async function runClaudeCodeCliWrapper(args) { const realCli = expandHome(nonEmptyEnv("CCR_REAL_CLAUDE_CODE_BIN") || nonEmptyEnv("CCR_CLAUDE_CODE_BIN") || nonEmptyEnv("CODEXL_CLAUDE_CODE_BIN") || "claude"); log("claude_code_wrapper_start", { realCli, args }); + const captureStdout = shouldCaptureClaudeCodeCliStdout(args); const remoteSync = createRemoteSyncClient({ args, cwd: process.cwd(), @@ -67,7 +68,7 @@ async function runClaudeCodeCliWrapper(args) { const injectRemoteStdin = boolEnv("CCR_REMOTE_SYNC_INJECT_STDIN"); const child = childProcess.spawn(realCli, args, { env: withoutKeys(process.env, ["CCR_CLAUDE_CODE_WRAPPER", "CCR_REAL_CLAUDE_CODE_BIN"]), - stdio: [injectRemoteStdin ? "pipe" : "inherit", "pipe", "inherit"] + stdio: [injectRemoteStdin ? "pipe" : "inherit", captureStdout ? "pipe" : "inherit", "inherit"] }); if (injectRemoteStdin && child.stdin) { process.stdin.pipe(child.stdin); @@ -88,18 +89,20 @@ async function runClaudeCodeCliWrapper(args) { remoteSync.postEvent("claude.spawn.error", { error: formatError(error) }, { direction: "system" }); }); let pending = ""; - child.stdout.on("data", (chunk) => { - process.stdout.write(chunk); - pending += chunk.toString("utf8"); - const lines = pending.split(/\r?\n/g); - pending = lines.pop() || ""; - for (const line of lines) { - botBridge().handleClaudeCliLine(line); - remoteSync.postEvent("claude.stdout", { line }, { text: line }); - } - }); + if (captureStdout && child.stdout) { + child.stdout.on("data", (chunk) => { + process.stdout.write(chunk); + pending += chunk.toString("utf8"); + const lines = pending.split(/\r?\n/g); + pending = lines.pop() || ""; + for (const line of lines) { + botBridge().handleClaudeCliLine(line); + remoteSync.postEvent("claude.stdout", { line }, { text: line }); + } + }); + } const code = await waitForChild(child); - if (pending.trim()) { + if (captureStdout && pending.trim()) { botBridge().handleClaudeCliLine(pending); remoteSync.postEvent("claude.stdout", { line: pending }, { text: pending }); } @@ -109,6 +112,26 @@ async function runClaudeCodeCliWrapper(args) { process.exitCode = code; } +function shouldCaptureClaudeCodeCliStdout(args) { + if (boolEnv("CCR_CLAUDE_CODE_CAPTURE_STDOUT") || boolEnv("CODEXL_CLAUDE_CODE_CAPTURE_STDOUT")) { + return true; + } + if (botGatewayCliCaptureEnabled()) { + return true; + } + return claudeCodeArgsUsePrintMode(args); +} + +function botGatewayCliCaptureEnabled() { + const enabled = boolEnv("CCR_BOT_GATEWAY_ENABLED") || boolEnv("CODEXL_BOT_GATEWAY_ENABLED"); + const platform = normalizeBotGatewayPlatform(nonEmptyEnv("CCR_BOT_GATEWAY_PLATFORM") || nonEmptyEnv("CODEXL_BOT_GATEWAY_PLATFORM") || "none"); + return enabled && platform !== "none"; +} + +function claudeCodeArgsUsePrintMode(args) { + return args.some((arg) => arg === "--print" || arg === "-p"); +} + function defaultCodexArgs() { return normalizeProfileSurface(nonEmptyEnv("CCR_PROFILE_SURFACE") || nonEmptyEnv("CODEXL_PROFILE_SURFACE")) === "cli" ? [] diff --git a/src/main/constants.ts b/src/main/constants.ts index ba05680..a7b0bf8 100644 --- a/src/main/constants.ts +++ b/src/main/constants.ts @@ -1,35 +1,17 @@ -import * as electron from "electron"; -import os from "node:os"; import path from "node:path"; +import { APP_NAME, LEGACY_CONFIGDIR, resolveRuntimeAppPath } from "./app-paths"; + export { IPC_CHANNELS } from "../shared/ipc-channels"; - -export const APP_NAME = "Claude Code Router"; - -const electronApp = typeof electron.app?.getPath === "function" ? electron.app : undefined; -export const LEGACY_CONFIGDIR = path.join(os.homedir(), ".claude-code-router"); export const LEGACY_CONFIG_FILE = path.join(LEGACY_CONFIGDIR, "config.json"); -function appPath(name: "appData" | "home" | "userData"): string { - if (electronApp) { - return electronApp.getPath(name); - } - if (name === "home") { - return os.homedir(); - } - if (name === "appData") { - return process.env.APPDATA || - process.env.LOCALAPPDATA || - (process.env.USERPROFILE ? path.join(process.env.USERPROFILE, "AppData", "Roaming") : path.join(os.homedir(), "AppData", "Roaming")); - } - return path.join(LEGACY_CONFIGDIR, "app-data"); -} +export { APP_NAME, LEGACY_CONFIGDIR }; export const CONFIGDIR = process.platform === "win32" - ? path.join(appPath("appData"), APP_NAME) + ? path.join(resolveRuntimeAppPath("appData"), APP_NAME) : LEGACY_CONFIGDIR; export const CONFIG_FILE = path.join(CONFIGDIR, "config.json"); export const ONBOARDING_FINISHED_FILE = path.join(CONFIGDIR, ".onboard_finished"); -export const DATADIR = appPath("userData"); +export const DATADIR = resolveRuntimeAppPath("userData"); export const APP_CONFIG_DB_FILE = path.join(CONFIGDIR, "config.sqlite"); export const API_KEYS_DB_FILE = path.join(DATADIR, "api-keys.sqlite"); export const CERTDIR = path.join(DATADIR, "certs"); diff --git a/src/main/electron-node-shim.ts b/src/main/electron-node-shim.ts deleted file mode 100644 index 212ed6a..0000000 --- a/src/main/electron-node-shim.ts +++ /dev/null @@ -1,14 +0,0 @@ -export const app = undefined; -export const BrowserWindow = undefined; -export const clipboard = undefined; -export const contextBridge = undefined; -export const dialog = undefined; -export const ipcMain = undefined; -export const ipcRenderer = undefined; -export const Menu = undefined; -export const nativeImage = undefined; -export const screen = undefined; -export const session = undefined; -export const shell = undefined; -export const Tray = undefined; -export const WebContentsView = undefined; diff --git a/src/main/main-app.ts b/src/main/main-app.ts new file mode 100644 index 0000000..72f63d5 --- /dev/null +++ b/src/main/main-app.ts @@ -0,0 +1,246 @@ +import { app, BrowserWindow, dialog, shell } from "electron"; +import { setupApplicationMenu } from "./app-menu"; +import { loadAppConfig } from "./config"; +import { restoreClaudeAppGatewayConfig, syncClaudeAppGatewayConfig } from "./claude-app-gateway-service"; +import { deepLinkService } from "./deep-link"; +import { gatewayService } from "../server/gateway/service"; +import "./ipc"; +import { applyProfileConfig } from "./profile-service"; +import { ensureCcrCliLauncher } from "./profile-launch-service"; +import { proxyService } from "../server/proxy/service"; +import trayController from "./tray-controller"; +import { appUpdateService } from "./update-service"; +import windowsManager from "./windows"; + +const gotTheLock = app.requestSingleInstanceLock(); +const quitProxyRestoreTimeoutMs = 30_000; +let quitPrepared = false; +let stoppingForQuit = false; +let ensureProxyModePromise: Promise | undefined; +let startServicesPromise: Promise | undefined; +let stopForQuitPromise: Promise | undefined; + +if (!gotTheLock) { + app.quit(); +} else { + startPrimaryInstance(); +} + +function startPrimaryInstance(): void { + deepLinkService.register(); + deepLinkService.handleArgv(process.argv); + + app.on("second-instance", (_event, argv) => { + windowsManager.showMainWindow(); + deepLinkService.handleArgv(argv); + queueEnsureConfiguredProxyModeActive("second-instance"); + }); + + app.whenReady().then(() => { + configureProxyDesktopIntegration(); + try { + ensureCcrCliLauncher(); + } catch (error) { + console.error(`Failed to install ccr CLI launcher: ${formatError(error)}`); + } + setupApplicationMenu(); + windowsManager.createMainWindow(); + trayController.start(); + appUpdateService.start(); + appUpdateService.setInstallPreparation(prepareForUpdateInstall); + void startConfiguredServices("startup"); + + app.on("activate", () => { + windowsManager.showMainWindow(); + queueEnsureConfiguredProxyModeActive("activate"); + }); + }); + + app.on("before-quit", (event) => { + if (quitPrepared || appUpdateService.isInstallingUpdate()) { + return; + } + event.preventDefault(); + prepareAndQuit(); + }); + + app.on("will-quit", (event) => { + if (quitPrepared || appUpdateService.isInstallingUpdate()) { + return; + } + event.preventDefault(); + prepareAndQuit(); + }); + + app.on("window-all-closed", () => { + if (process.platform !== "darwin") { + app.quit(); + } + }); + + process.once("SIGINT", () => handleTerminationSignal("SIGINT")); + process.once("SIGTERM", () => handleTerminationSignal("SIGTERM")); +} + +function configureProxyDesktopIntegration(): void { + proxyService.setDesktopIntegration({ + requestMacosCertificateInstallPermission, + revealFile: async (file) => { + shell.showItemInFolder(file); + } + }); +} + +async function requestMacosCertificateInstallPermission(): Promise { + const window = BrowserWindow.getFocusedWindow() ?? BrowserWindow.getAllWindows()[0]; + const options = { + buttons: ["Continue", "Cancel"], + cancelId: 1, + defaultId: 0, + detail: + "macOS will ask for administrator credentials. This is required so Chrome can trust HTTPS certificates generated by CCR proxy mode.", + message: "Install CCR Proxy CA into the macOS System keychain?", + noLink: true, + type: "warning" as const + }; + const result = window ? await dialog.showMessageBox(window, options) : await dialog.showMessageBox(options); + return result.response === 0; +} + +function prepareAndQuit(): void { + if (stoppingForQuit) { + return; + } + + stoppingForQuit = true; + void stopServicesForQuit().finally(() => { + quitPrepared = true; + app.quit(); + }); +} + +async function prepareForUpdateInstall(): Promise { + if (!stoppingForQuit) { + stoppingForQuit = true; + } + await stopServicesForQuit(); + quitPrepared = true; +} + +function handleTerminationSignal(signal: NodeJS.Signals): void { + if (stoppingForQuit) { + return; + } + + stoppingForQuit = true; + void stopServicesForQuit().finally(() => { + quitPrepared = true; + app.exit(signal === "SIGINT" ? 130 : 143); + }); +} + +function stopServicesForQuit(): Promise { + if (!stopForQuitPromise) { + stopForQuitPromise = gatewayService + .stop({ proxyRestoreTimeoutMs: quitProxyRestoreTimeoutMs }) + .then(() => undefined) + .catch((error) => { + console.error(`Failed to stop services before quit: ${formatError(error)}`); + }) + .finally(() => { + try { + restoreClaudeAppGatewayConfig(); + } catch (error) { + console.error(`Failed to restore Claude App gateway config before quit: ${formatError(error)}`); + } + trayController.destroy(); + }); + } + return stopForQuitPromise; +} + +function startConfiguredServices(reason: string): Promise { + if (!startServicesPromise) { + startServicesPromise = loadAppConfig() + .then(async (config) => { + try { + config = (await syncClaudeAppGatewayConfig(config)).config; + } catch (error) { + console.error(`Failed to sync Claude App gateway config during ${reason}: ${formatError(error)}`); + } + const status = await gatewayService.start(config); + if (status.state === "error") { + console.error(`Failed to start gateway during ${reason}: ${status.lastError}`); + } + if (status.state === "running") { + const profileResult = await applyProfileConfig(config); + for (const client of profileResult.clients) { + if (!client.ok) { + console.error(`Failed to apply ${client.client} profile during ${reason}: ${client.message}`); + } + } + } + if (config.proxy.enabled && config.proxy.systemProxy) { + const proxyStatus = await proxyService.ensureSystemProxyActive(); + logProxySystemProxyIssue(reason, proxyStatus); + } + }) + .catch((error) => { + console.error(`Failed to start configured services during ${reason}: ${formatError(error)}`); + }) + .finally(() => { + trayController.refreshUsageTitle(); + startServicesPromise = undefined; + }); + } + return startServicesPromise; +} + +function queueEnsureConfiguredProxyModeActive(reason: string): void { + void (startServicesPromise ?? Promise.resolve()) + .then(() => ensureConfiguredProxyModeActive(reason)) + .catch((error) => { + console.error(`Failed to ensure proxy mode during ${reason}: ${formatError(error)}`); + }); +} + +function ensureConfiguredProxyModeActive(reason: string): Promise { + if (stoppingForQuit) { + return Promise.resolve(); + } + + if (!ensureProxyModePromise) { + ensureProxyModePromise = loadAppConfig() + .then(async (config) => { + if (!config.proxy.enabled || !config.proxy.systemProxy) { + return; + } + + const proxyStatus = proxyService.getStatus(); + if (proxyStatus.state !== "running") { + await startConfiguredServices(reason); + return; + } + + const ensuredStatus = await proxyService.ensureSystemProxyActive(); + logProxySystemProxyIssue(reason, ensuredStatus); + }) + .finally(() => { + ensureProxyModePromise = undefined; + }); + } + return ensureProxyModePromise; +} + +function logProxySystemProxyIssue(reason: string, status: ReturnType): void { + if (status.systemProxy.state === "active") { + return; + } + + const details = status.systemProxy.lastError ? `: ${status.systemProxy.lastError}` : ""; + console.error(`Proxy mode is enabled, but system proxy is ${status.systemProxy.state} during ${reason}${details}`); +} + +function formatError(error: unknown): string { + return error instanceof Error ? error.message : String(error); +} diff --git a/src/main/main.ts b/src/main/main.ts index 0e96bce..44f233b 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -1,214 +1,13 @@ import { app } from "electron"; -import { setupApplicationMenu } from "./app-menu"; -import { loadAppConfig } from "./config"; -import { restoreClaudeAppGatewayConfig, syncClaudeAppGatewayConfig } from "./claude-app-gateway-service"; -import { deepLinkService } from "./deep-link"; -import { gatewayService } from "../server/gateway/service"; -import "./ipc"; -import { applyProfileConfig } from "./profile-service"; -import { proxyService } from "../server/proxy/service"; -import trayController from "./tray-controller"; -import { appUpdateService } from "./update-service"; -import windowsManager from "./windows"; +import { setRuntimeAppPaths } from "./app-paths"; -const gotTheLock = app.requestSingleInstanceLock(); -const quitProxyRestoreTimeoutMs = 30_000; -let quitPrepared = false; -let stoppingForQuit = false; -let ensureProxyModePromise: Promise | undefined; -let startServicesPromise: Promise | undefined; -let stopForQuitPromise: Promise | undefined; +setRuntimeAppPaths({ + appData: app.getPath("appData"), + home: app.getPath("home"), + userData: app.getPath("userData") +}); -if (!gotTheLock) { +void import("./main-app.js").catch((error) => { + console.error(error instanceof Error ? error.message : String(error)); app.quit(); -} else { - startPrimaryInstance(); -} - -function startPrimaryInstance(): void { - deepLinkService.register(); - deepLinkService.handleArgv(process.argv); - - app.on("second-instance", (_event, argv) => { - windowsManager.showMainWindow(); - deepLinkService.handleArgv(argv); - queueEnsureConfiguredProxyModeActive("second-instance"); - }); - - app.whenReady().then(() => { - setupApplicationMenu(); - windowsManager.createMainWindow(); - trayController.start(); - appUpdateService.start(); - appUpdateService.setInstallPreparation(prepareForUpdateInstall); - void startConfiguredServices("startup"); - - app.on("activate", () => { - windowsManager.showMainWindow(); - queueEnsureConfiguredProxyModeActive("activate"); - }); - }); - - app.on("before-quit", (event) => { - if (quitPrepared || appUpdateService.isInstallingUpdate()) { - return; - } - event.preventDefault(); - prepareAndQuit(); - }); - - app.on("will-quit", (event) => { - if (quitPrepared || appUpdateService.isInstallingUpdate()) { - return; - } - event.preventDefault(); - prepareAndQuit(); - }); - - app.on("window-all-closed", () => { - if (process.platform !== "darwin") { - app.quit(); - } - }); - - process.once("SIGINT", () => handleTerminationSignal("SIGINT")); - process.once("SIGTERM", () => handleTerminationSignal("SIGTERM")); -} - -function prepareAndQuit(): void { - if (stoppingForQuit) { - return; - } - - stoppingForQuit = true; - void stopServicesForQuit().finally(() => { - quitPrepared = true; - app.quit(); - }); -} - -async function prepareForUpdateInstall(): Promise { - if (!stoppingForQuit) { - stoppingForQuit = true; - } - await stopServicesForQuit(); - quitPrepared = true; -} - -function handleTerminationSignal(signal: NodeJS.Signals): void { - if (stoppingForQuit) { - return; - } - - stoppingForQuit = true; - void stopServicesForQuit().finally(() => { - quitPrepared = true; - app.exit(signal === "SIGINT" ? 130 : 143); - }); -} - -function stopServicesForQuit(): Promise { - if (!stopForQuitPromise) { - stopForQuitPromise = gatewayService - .stop({ proxyRestoreTimeoutMs: quitProxyRestoreTimeoutMs }) - .then(() => undefined) - .catch((error) => { - console.error(`Failed to stop services before quit: ${formatError(error)}`); - }) - .finally(() => { - try { - restoreClaudeAppGatewayConfig(); - } catch (error) { - console.error(`Failed to restore Claude App gateway config before quit: ${formatError(error)}`); - } - trayController.destroy(); - }); - } - return stopForQuitPromise; -} - -function startConfiguredServices(reason: string): Promise { - if (!startServicesPromise) { - startServicesPromise = loadAppConfig() - .then(async (config) => { - try { - config = (await syncClaudeAppGatewayConfig(config)).config; - } catch (error) { - console.error(`Failed to sync Claude App gateway config during ${reason}: ${formatError(error)}`); - } - const status = await gatewayService.start(config); - if (status.state === "error") { - console.error(`Failed to start gateway during ${reason}: ${status.lastError}`); - } - if (status.state === "running") { - const profileResult = await applyProfileConfig(config); - for (const client of profileResult.clients) { - if (!client.ok) { - console.error(`Failed to apply ${client.client} profile during ${reason}: ${client.message}`); - } - } - } - if (config.proxy.enabled && config.proxy.systemProxy) { - const proxyStatus = await proxyService.ensureSystemProxyActive(); - logProxySystemProxyIssue(reason, proxyStatus); - } - }) - .catch((error) => { - console.error(`Failed to start configured services during ${reason}: ${formatError(error)}`); - }) - .finally(() => { - trayController.refreshUsageTitle(); - startServicesPromise = undefined; - }); - } - return startServicesPromise; -} - -function queueEnsureConfiguredProxyModeActive(reason: string): void { - void (startServicesPromise ?? Promise.resolve()) - .then(() => ensureConfiguredProxyModeActive(reason)) - .catch((error) => { - console.error(`Failed to ensure proxy mode during ${reason}: ${formatError(error)}`); - }); -} - -function ensureConfiguredProxyModeActive(reason: string): Promise { - if (stoppingForQuit) { - return Promise.resolve(); - } - - if (!ensureProxyModePromise) { - ensureProxyModePromise = loadAppConfig() - .then(async (config) => { - if (!config.proxy.enabled || !config.proxy.systemProxy) { - return; - } - - const proxyStatus = proxyService.getStatus(); - if (proxyStatus.state !== "running") { - await startConfiguredServices(reason); - return; - } - - const ensuredStatus = await proxyService.ensureSystemProxyActive(); - logProxySystemProxyIssue(reason, ensuredStatus); - }) - .finally(() => { - ensureProxyModePromise = undefined; - }); - } - return ensureProxyModePromise; -} - -function logProxySystemProxyIssue(reason: string, status: ReturnType): void { - if (status.systemProxy.state === "active") { - return; - } - - const details = status.systemProxy.lastError ? `: ${status.systemProxy.lastError}` : ""; - console.error(`Proxy mode is enabled, but system proxy is ${status.systemProxy.state} during ${reason}${details}`); -} - -function formatError(error: unknown): string { - return error instanceof Error ? error.message : String(error); -} +}); diff --git a/src/main/profile-launch-service.ts b/src/main/profile-launch-service.ts index e1965e8..ed18cdf 100644 --- a/src/main/profile-launch-service.ts +++ b/src/main/profile-launch-service.ts @@ -1,10 +1,10 @@ import { spawn, spawnSync, type ChildProcess } from "node:child_process"; -import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import { chmodSync, existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs"; import os from "node:os"; import path from "node:path"; import type { AppConfig, ProfileOpenCommandResult, ProfileOpenRequest, ProfileOpenResult, ProfileRuntimeEntry, ProfileRuntimeStatus, ProfileStopResult } from "../shared/app"; import { botGatewayProfileEnv } from "./bot-gateway-env"; -import { applyClaudeAppGatewayConfig } from "./claude-app-gateway-service"; +import { applyClaudeAppGatewayConfig, readClaudeAppGatewayApiKeyCandidates } from "./claude-app-gateway-service"; import { launchClaudeAppProfile, resolveClaudeAppProfileUserDataDir } from "./claude-app-launch"; import { launchCodexAppProfile, launchZcodeAppProfile, refreshCodexCompatibleAppProfileFiles } from "./codex-app-launch"; import { codexCliMiddlewareRuntimeScript } from "./codex-cli-middleware-runtime"; @@ -156,7 +156,7 @@ async function openClaudeAppProfile(config: AppConfig, profile: ReturnType, - appName: string + appName: string, + options: { reuseExisting?: boolean; startIfMissing?: boolean } = {} ): Promise { const profileGatewayConfig = profileGatewayConfigFor(config, profile); - await ensureGatewayConfigRunning(profileGatewayConfig, appName); - return profileGatewayConfig; + const result = await ensureGatewayConfigRunning(profileGatewayConfig, profile, appName, options, config); + return result.acceptedApiKey && result.acceptedApiKey !== result.config.APIKEY + ? profileGatewayConfigWithToken(result.config, profile, result.acceptedApiKey) + : result.config; } -async function ensureGatewayConfigRunning(config: AppConfig, appName: string): Promise { - const startedStatus = await gatewayService.start(config); - if (startedStatus.state !== "running") { - throw new Error(startedStatus.lastError || `CCR gateway did not start for ${appName}.`); +type EnsureGatewayConfigRunningResult = { + acceptedApiKey?: string; + config: AppConfig; +}; + +async function ensureGatewayConfigRunning( + config: AppConfig, + profile: ReturnType, + appName: string, + options: { reuseExisting?: boolean; startIfMissing?: boolean } = {}, + candidateConfig: AppConfig = config +): Promise { + const startIfMissing = options.startIfMissing !== false; + if (options.reuseExisting) { + const existingGateway = await probeExistingProfileGateway(config, profile, candidateConfig); + if (existingGateway.state === "usable") { + return { acceptedApiKey: existingGateway.apiKey, config }; + } + if (existingGateway.state === "unavailable") { + if (!startIfMissing) { + throw new Error(`CCR gateway is not running at ${profileGatewayEndpoint(config)}. Start CCR Desktop or run ccr start before opening ${appName}.`); + } + } else { + throw new Error(existingGatewayConflictMessage(existingGateway, appName)); + } } + + if (!startIfMissing) { + throw new Error(`CCR gateway is not running at ${profileGatewayEndpoint(config)}. Start CCR Desktop or run ccr start before opening ${appName}.`); + } + + const startedStatus = await gatewayService.start(config); + if (startedStatus.state === "running") { + return { config }; + } + + if (options.reuseExisting && isAddressInUseError(startedStatus.lastError)) { + const existingGateway = await probeExistingProfileGateway(config, profile, candidateConfig); + if (existingGateway.state === "usable") { + return { acceptedApiKey: existingGateway.apiKey, config }; + } + throw new Error(existingGatewayConflictMessage(existingGateway, appName)); + } + + throw new Error(startedStatus.lastError || `CCR gateway did not start for ${appName}.`); +} + +type ExistingProfileGatewayProbe = + | { endpoint: string; reason?: string; state: "unavailable" } + | { endpoint: string; status?: number; state: "not-ccr" } + | { endpoint: string; message?: string; status: number; state: "unauthorized" } + | { endpoint: string; status: number; state: "unusable" } + | { apiKey?: string; endpoint: string; state: "usable" }; + +type ExistingGatewayHttpProbe = { + payload?: unknown; + reason?: string; + status?: number; +}; + +async function probeExistingProfileGateway( + config: AppConfig, + profile: ReturnType, + candidateConfig: AppConfig = config +): Promise { + const endpoint = profileGatewayEndpoint(config); + const root = await fetchExistingGateway(endpoint, "/"); + if (root.status === undefined) { + return { endpoint, reason: root.reason, state: "unavailable" }; + } + + let ccrGateway = isCcrGatewayRoot(root.payload); + if (!ccrGateway) { + const health = await fetchExistingGateway(endpoint, "/health"); + ccrGateway = isCcrGatewayHealth(health.payload); + } + if (!ccrGateway) { + return { endpoint, status: root.status, state: "not-ccr" }; + } + + let lastUnauthorized: ExistingGatewayHttpProbe | undefined; + for (const apiKey of existingGatewayApiKeyCandidates(config, profile, candidateConfig)) { + const headers: Record = { + "user-agent": "Claude Code" + }; + if (apiKey) { + headers.authorization = `Bearer ${apiKey}`; + } + const models = await fetchExistingGateway(endpoint, "/v1/models", { headers }); + if (models.status === 200) { + return { apiKey, endpoint, state: "usable" }; + } + if (models.status === 401 || models.status === 403) { + lastUnauthorized = models; + continue; + } + return { endpoint, status: models.status ?? 0, state: "unusable" }; + } + + if (lastUnauthorized?.status === 401 || lastUnauthorized?.status === 403) { + return { + endpoint, + message: readGatewayErrorMessage(lastUnauthorized.payload), + status: lastUnauthorized.status, + state: "unauthorized" + }; + } + return { endpoint, status: 0, state: "unusable" }; +} + +async function fetchExistingGateway( + endpoint: string, + pathname: string, + init: RequestInit = {} +): Promise { + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), 1200); + try { + const response = await fetch(new URL(pathname, endpoint).toString(), { + ...init, + signal: controller.signal + }); + return { + payload: await readResponseJson(response), + status: response.status + }; + } catch (error) { + return { reason: formatError(error) }; + } finally { + clearTimeout(timeout); + } +} + +async function readResponseJson(response: Response): Promise { + const text = await response.text(); + if (!text.trim()) { + return undefined; + } + try { + return JSON.parse(text) as unknown; + } catch { + return undefined; + } +} + +function isCcrGatewayRoot(value: unknown): boolean { + if (!isRecord(value)) { + return false; + } + return value.name === "claude-code-router" || value.plugin === "claude-code-router"; +} + +function isCcrGatewayHealth(value: unknown): boolean { + if (!isRecord(value)) { + return false; + } + return typeof value.core === "string" && typeof value.status === "string" && typeof value.timestamp === "string"; +} + +function readGatewayErrorMessage(value: unknown): string | undefined { + if (!isRecord(value) || !isRecord(value.error)) { + return undefined; + } + return typeof value.error.message === "string" ? value.error.message : undefined; +} + +function existingGatewayApiKeyCandidates( + config: AppConfig, + profile: ReturnType, + candidateConfig: AppConfig +): Array { + const values = [ + config.APIKEY, + ...(Array.isArray(config.APIKEYS) ? config.APIKEYS.map((apiKey) => apiKey.key) : []), + candidateConfig.APIKEY, + ...(Array.isArray(candidateConfig.APIKEYS) ? candidateConfig.APIKEYS.map((apiKey) => apiKey.key) : []), + ...readClaudeAppGatewayApiKeyCandidates(), + ...readClaudeCodeApiKeyHelperCandidates(profile) + ]; + const seen = new Set(); + const result: string[] = []; + for (const value of values) { + const key = value?.trim(); + if (!key || seen.has(key)) { + continue; + } + seen.add(key); + result.push(key); + } + return result.length > 0 ? result : [undefined]; +} + +function readClaudeCodeApiKeyHelperCandidates(profile: ReturnType): string[] { + const file = path.join(CONFIGDIR, "bin", claudeCodeApiKeyHelperFilename(profile)); + const files = [ + file, + ...readBackupFiles(file) + ]; + return uniqueStrings(files.map(readClaudeCodeApiKeyHelperToken)); +} + +function claudeCodeApiKeyHelperFilename(profile: ReturnType): string { + const slug = sanitizeProfilePathSegment(profile.id || profile.name || profile.agent) || "claude-code"; + return process.platform === "win32" + ? `ccr-claude-code-api-key-${slug}.cmd` + : `ccr-claude-code-api-key-${slug}`; +} + +function readBackupFiles(file: string): string[] { + const dir = path.dirname(file); + const prefix = `${path.basename(file)}.ccr-backup-`; + try { + return readdirSync(dir) + .filter((entry) => entry.startsWith(prefix)) + .sort() + .reverse() + .map((entry) => path.join(dir, entry)); + } catch { + return []; + } +} + +function readClaudeCodeApiKeyHelperToken(file: string): string { + if (!existsSync(file)) { + return ""; + } + try { + const content = readFileSync(file, "utf8"); + for (const line of content.split(/\r?\n/g)) { + const token = parseClaudeCodeApiKeyHelperLine(line); + if (token) { + return token; + } + } + } catch { + return ""; + } + return ""; +} + +function parseClaudeCodeApiKeyHelperLine(line: string): string { + const trimmed = line.trim(); + const shellPrefix = "printf '%s\\n' "; + if (trimmed.startsWith(shellPrefix)) { + return unquoteShellValue(trimmed.slice(shellPrefix.length).trim()); + } + if (/^echo\s+/i.test(trimmed)) { + return trimmed.replace(/^echo\s+/i, "").trim().replace(/^"|"$/g, ""); + } + return ""; +} + +function unquoteShellValue(value: string): string { + if (value.startsWith("'") && value.endsWith("'")) { + return value.slice(1, -1).replace(/'\\''/g, "'"); + } + return value.replace(/^"|"$/g, ""); +} + +function existingGatewayConflictMessage(probe: ExistingProfileGatewayProbe, appName: string): string { + if (probe.state === "unauthorized") { + const details = probe.message ? ` ${probe.message}` : ""; + return `CCR gateway is already running at ${probe.endpoint}, but it does not accept the API key for ${appName}.${details} Restart CCR Desktop or run ccr start to refresh the gateway before opening this profile.`; + } + if (probe.state === "unusable") { + return `CCR gateway is already running at ${probe.endpoint}, but it cannot serve ${appName} right now (HTTP ${probe.status}). Restart CCR Desktop or run ccr start to refresh the gateway before opening this profile.`; + } + if (probe.state === "not-ccr") { + return `Port ${probe.endpoint} is already in use by a non-CCR service. Stop that process or change the CCR gateway port.`; + } + if (probe.state === "unavailable") { + return `CCR gateway is not reachable at ${probe.endpoint}${probe.reason ? `: ${probe.reason}` : ""}.`; + } + return `CCR gateway is already running at ${probe.endpoint}.`; +} + +function isAddressInUseError(message: string | undefined): boolean { + return /\bEADDRINUSE\b/i.test(message || ""); +} + +function profileGatewayEndpoint(config: AppConfig): string { + const host = probeGatewayHost(config.gateway.host); + return `http://${formatEndpointHost(host)}:${config.gateway.port}/`; +} + +function probeGatewayHost(host: string): string { + if (!host || host === "0.0.0.0") { + return "127.0.0.1"; + } + if (host === "::") { + return "::1"; + } + return host; +} + +function formatEndpointHost(host: string): string { + return host.includes(":") && !host.startsWith("[") ? `[${host}]` : host; } function profileGatewayConfigFor(config: AppConfig, profile: ReturnType): AppConfig { @@ -201,6 +496,10 @@ function profileGatewayConfigFor(config: AppConfig, profile: ReturnType, token: string): AppConfig { return { ...config, APIKEY: token, @@ -779,7 +1078,7 @@ function commandProfileRef(config: AppConfig, profile: ReturnType/dev/null 2>&1; then", - ` exec node ${shQuote(runtimeFile)} "$@"`, - "fi", `ELECTRON_RUN_AS_NODE=1 exec ${shQuote(process.execPath)} ${shQuote(runtimeFile)} "$@"` ].join("\n") + "\n"; } function windowsCcrLauncher(runtimeFile: string): string { + const nodePath = bundledNodePath(); return [ "@echo off", "setlocal", `set "CCR_CLI_RUNTIME=${cmdEnvValue(runtimeFile)}"`, + `set "CCR_CLI_NODE_PATH=${cmdEnvValue(nodePath)}"`, + "if defined NODE_PATH (", + " set \"NODE_PATH=%CCR_CLI_NODE_PATH%;%NODE_PATH%\"", + ") else (", + " set \"NODE_PATH=%CCR_CLI_NODE_PATH%\"", + ")", "if defined CCR_NODE_BIN (", ' "%CCR_NODE_BIN%" "%CCR_CLI_RUNTIME%" %*', " exit /b %ERRORLEVEL%", ")", - "where node >nul 2>nul", - "if %ERRORLEVEL%==0 (", - ' node "%CCR_CLI_RUNTIME%" %*', - " exit /b %ERRORLEVEL%", - ")", "set \"ELECTRON_RUN_AS_NODE=1\"", `${cmdQuote(process.execPath)} "%CCR_CLI_RUNTIME%" %*`, "exit /b %ERRORLEVEL%" ].join("\r\n") + "\r\n"; } +function bundledNodePath(): string { + const resourcesPath = (process as NodeJS.Process & { resourcesPath?: string }).resourcesPath; + const candidates = [ + path.join(__dirname, "..", "..", "node_modules"), + ...(resourcesPath + ? [ + path.join(resourcesPath, "app.asar", "node_modules"), + path.join(resourcesPath, "app.asar.unpacked", "node_modules"), + path.join(resourcesPath, "app", "node_modules") + ] + : []), + path.join(process.cwd(), "node_modules") + ]; + return uniqueStrings(candidates).join(path.delimiter); +} + +function uniqueStrings(values: string[]): string[] { + const seen = new Set(); + const result: string[] = []; + for (const value of values) { + if (!value || seen.has(value)) { + continue; + } + seen.add(value); + result.push(value); + } + return result; +} + function writeFileIfChanged(file: string, content: string): void { if (existsSync(file) && readFileSync(file, "utf8") === content) { return; diff --git a/src/main/profile-service.ts b/src/main/profile-service.ts index f0df909..3a4521b 100644 --- a/src/main/profile-service.ts +++ b/src/main/profile-service.ts @@ -44,6 +44,15 @@ export async function applyProfileConfig(config: AppConfig): Promise candidate.internalName), credentialIds: selection.credentials.map((candidate) => candidate.credentialId), credentialProtocol: target.protocol, @@ -1938,6 +1942,22 @@ function prepareUpstreamCredentialAttempt(input: { }; } +function normalizeConfiguredProviderModelBody( + body: Buffer | undefined, + config: AppConfig +): { body: Buffer; model: string } | undefined { + const parsedBody = parseJsonObjectSafe(body); + const model = stringValue(parsedBody?.model); + const selector = resolveConfiguredProviderModelSelector(model, config); + if (!parsedBody || !selector || selector.model === model) { + return undefined; + } + return { + body: serializeJsonBodyWithModel(parsedBody, selector.model), + model: selector.model + }; +} + function resolveProviderCredentialRoutingTarget( config: AppConfig, headers: Record, @@ -1951,14 +1971,14 @@ function resolveProviderCredentialRoutingTarget( const parsedBody = parseJsonObjectSafe(body); const bodyModel = stringValue(parsedBody?.model); - const parsedModel = parseProviderModelSelector(bodyModel); - if (parsedModel) { - const provider = findProviderByPublicOrInternalName(config, parsedModel.provider); + const modelSelector = resolveConfiguredProviderModelSelector(bodyModel, config); + if (modelSelector) { + const provider = modelSelector.provider; const providerProtocol = provider ? providerProtocolForClientProtocol(provider, protocol) : undefined; if (provider && providerProtocol && activeProviderCredentials(provider).length > 0) { return { - body: parsedBody ? serializeJsonBodyWithModel(parsedBody, parsedModel.model) : body, - model: parsedModel.model, + body: parsedBody ? serializeJsonBodyWithModel(parsedBody, modelSelector.model) : body, + model: modelSelector.model, provider, protocol: providerProtocol }; @@ -2001,6 +2021,44 @@ function parseProviderModelSelector(value: string | undefined): { model: string; return provider && model ? { model, provider } : undefined; } +function resolveConfiguredProviderModelSelector( + value: string | undefined, + config: AppConfig +): { model: string; provider: GatewayProviderConfig } | undefined { + let current = normalizeRouteSelector(value); + if (!current) { + return undefined; + } + + let selectedProvider: GatewayProviderConfig | undefined; + for (let depth = 0; depth < 4; depth += 1) { + const parsed = parseProviderModelSelector(current); + if (!parsed) { + break; + } + + const provider = findProviderByPublicOrInternalName(config, parsed.provider); + if (!provider) { + break; + } + + selectedProvider = provider; + current = parsed.model; + + const nested = parseProviderModelSelector(current); + if (!nested) { + return current ? { model: current, provider } : undefined; + } + + const nestedProvider = findProviderByPublicOrInternalName(config, nested.provider); + if (!nestedProvider || providerRuntimeId(nestedProvider) !== providerRuntimeId(provider)) { + return current ? { model: current, provider } : undefined; + } + } + + return selectedProvider && current ? { model: current, provider: selectedProvider } : undefined; +} + function firstTargetProviderHeader(headers: Record): string | undefined { const provider = headers["x-target-provider"] || headers["x-gateway-target-provider"]; if (provider?.trim()) { @@ -2707,6 +2765,15 @@ function normalizeBindHost(host: string): string { return host.trim().replace(/^\[|\]$/g, "").toLowerCase(); } +function isLoopbackBindHost(host: string): boolean { + const normalized = normalizeBindHost(host).replace(/\.$/, ""); + return normalized === "localhost" || + normalized === "127.0.0.1" || + normalized === "::1" || + normalized === "0:0:0:0:0:0:0:1" || + /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(normalized); +} + function isWildcardBindHost(host: string): boolean { return host === "" || host === "0.0.0.0" || host === "::" || host === "::0"; } @@ -2945,14 +3012,26 @@ function applyCors(response: ServerResponse, config?: AppConfig): void { response.setHeader("Access-Control-Expose-Headers", "Mcp-Session-Id"); } -function authorize(request: IncomingMessage, response: ServerResponse, config: AppConfig): ApiKeyAuthorizationResult { - const apiKeys = configuredApiKeys(config); +async function authorize(request: IncomingMessage, response: ServerResponse, config: AppConfig): Promise { + let apiKeys = await configuredApiKeys(config); if (apiKeys.length === 0) { - return { ok: true }; + if (isLoopbackBindHost(config.gateway.host)) { + return { ok: true }; + } + sendJson(response, 403, { + error: { + message: "Configure at least one CCR API key before listening on a non-loopback gateway host." + } + }); + return { ok: false }; } const token = readAuthToken(request.headers) || readRemoteControlQueryAuthToken(request); - const apiKey = token ? apiKeys.find((item) => item.key === token) : undefined; + let apiKey = token ? apiKeys.find((item) => item.key === token) : undefined; + if (!apiKey && token) { + apiKeys = await configuredApiKeys(config, { refresh: true }); + apiKey = apiKeys.find((item) => item.key === token); + } if (apiKey) { if (isApiKeyExpired(apiKey)) { sendJson(response, 401, { error: { message: "API key is expired." } }); @@ -2965,8 +3044,10 @@ function authorize(request: IncomingMessage, response: ServerResponse, config: A return { ok: false }; } -function configuredApiKeys(config: AppConfig): ApiKeyConfig[] { +async function configuredApiKeys(config: AppConfig, options: { refresh?: boolean } = {}): Promise { + const persistedApiKeys = await loadPersistedApiKeysCached(options); const values = [ + ...persistedApiKeys, ...(Array.isArray(config.APIKEYS) ? config.APIKEYS : []), ...(config.APIKEY ? [{ createdAt: new Date(0).toISOString(), id: "legacy", key: config.APIKEY }] : []) ]; @@ -2983,6 +3064,24 @@ function configuredApiKeys(config: AppConfig): ApiKeyConfig[] { return result; } +async function loadPersistedApiKeysCached(options: { refresh?: boolean } = {}): Promise { + const now = Date.now(); + if (!options.refresh && persistedApiKeyCache && now - persistedApiKeyCache.loadedAt < persistedApiKeyCacheTtlMs) { + return persistedApiKeyCache.values; + } + try { + const values = await loadPersistedApiKeys(); + persistedApiKeyCache = { + loadedAt: now, + values + }; + return values; + } catch (error) { + console.warn(`[gateway] Failed to load persisted API keys: ${formatError(error)}`); + return []; + } +} + function isApiKeyExpired(apiKey: ApiKeyConfig): boolean { if (!apiKey.expiresAt) { return false; @@ -3009,7 +3108,7 @@ function reserveApiKeyLimits(apiKey: ApiKeyConfig | undefined, request: Incoming }); for (const check of checks) { - const counter = readApiKeyWindowCounter(check.counterKey, check.windowStart); + const counter = readApiKeyWindowCounter(check.counterKey, check.windowStart, check.rule.windowMs, now); if (counter.value + check.rule.requested > check.rule.limit) { sendJson(response, 429, { error: { @@ -3030,7 +3129,7 @@ function reserveApiKeyLimits(apiKey: ApiKeyConfig | undefined, request: Incoming } for (const check of checks) { - readApiKeyWindowCounter(check.counterKey, check.windowStart).value += check.rule.requested; + readApiKeyWindowCounter(check.counterKey, check.windowStart, check.rule.windowMs, now).value += check.rule.requested; } return true; } @@ -3076,7 +3175,7 @@ function providerCredentialLimitState( let utilization = 0; for (const rule of rules) { const windowStart = Math.floor(now / rule.windowMs) * rule.windowMs; - const counter = readApiKeyWindowCounter(providerCredentialCounterKey(provider, credential, rule, windowStart), windowStart); + const counter = readApiKeyWindowCounter(providerCredentialCounterKey(provider, credential, rule, windowStart), windowStart, rule.windowMs, now); blocked = blocked || counter.value + rule.requested > rule.limit; utilization = Math.max(utilization, (counter.value + rule.requested) / rule.limit); } @@ -3141,7 +3240,7 @@ function incrementProviderCredentialCounters( const now = Date.now(); for (const rule of rules) { const windowStart = Math.floor(now / rule.windowMs) * rule.windowMs; - readApiKeyWindowCounter(providerCredentialCounterKey(provider, credential, rule, windowStart), windowStart).value += rule.requested; + readApiKeyWindowCounter(providerCredentialCounterKey(provider, credential, rule, windowStart), windowStart, rule.windowMs, now).value += rule.requested; } } @@ -3202,16 +3301,29 @@ function addApiKeyLimitRule( }); } -function readApiKeyWindowCounter(key: string, windowStart: number): ApiKeyWindowCounter { +function readApiKeyWindowCounter(key: string, windowStart: number, windowMs: number, now = Date.now()): ApiKeyWindowCounter { + pruneExpiredApiKeyLimitCounters(now); const existing = apiKeyLimitCounters.get(key); if (existing && existing.windowStart === windowStart) { return existing; } - const fresh = { value: 0, windowStart }; + const fresh = { + expiresAt: windowStart + windowMs * apiKeyLimitCounterRetentionWindows, + value: 0, + windowStart + }; apiKeyLimitCounters.set(key, fresh); return fresh; } +function pruneExpiredApiKeyLimitCounters(now: number): void { + for (const [key, counter] of apiKeyLimitCounters) { + if (counter.expiresAt <= now) { + apiKeyLimitCounters.delete(key); + } + } +} + function estimateApiKeyLimitUsage(request: IncomingMessage, requestBody: Buffer): ApiKeyLimitUsage { return estimateLimitUsage(request.method ?? "GET", requestBody); } diff --git a/src/server/mcp/network-capture-mcp.ts b/src/server/mcp/network-capture-mcp.ts index 21759ce..2c6e0b7 100644 --- a/src/server/mcp/network-capture-mcp.ts +++ b/src/server/mcp/network-capture-mcp.ts @@ -1,4 +1,3 @@ -import * as electron from "electron"; import packageJson from "../../../package.json"; import type { IncomingMessage, ServerResponse } from "node:http"; import type { ProxyNetworkExchange } from "../../shared/app"; @@ -170,7 +169,7 @@ async function handleJsonRpcRequest(payload: unknown): Promise { diff --git a/src/server/proxy/service.ts b/src/server/proxy/service.ts index eb8f031..4db90ff 100644 --- a/src/server/proxy/service.ts +++ b/src/server/proxy/service.ts @@ -1,6 +1,5 @@ import { execFile } from "node:child_process"; import { randomUUID } from "node:crypto"; -import * as electron from "electron"; import { chmodSync, writeFileSync } from "node:fs"; import http, { type ClientRequest, type IncomingHttpHeaders, type IncomingMessage, type Server, type ServerResponse } from "node:http"; import https from "node:https"; @@ -9,6 +8,7 @@ import os from "node:os"; import path from "node:path"; import { PassThrough } from "node:stream"; import tls from "node:tls"; +import { pathToFileURL } from "node:url"; import { brotliDecompressSync, gunzipSync, inflateRawSync, inflateSync } from "node:zlib"; import type { AppConfig, @@ -85,6 +85,11 @@ type ActiveProxyNetworkCapture = { setResponse: (statusCode: number, headers: CapturedHeaders | IncomingHttpHeaders) => void; }; +export type ProxyDesktopIntegration = { + requestMacosCertificateInstallPermission?: () => Promise; + revealFile?: (file: string) => Promise; +}; + const requestHopByHopHeaders = new Set([ "connection", "keep-alive", @@ -119,6 +124,7 @@ class ProxyService { private authority?: CertificateAuthority; private attachedServer?: AttachedServer; private config?: AppConfig; + private desktopIntegration: ProxyDesktopIntegration = {}; private networkCaptureEnabled = false; private networkCaptures: ProxyNetworkCaptureRecord[] = []; private secureServers = new Map>(); @@ -134,6 +140,10 @@ class ProxyService { }; private upstreamProxy?: UpstreamProxyConfig; + setDesktopIntegration(integration: ProxyDesktopIntegration): void { + this.desktopIntegration = integration; + } + async start(config: AppConfig): Promise { await this.stop(); this.config = config; @@ -375,7 +385,7 @@ class ProxyService { async installCertificate(): Promise { ensureProxyCertificateAuthority(); if (process.platform === "darwin") { - const approved = await requestMacosCertificateInstallPermission(); + const approved = await this.requestMacosCertificateInstallPermission(); if (!approved) { const status = await this.getCertificateStatus(); return { @@ -398,7 +408,7 @@ class ProxyService { const installerFile = await openMacosTerminalCertificateInstaller(); terminalMessage = ` Opened Terminal installer: ${installerFile}`; } catch (terminalError) { - electron.shell?.showItemInFolder?.(PROXY_CA_CERT_FILE); + await this.revealFile(PROXY_CA_CERT_FILE).catch(() => undefined); terminalMessage = ` Could not open Terminal installer: ${formatError(terminalError)}`; } const status = await this.getCertificateStatus(); @@ -455,6 +465,18 @@ class ProxyService { }; } + private async requestMacosCertificateInstallPermission(): Promise { + return await (this.desktopIntegration.requestMacosCertificateInstallPermission?.() ?? Promise.resolve(true)); + } + + private async revealFile(file: string): Promise { + if (this.desktopIntegration.revealFile) { + await this.desktopIntegration.revealFile(file); + return; + } + await revealFileWithSystemCommand(file); + } + async getCertificateStatus(): Promise { const base = { caCertFile: proxyCaCertFile(), @@ -1469,25 +1491,6 @@ function execFilePromise(file: string, args: string[]): Promise { }); } -async function requestMacosCertificateInstallPermission(): Promise { - if (!electron.dialog || !electron.BrowserWindow) { - return true; - } - const window = electron.BrowserWindow.getFocusedWindow() ?? electron.BrowserWindow.getAllWindows()[0]; - const options = { - buttons: ["Continue", "Cancel"], - cancelId: 1, - defaultId: 0, - detail: - "macOS will ask for administrator credentials. This is required so Chrome can trust HTTPS certificates generated by CCR proxy mode.", - message: "Install CCR Proxy CA into the macOS System keychain?", - noLink: true, - type: "warning" as const - }; - const result = window ? await electron.dialog.showMessageBox(window, options) : await electron.dialog.showMessageBox(options); - return result.response === 0; -} - function execFileText(file: string, args: string[]): Promise { return new Promise((resolve, reject) => { execFile(file, args, { maxBuffer: 8 * 1024 * 1024, windowsHide: process.platform === "win32" }, (error, stdout, stderr) => { @@ -1555,17 +1558,22 @@ async function openMacosTerminalCertificateInstaller(): Promise { const installerFile = path.join(os.tmpdir(), `ccr-install-proxy-ca-${randomUUID()}.command`); writeFileSync(installerFile, `${macosTerminalCertificateInstallScript()}\n`, "utf8"); chmodSync(installerFile, 0o700); - if (electron.shell?.openPath) { - const errorMessage = await electron.shell.openPath(installerFile); - if (errorMessage) { - throw new Error(errorMessage); - } - return installerFile; - } await execFilePromise("/usr/bin/open", [installerFile]); return installerFile; } +async function revealFileWithSystemCommand(file: string): Promise { + if (process.platform === "darwin") { + await execFilePromise("/usr/bin/open", ["-R", file]); + return; + } + if (process.platform === "win32") { + await execFilePromise("explorer.exe", ["/select,", file]); + return; + } + await execFilePromise("xdg-open", [pathToFileURL(path.dirname(file)).toString()]); +} + function macosTerminalCertificateInstallScript(): string { return [ "#!/bin/zsh", From c583f696664e486df47ead72a2376929297995cb Mon Sep 17 00:00:00 2001 From: musistudio Date: Mon, 29 Jun 2026 20:24:27 +0800 Subject: [PATCH 07/94] Refactor router configuration and provider handling --- README.md | 2 +- README_zh.md | 2 +- build/esbuild.config.mjs | 62 +++- .../en/configuration/provider-deeplink.md | 30 +- .../zh/configuration/provider-deeplink.md | 30 +- docs/src/styles/global.css | 6 + package-lock.json | 8 +- package.json | 2 +- src/main/claude-app-gateway-service.ts | 28 +- src/main/cli.ts | 3 +- src/main/presets/index.ts | 2 + src/main/presets/kimi-coding/index.ts | 39 +++ src/main/presets/moonshot/index.ts | 6 +- src/main/profile-launch-service.ts | 4 +- src/main/profile-service.ts | 319 +++++++++++++++++- src/main/provider-account-service.ts | 251 +++++++++++++- src/main/provider-model-catalog.ts | 48 +++ src/main/provider-probe.ts | 60 +++- src/main/zcode-profile-config.ts | 18 + src/renderer/pages/home/App.tsx | 259 ++++++-------- .../pages/home/components/dashboard.tsx | 6 +- src/renderer/pages/home/components/layout.tsx | 46 +-- .../pages/home/components/providers.tsx | 220 +++++++++--- src/renderer/pages/home/components/update.tsx | 6 +- src/renderer/pages/home/shared/external.tsx | 10 +- src/renderer/pages/home/shared/i18n.tsx | 5 + src/renderer/pages/home/shared/options.ts | 1 + src/renderer/pages/home/shared/profiles.ts | 2 +- src/renderer/pages/home/shared/providers.ts | 98 +++++- src/renderer/pages/home/shared/types.ts | 1 + src/server/gateway/service.ts | 130 ++++++- src/server/mcp/fusion-tool-fallback-mcp.ts | 235 +++++++++++++ src/server/mcp/fusion-vision-mcp.ts | 5 +- src/shared/app.ts | 85 +++++ src/shared/i18n.ts | 1 + src/shared/provider-preset-utils.ts | 7 + src/shared/provider-presets.ts | 2 + 37 files changed, 1738 insertions(+), 301 deletions(-) create mode 100644 src/main/presets/kimi-coding/index.ts create mode 100644 src/server/mcp/fusion-tool-fallback-mcp.ts diff --git a/README.md b/README.md index 1e4bde0..941b7ad 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ CCR runs on your machine, keeps provider configuration in your local config dire - Use one local endpoint for multiple agent tools instead of configuring every client separately. - Route different workloads to different models, such as fast background work, reasoning tasks, long-context requests, image tasks, or web-search-capable models. -- Mix providers without changing your workflow. CCR supports OpenAI-compatible APIs, Anthropic Messages, Gemini Generate Content, OpenRouter, DeepSeek, SiliconFlow, Moonshot, Mistral, Z.AI, Bailian, and custom providers. +- Mix providers without changing your workflow. CCR supports OpenAI-compatible APIs, Anthropic Messages, Gemini Generate Content, OpenRouter, DeepSeek, SiliconFlow, Moonshot, Kimi Code, Mistral, Z.AI, Bailian, and custom providers. - Control cost and reliability with fallback routing, API key rotation, usage statistics, and request logs. - Manage everything from a desktop UI instead of editing JSON by hand. - Extend the gateway with plugins, proxy routes, local HTTP backends, and provider deeplinks. diff --git a/README_zh.md b/README_zh.md index 7c94376..b450f38 100644 --- a/README_zh.md +++ b/README_zh.md @@ -19,7 +19,7 @@ CCR 在你的本机运行,Provider 配置保存在本地配置目录,并默 - 用一个本地入口连接多个 Agent 工具,不需要在每个客户端里重复配置 Provider。 - 不同任务使用不同模型,例如后台任务、推理任务、长上下文、图片任务或支持联网搜索的模型。 -- 在不改变工作流的情况下混用不同 Provider。CCR 支持 OpenAI 兼容 API、Anthropic Messages、Gemini Generate Content、OpenRouter、DeepSeek、SiliconFlow、Moonshot、Mistral、Z.AI、百炼以及自定义 Provider。 +- 在不改变工作流的情况下混用不同 Provider。CCR 支持 OpenAI 兼容 API、Anthropic Messages、Gemini Generate Content、OpenRouter、DeepSeek、SiliconFlow、Moonshot、Kimi Code、Mistral、Z.AI、百炼以及自定义 Provider。 - 通过 fallback 路由、API Key 轮换、用量统计和请求日志来控制成本和可靠性。 - 使用桌面 UI 管理配置,减少手写 JSON。 - 通过插件、代理路由、本地 HTTP 后端和 Provider deeplink 扩展网关能力。 diff --git a/build/esbuild.config.mjs b/build/esbuild.config.mjs index 9fc975f..9bdaba7 100644 --- a/build/esbuild.config.mjs +++ b/build/esbuild.config.mjs @@ -27,6 +27,15 @@ export const trayRendererHtmlOutput = path.join(rendererOutDir, "pages", "tray", export const cssInput = path.join(rendererRoot, "styles", "globals.css"); export const cssOutput = path.join(rendererAssetsDir, "main.css"); export const webClientBridgeOutput = path.join(rendererAssetsDir, "web-client-bridge.js"); +const lightweightMcpBundleNames = ["fusion-vision-mcp.js", "fusion-tool-fallback-mcp.js"]; +const lightweightMcpBundleMaxBytes = 128 * 1024; +const forbiddenLightweightMcpInputs = [ + { prefix: "src/main/", reason: "main-process modules can pull in config, Electron, or native storage side effects" }, + { prefix: "src/renderer/", reason: "renderer modules do not belong in stdio MCP subprocesses" }, + { prefix: "node_modules/better-sqlite3/", reason: "native SQLite is not allowed in lightweight MCP subprocesses" }, + { prefix: "node_modules/electron/", reason: "Electron runtime modules are not allowed in lightweight MCP subprocesses" } +]; +const forbiddenLightweightMcpExternalImports = new Set(["better-sqlite3", "electron"]); const nodeExternals = [ "electron", @@ -112,12 +121,14 @@ export function createMainBuildOptions({ mode = "production", plugins = [] } = { path.join(projectRoot, "src", "main", "main.ts"), path.join(projectRoot, "src", "main", "browser-preload.ts"), path.join(projectRoot, "src", "server", "mcp", "fusion-vision-mcp.ts"), + path.join(projectRoot, "src", "server", "mcp", "fusion-tool-fallback-mcp.ts"), path.join(projectRoot, "src", "main", "preload.ts") ], external: nodeExternals, format: "cjs", legalComments: "none", logLevel: "info", + metafile: true, minify: mode === "production", outdir: mainOutDir, platform: "node", @@ -225,10 +236,11 @@ export function watchPlugin(name, onEnd) { } export async function buildMain(options = {}) { - await Promise.all([ + const [mainBuildResult] = await Promise.all([ esbuild.build(createMainBuildOptions(options)), esbuild.build(createCliBuildOptions(options)) ]); + validateLightweightMcpBundles(mainBuildResult.metafile); } export async function buildRenderer(options = {}) { @@ -309,6 +321,54 @@ function forbidCliElectronPlugin() { }; } +function validateLightweightMcpBundles(metafile) { + if (!metafile) { + return; + } + + const outputsByName = new Map( + Object.entries(metafile.outputs).map(([outputPath, output]) => [path.basename(outputPath), { output, outputPath }]) + ); + + for (const bundleName of lightweightMcpBundleNames) { + const entry = outputsByName.get(bundleName); + if (!entry) { + continue; + } + + const violations = []; + if (entry.output.bytes > lightweightMcpBundleMaxBytes) { + violations.push(`bundle size ${entry.output.bytes} bytes exceeds ${lightweightMcpBundleMaxBytes} bytes`); + } + + for (const inputPath of Object.keys(entry.output.inputs ?? {})) { + const normalizedInput = normalizeBuildPath(inputPath); + for (const rule of forbiddenLightweightMcpInputs) { + if (normalizedInput.startsWith(rule.prefix)) { + violations.push(`${normalizedInput} (${rule.reason})`); + } + } + } + + for (const imported of entry.output.imports ?? []) { + if (imported.external && forbiddenLightweightMcpExternalImports.has(imported.path)) { + violations.push(`${imported.path} (external native/runtime dependency is not allowed)`); + } + } + + if (violations.length > 0) { + throw new Error([ + `Lightweight MCP bundle ${bundleName} crossed its dependency boundary.`, + ...violations.map((violation) => `- ${violation}`) + ].join("\n")); + } + } +} + +function normalizeBuildPath(value) { + return value.split(path.sep).join("/"); +} + function resolveRendererImport(importPath) { const basePath = path.resolve(rendererRoot, importPath); const candidates = [ diff --git a/docs/src/content/docs/en/configuration/provider-deeplink.md b/docs/src/content/docs/en/configuration/provider-deeplink.md index 449133c..f4a3818 100644 --- a/docs/src/content/docs/en/configuration/provider-deeplink.md +++ b/docs/src/content/docs/en/configuration/provider-deeplink.md @@ -10,55 +10,59 @@ lead: Use ccr://provider links to hand provider configuration to CCR, or open th The buttons below open the CCR desktop app's provider import confirmation dialog. Preset buttons do not include API keys; custom provider links may include one. Always confirm the provider name, Base URL, protocol, and models before importing a key.

- + OpenAIResponses / Chat Completions - + AnthropicAnthropic Messages - + Google GeminiGemini Generate Content - + OpenRouterOpenAI compatible gateway - + DeepSeekChat Completions - + Zhipu CodingChina Coding Plan - + Zhipu GeneralChina General Endpoint - + Z.ai CodingGlobal Coding Plan - + Z.ai GeneralGlobal General Endpoint - + MistralChat Completions - + Moonshot KimiChat Completions - + + + Kimi CodeCoding Plan + + Alibaba BailianDashScope compatible - + SiliconFlowChat Completions diff --git a/docs/src/content/docs/zh/configuration/provider-deeplink.md b/docs/src/content/docs/zh/configuration/provider-deeplink.md index b0e2f37..d51e045 100644 --- a/docs/src/content/docs/zh/configuration/provider-deeplink.md +++ b/docs/src/content/docs/zh/configuration/provider-deeplink.md @@ -10,55 +10,59 @@ lead: 使用 ccr://provider 链接把供应商配置带入 CCR,并通过现有 下面的按钮会打开 CCR 桌面 App 的供应商导入确认页。预设按钮不会携带 API Key;自定义供应商链接可以携带 Key。导入前始终确认供应商名称、Base URL、协议和模型。
- + OpenAI 官方Responses / Chat Completions - + Anthropic 官方Anthropic Messages - + 谷歌 GeminiGemini Generate Content - + OpenRouter 路由OpenAI compatible gateway - + DeepSeek 深度求索Chat Completions - + 智谱 Coding中国 Coding 计划 - + 智谱通用中国通用端点 - + 智谱国际 Coding全球 Coding 计划 - + 智谱国际通用全球通用端点 - + Mistral 官方Chat Completions - + 月之暗面 KimiChat Completions - + + + Kimi CodeCoding Plan + + 阿里百炼DashScope 兼容 - + 硅基流动Chat Completions diff --git a/docs/src/styles/global.css b/docs/src/styles/global.css index ab21f4e..ff83d59 100644 --- a/docs/src/styles/global.css +++ b/docs/src/styles/global.css @@ -1322,6 +1322,12 @@ h1 { --provider-brand-3: #e4ddff; } +.doc-markdown a.provider-import-button.provider-kimi-coding { + --provider-brand: #111237; + --provider-brand-2: #5c6bff; + --provider-brand-3: #dfe4ff; +} + .doc-markdown a.provider-import-button.provider-bailian { --provider-brand: #5a2300; --provider-brand-2: #ff6a00; diff --git a/package-lock.json b/package-lock.json index f3783f2..3208688 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "3.0.2", "license": "MIT", "dependencies": { - "@the-next-ai/ai-gateway": "^1.0.1", + "@the-next-ai/ai-gateway": "^1.0.2", "@the-next-ai/bot-gateway-sdk": "^0.1.0", "better-sqlite3": "^12.11.1", "electron-updater": "^6.8.9", @@ -2066,9 +2066,9 @@ } }, "node_modules/@the-next-ai/ai-gateway": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@the-next-ai/ai-gateway/-/ai-gateway-1.0.1.tgz", - "integrity": "sha512-o752COZV+AV1NiaO9TO7myr3SoeIHL5Cb9ZpPi74vPm1Eo3gP0oSxOzd48WbTQFN5ZP0YPmsMCzEBjqkyTNa9g==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@the-next-ai/ai-gateway/-/ai-gateway-1.0.2.tgz", + "integrity": "sha512-K0UiXwyC1i6dpPtQWDndJ1SgwI+zmfGpwqFUWhnVico4dxCt9eph5d9mt0RHcEduZOv4MRBdWDHyHMpxDgSUCw==", "license": "MIT", "dependencies": { "diff": "^8.0.3", diff --git a/package.json b/package.json index 8cc0416..ab86d4f 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "rebuild:sqlite3": "electron-rebuild -f -w better-sqlite3" }, "dependencies": { - "@the-next-ai/ai-gateway": "^1.0.1", + "@the-next-ai/ai-gateway": "^1.0.2", "@the-next-ai/bot-gateway-sdk": "^0.1.0", "better-sqlite3": "^12.11.1", "electron-updater": "^6.8.9", diff --git a/src/main/claude-app-gateway-service.ts b/src/main/claude-app-gateway-service.ts index 3a56652..89d1e35 100644 --- a/src/main/claude-app-gateway-service.ts +++ b/src/main/claude-app-gateway-service.ts @@ -10,7 +10,7 @@ import { type ClaudeAppGatewayInferenceModel, type ClaudeAppGatewayModelRouteOptions } from "../shared/claude-app-gateway"; -import type { ApiKeyConfig, AppConfig, ClaudeAppGatewayApplyResult } from "../shared/app"; +import { NO_AVAILABLE_GATEWAY_MODELS_MESSAGE, hasAvailableGatewayModels, type ApiKeyConfig, type AppConfig, type ClaudeAppGatewayApplyResult } from "../shared/app"; import { findModelCatalogEntry } from "../server/gateway/model-catalog"; const CLAUDE_APP_CONFIG_ID = "8f69f2f1-3275-4ad8-9317-4aa7e972f311"; @@ -77,6 +77,14 @@ export type ClaudeAppGatewaySyncResult = { }; export async function syncClaudeAppGatewayConfig(config: AppConfig): Promise { + if (!hasAvailableGatewayModels(config)) { + return { + config, + configChanged: false, + result: skippedClaudeAppGatewayResult(config) + }; + } + const applied = applyClaudeAppGatewayConfig(config); if (applied.config === config) { return { @@ -93,7 +101,25 @@ export async function syncClaudeAppGatewayConfig(config: AppConfig): Promise { const configDir = CONFIGDIR; const config = await loadAppConfig(); + assertAvailableGatewayModels(config); await applyProfileConfig(config); const profile = findProfileForOpen(config, profileOptions.profileRef); const surface = profileOptions.surface ?? (profile.agent === "zcode" || profile.surface === "app" ? "app" : "cli"); diff --git a/src/main/presets/index.ts b/src/main/presets/index.ts index 424f941..2c48c59 100644 --- a/src/main/presets/index.ts +++ b/src/main/presets/index.ts @@ -2,6 +2,7 @@ import { anthropicProviderPreset } from "./anthropic"; import { bailianProviderPreset } from "./bailian"; import { deepSeekProviderPreset } from "./deepseek"; import { geminiProviderPreset } from "./gemini"; +import { kimiCodingProviderPreset } from "./kimi-coding"; import { mistralProviderPreset } from "./mistral"; import { moonshotProviderPreset } from "./moonshot"; import { openaiProviderPreset } from "./openai"; @@ -28,6 +29,7 @@ export const providerPresets: ProviderPreset[] = [ geminiProviderPreset, openRouterProviderPreset, deepSeekProviderPreset, + kimiCodingProviderPreset, zhipuCnCodingProviderPreset, zhipuCnGeneralProviderPreset, zaiGlobalCodingProviderPreset, diff --git a/src/main/presets/kimi-coding/index.ts b/src/main/presets/kimi-coding/index.ts new file mode 100644 index 0000000..9ff6dd8 --- /dev/null +++ b/src/main/presets/kimi-coding/index.ts @@ -0,0 +1,39 @@ +import type { ProviderAccountConfig } from "../../../shared/app"; +import type { ProviderPreset } from "../../../shared/provider-presets"; + +const kimiCodingProviderAccountConfig: ProviderAccountConfig = { + connectors: [ + { + auth: "provider-api-key", + endpoint: "https://api.kimi.com/coding/v1/usages", + mapping: { + meters: [] + }, + parser: "kimi-code-usages", + type: "http-json" + } + ], + enabled: true +}; + +export const kimiCodingProviderPreset: ProviderPreset = { + account: kimiCodingProviderAccountConfig, + aliases: ["kimi code", "kimi coding", "kimi coding plan", "kimi-for-coding"], + defaultModelDisplayNames: { + "kimi-for-coding": "K2.7 Code" + }, + defaultModels: ["kimi-for-coding"], + endpoints: [ + { + baseUrl: "https://api.kimi.com/coding/v1", + protocols: ["openai_chat_completions"] + }, + { + baseUrl: "https://api.kimi.com/coding/", + protocols: ["anthropic_messages"] + } + ], + id: "kimi-coding", + name: "Kimi Code - Coding Plan", + websiteUrl: "https://www.kimi.com/code/docs/" +}; diff --git a/src/main/presets/moonshot/index.ts b/src/main/presets/moonshot/index.ts index ea1e75a..c7c9125 100644 --- a/src/main/presets/moonshot/index.ts +++ b/src/main/presets/moonshot/index.ts @@ -5,7 +5,7 @@ const moonshotProviderAccountConfig: ProviderAccountConfig = { connectors: [ { auth: "provider-api-key", - endpoint: "https://api.moonshot.cn/v1/users/me/balance", + endpoint: "https://api.moonshot.ai/v1/users/me/balance", mapping: { meters: [ { @@ -42,6 +42,10 @@ export const moonshotProviderPreset: ProviderPreset = { aliases: ["kimi", "moonshot"], defaultModels: ["moonshot-v1-8k"], endpoints: [ + { + baseUrl: "https://api.moonshot.ai/v1", + protocols: ["openai_chat_completions"] + }, { baseUrl: "https://api.moonshot.cn/v1", protocols: ["openai_chat_completions"] diff --git a/src/main/profile-launch-service.ts b/src/main/profile-launch-service.ts index ed18cdf..16b2b48 100644 --- a/src/main/profile-launch-service.ts +++ b/src/main/profile-launch-service.ts @@ -2,7 +2,7 @@ import { spawn, spawnSync, type ChildProcess } from "node:child_process"; import { chmodSync, existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs"; import os from "node:os"; import path from "node:path"; -import type { AppConfig, ProfileOpenCommandResult, ProfileOpenRequest, ProfileOpenResult, ProfileRuntimeEntry, ProfileRuntimeStatus, ProfileStopResult } from "../shared/app"; +import { assertAvailableGatewayModels, type AppConfig, type ProfileOpenCommandResult, type ProfileOpenRequest, type ProfileOpenResult, type ProfileRuntimeEntry, type ProfileRuntimeStatus, type ProfileStopResult } from "../shared/app"; import { botGatewayProfileEnv } from "./bot-gateway-env"; import { applyClaudeAppGatewayConfig, readClaudeAppGatewayApiKeyCandidates } from "./claude-app-gateway-service"; import { launchClaudeAppProfile, resolveClaudeAppProfileUserDataDir } from "./claude-app-launch"; @@ -41,6 +41,7 @@ type RunningProfileApp = ProfileRuntimeEntry & { process.once("exit", () => stopClaudeAppBotWorker()); export async function getProfileOpenCommand(config: AppConfig, request: ProfileOpenRequest): Promise { + assertAvailableGatewayModels(config); await applyProfileConfig(config); const profile = findProfileForOpen(config, request.profileId); const surface = resolveProfileOpenSurface(profile, request.surface); @@ -54,6 +55,7 @@ export async function getProfileOpenCommand(config: AppConfig, request: ProfileO } export async function openProfileFromCcr(config: AppConfig, request: ProfileOpenRequest): Promise { + assertAvailableGatewayModels(config); await applyProfileConfig(config); const profile = findProfileForOpen(config, request.profileId); const surface = resolveProfileOpenSurface(profile, request.surface); diff --git a/src/main/profile-service.ts b/src/main/profile-service.ts index 3a4521b..32087bd 100644 --- a/src/main/profile-service.ts +++ b/src/main/profile-service.ts @@ -1,8 +1,8 @@ import { randomBytes } from "node:crypto"; -import { chmodSync, copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import { chmodSync, copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import os from "node:os"; import path from "node:path"; -import { CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY_ENV, enforceSingleEnabledGlobalProfilePerAgent, type ApiKeyConfig, type AppConfig, type ProfileApplyResult, type ProfileClientApplyStatus, type ProfileClientKind, type ProfileConfig } from "../shared/app"; +import { CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY_ENV, NO_AVAILABLE_GATEWAY_MODELS_MESSAGE, enforceSingleEnabledGlobalProfilePerAgent, hasAvailableGatewayModels, type ApiKeyConfig, type AppConfig, type ProfileApplyResult, type ProfileClientApplyStatus, type ProfileClientKind, type ProfileConfig } from "../shared/app"; import { replacePersistedApiKeys } from "./api-key-store"; import { botGatewayProfileEnv } from "./bot-gateway-env"; import { codexCliMiddlewareRuntimeScript } from "./codex-cli-middleware-runtime"; @@ -15,6 +15,8 @@ const managedRootStart = "# BEGIN CCR managed profile"; const managedRootEnd = "# END CCR managed profile"; const managedProviderStart = "# BEGIN CCR managed Codex provider"; const managedProviderEnd = "# END CCR managed Codex provider"; +const originalBackupSuffix = ".ccr-original"; +const originalMissingSuffix = ".ccr-original-missing"; const fallbackClientToken = "ccr-local"; const privateDirMode = 0o700; const privateExecutableMode = 0o700; @@ -24,13 +26,28 @@ const publicExecutableMode = 0o755; export async function applyProfileConfig(config: AppConfig): Promise { const appliedAt = new Date().toISOString(); const profiles = profileEntries(config); - const profileApiKeys = await ensureProfileApiKeys(config, profiles); const result: ProfileApplyResult = { appliedAt, clients: [], enabled: profiles.some((profile) => profile.enabled) }; + if (!result.enabled) { + result.clients = profiles.map(disabledProfileStatus); + return result; + } + + if (!hasAvailableGatewayModels(config)) { + result.clients = profiles.map((profile) => + profile.enabled + ? unavailableModelStatus(profile, profilePath(profile)) + : disabledProfileStatus(profile) + ); + return result; + } + + const profileApiKeys = await ensureProfileApiKeys(config, profiles); + for (const profile of profiles) { const token = profileApiKeys.get(profile.id) ?? fallbackClientToken; result.clients.push( @@ -56,7 +73,7 @@ export function applyProfileRuntimeConfig(config: AppConfig, profile: ProfileCon function applyClaudeCodeProfile(config: AppConfig, profile: ProfileConfig, token: string, appliedAt: string): ProfileClientApplyStatus { const settingsFile = resolveClaudeCodeSettingsFile(profile); if (!profile.enabled) { - return disabledStatus("claude-code", settingsFile, "Claude Code profile is disabled."); + return restoreDisabledGlobalProfile(profile, settingsFile, "Claude Code profile is disabled.", isManagedClaudeCodeSettingsContent); } try { @@ -117,7 +134,12 @@ function applyCodexProfile(config: AppConfig, profile: ProfileConfig, token: str const clientName = codexCompatibleClientName(profile.agent); const configFile = resolveCodexConfigFile(profile); if (!profile.enabled) { - return disabledStatus(profile.agent, configFile, `${clientName} profile is disabled.`); + return restoreDisabledGlobalProfile( + profile, + configFile, + `${clientName} profile is disabled.`, + (content) => isManagedCodexConfigContent(content, sanitizeCodexProviderId(profile.providerId || "") || "claude-code-router") + ); } try { @@ -187,7 +209,7 @@ function applyCodexProfile(config: AppConfig, profile: ProfileConfig, token: str function applyZcodeProfile(config: AppConfig, profile: ProfileConfig, token: string, appliedAt: string): ProfileClientApplyStatus { const configFile = resolveZcodeConfigFile(profile); if (!profile.enabled) { - return disabledStatus("zcode", configFile, "ZCode profile is disabled."); + return restoreDisabledZcodeProfile(profile, configFile); } try { @@ -942,6 +964,7 @@ function writeFileWithBackup( chmodFileIfRequested(file, options.mode); return { changed: false }; } + ensureOriginalSnapshot(file, previous, options.mode); const backupFile = previous === undefined ? undefined : backupFilePath(file); if (backupFile) { copyFileSync(file, backupFile); @@ -952,6 +975,200 @@ function writeFileWithBackup( return { backupFile, changed: true }; } +type RestoreFileResult = { + backupFile?: string; + changed: boolean; + file: string; + missingBackup: boolean; + restored: boolean; +}; + +function restoreDisabledGlobalProfile( + profile: ProfileConfig, + file: string, + disabledMessage: string, + isManagedContent: (content: string) => boolean +): ProfileClientApplyStatus { + if (!isGlobalProfile(profile)) { + return disabledStatus(profile.agent, file, disabledMessage); + } + + const restoreResult = restoreGlobalConfigFile(file, { isManagedContent, mode: privateFileMode }); + return disabledRestoreStatus(profile.agent, file, disabledMessage, restoreResult, profile.name || profile.id || profile.agent); +} + +function disabledProfileStatus(profile: ProfileConfig): ProfileClientApplyStatus { + if (profile.agent === "claude-code") { + return restoreDisabledGlobalProfile(profile, resolveClaudeCodeSettingsFile(profile), "Claude Code profile is disabled.", isManagedClaudeCodeSettingsContent); + } + if (profile.agent === "zcode") { + return restoreDisabledZcodeProfile(profile, resolveZcodeConfigFile(profile)); + } + const providerId = sanitizeCodexProviderId(profile.providerId || "") || "claude-code-router"; + return restoreDisabledGlobalProfile( + profile, + resolveCodexConfigFile(profile), + "Codex profile is disabled.", + (content) => isManagedCodexConfigContent(content, providerId) + ); +} + +function restoreDisabledZcodeProfile(profile: ProfileConfig, configFile: string): ProfileClientApplyStatus { + const disabledMessage = "ZCode profile is disabled."; + if (!isGlobalProfile(profile)) { + return disabledStatus("zcode", configFile, disabledMessage); + } + + const providerId = sanitizeCodexProviderId(profile.providerId || "") || "claude-code-router"; + const storageRoot = zcodeHomeFromConfigFile(configFile); + const files = [ + configFile, + path.join(storageRoot, "v2", "config.json"), + path.join(storageRoot, "v2", "bots-model-cache.v2.json") + ]; + const results = files.map((file) => + restoreGlobalConfigFile(file, { + isManagedContent: (content) => isManagedZcodeConfigContent(content, providerId), + mode: privateFileMode + }) + ); + const changed = results.some((result) => result.changed); + const restored = results.some((result) => result.restored); + const missingBackup = results.some((result) => result.missingBackup); + return { + backupFile: results.find((result) => result.backupFile)?.backupFile, + client: "zcode", + enabled: false, + message: missingBackup + ? `${disabledMessage} No original ZCode config backup was found for ${profile.name || profile.id || "this profile"}.` + : restored + ? changed + ? "ZCode config was restored from the CCR backup because the global profile is disabled." + : "ZCode config already matches the CCR backup; profile is disabled." + : disabledMessage, + ok: !missingBackup, + path: resolveUserPath(configFile) + }; +} + +function disabledRestoreStatus( + client: ProfileClientKind, + file: string, + disabledMessage: string, + restoreResult: RestoreFileResult, + profileName: string +): ProfileClientApplyStatus { + return { + backupFile: restoreResult.backupFile, + client, + enabled: false, + message: restoreResult.missingBackup + ? `${disabledMessage} No original ${codexCompatibleClientName(client)} config backup was found for ${profileName}.` + : restoreResult.restored + ? restoreResult.changed + ? `${codexCompatibleClientName(client)} config was restored from the CCR backup because the global profile is disabled.` + : `${codexCompatibleClientName(client)} config already matches the CCR backup; profile is disabled.` + : disabledMessage, + ok: !restoreResult.missingBackup, + path: resolveUserPath(file) + }; +} + +function restoreGlobalConfigFile( + file: string, + options: { + isManagedContent: (content: string) => boolean; + mode?: number; + } +): RestoreFileResult { + const current = existsSync(file) ? readFileSync(file, "utf8") : undefined; + const currentManaged = current !== undefined && options.isManagedContent(current); + if (current !== undefined && !currentManaged) { + return { changed: false, file, missingBackup: false, restored: false }; + } + + if (existsSync(originalMissingFilePath(file))) { + if (currentManaged) { + const backupFile = backupCurrentConfigFile(file, options.mode); + rmSync(file, { force: true }); + return { backupFile, changed: true, file, missingBackup: false, restored: true }; + } + return { changed: false, file, missingBackup: false, restored: current === undefined }; + } + + const snapshot = originalSnapshotCandidate(file, options.isManagedContent); + if (!snapshot) { + return { + changed: false, + file, + missingBackup: Boolean(currentManaged), + restored: false + }; + } + + if (current === snapshot.content) { + chmodFileIfRequested(file, options.mode); + return { changed: false, file, missingBackup: false, restored: true }; + } + + const backupFile = current === undefined ? undefined : backupCurrentConfigFile(file, options.mode); + mkdirSync(path.dirname(file), { recursive: true }); + writeFileSync(file, snapshot.content, options.mode === undefined ? "utf8" : { encoding: "utf8", mode: options.mode }); + chmodFileIfRequested(file, options.mode); + return { backupFile, changed: true, file, missingBackup: false, restored: true }; +} + +function originalSnapshotCandidate( + file: string, + isManagedContent: (content: string) => boolean +): { content: string; file: string } | undefined { + for (const candidate of [originalBackupFilePath(file), ...backupFiles(file)]) { + if (!existsSync(candidate)) { + continue; + } + const content = readFileSync(candidate, "utf8"); + if (!isManagedContent(content)) { + return { content, file: candidate }; + } + } + return undefined; +} + +function backupCurrentConfigFile(file: string, mode: number | undefined): string { + const backupFile = backupFilePath(file); + copyFileSync(file, backupFile); + chmodFileIfRequested(backupFile, mode); + return backupFile; +} + +function backupFiles(file: string): string[] { + const dir = path.dirname(file); + const prefix = `${path.basename(file)}.ccr-backup-`; + try { + return readdirSync(dir) + .filter((entry) => entry.startsWith(prefix)) + .sort() + .map((entry) => path.join(dir, entry)); + } catch { + return []; + } +} + +function ensureOriginalSnapshot(file: string, previous: string | undefined, mode: number | undefined): void { + const originalBackup = originalBackupFilePath(file); + const originalMissing = originalMissingFilePath(file); + if (existsSync(originalBackup) || existsSync(originalMissing)) { + return; + } + if (previous === undefined) { + writeFileSync(originalMissing, "", "utf8"); + chmodFileIfRequested(originalMissing, mode); + return; + } + copyFileSync(file, originalBackup); + chmodFileIfRequested(originalBackup, mode); +} + function chmodFileIfRequested(file: string, mode: number | undefined): void { if (mode === undefined || process.platform === "win32") { return; @@ -968,6 +1185,14 @@ function backupFilePath(file: string): string { return `${file}.ccr-backup-${timestamp}`; } +function originalBackupFilePath(file: string): string { + return `${file}${originalBackupSuffix}`; +} + +function originalMissingFilePath(file: string): string { + return `${file}${originalMissingSuffix}`; +} + function disabledStatus(client: ProfileClientKind, file: string, message: string): ProfileClientApplyStatus { return { client, @@ -978,6 +1203,81 @@ function disabledStatus(client: ProfileClientKind, file: string, message: string }; } +function unavailableModelStatus(profile: ProfileConfig, file: string): ProfileClientApplyStatus { + return { + client: profile.agent, + enabled: true, + message: NO_AVAILABLE_GATEWAY_MODELS_MESSAGE, + ok: false, + path: resolveUserPath(file) + }; +} + +function disabledProfileMessage(profile: ProfileConfig): string { + if (profile.agent === "claude-code") { + return "Claude Code profile is disabled."; + } + return `${codexCompatibleClientName(profile.agent)} profile is disabled.`; +} + +function isGlobalProfile(profile: ProfileConfig): boolean { + return normalizeProfileScope(profile.scope) === "global"; +} + +function isManagedClaudeCodeSettingsContent(content: string): boolean { + const settings = parseJsonContent(content); + if (!settings) { + return false; + } + const apiKeyHelper = typeof settings.apiKeyHelper === "string" ? settings.apiKeyHelper : ""; + if (apiKeyHelper.includes("ccr-claude-code-api-key-")) { + return true; + } + const env = isRecord(settings.env) ? settings.env : {}; + return typeof env.ANTHROPIC_BASE_URL === "string" && + typeof env.ANTHROPIC_API_BASE_URL === "string" && + typeof env.CLAUDE_AGENT_API_BASE_URL === "string"; +} + +function isManagedCodexConfigContent(content: string, providerId: string): boolean { + if (content.includes(managedRootStart) || content.includes(managedProviderStart)) { + return true; + } + const escapedProvider = escapeRegExp(providerId); + return new RegExp(`^\\s*\\[model_providers\\.(?:${escapedProvider}|${escapeRegExp(tomlQuotedKey(providerId))})\\]`, "m").test(content); +} + +function isManagedZcodeConfigContent(content: string, providerId: string): boolean { + const config = parseJsonContent(content); + if (!config) { + return false; + } + if (isRecord(config.provider) && hasOwn(config.provider, providerId)) { + return true; + } + if (isRecord(config.model) && typeof config.model.main === "string" && config.model.main.startsWith(`${providerId}/`)) { + return true; + } + for (const key of ["defaultModel", "lastUsed", "lastUsedModel"]) { + const modelRef = config[key]; + if (isRecord(modelRef) && modelRef.providerId === providerId) { + return true; + } + } + return Array.isArray(config.providers) && config.providers.some((provider) => + isRecord(provider) && provider.id === providerId + ); +} + +function parseJsonContent(content: string): Record | undefined { + try { + const parsed = JSON.parse(content) as unknown; + return isRecord(parsed) ? parsed : undefined; + } catch { + return undefined; + } +} + function gatewayEndpoint(config: AppConfig): string { const host = config.gateway.host === "0.0.0.0" ? "127.0.0.1" : config.gateway.host || "127.0.0.1"; const formattedHost = host.includes(":") && !host.startsWith("[") ? `[${host}]` : host; @@ -1040,6 +1340,9 @@ function normalizeProfileSurface(value: ProfileConfig["surface"]): "auto" | "cli } function codexCompatibleClientName(agent: ProfileConfig["agent"]): string { + if (agent === "claude-code") { + return "Claude Code"; + } return agent === "zcode" ? "ZCode" : "Codex"; } @@ -1131,6 +1434,10 @@ function isRecord(value: unknown): value is Record { return typeof value === "object" && value !== null && !Array.isArray(value); } +function hasOwn(value: Record, key: string): boolean { + return Object.prototype.hasOwnProperty.call(value, key); +} + function escapeRegExp(value: string): string { return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } diff --git a/src/main/provider-account-service.ts b/src/main/provider-account-service.ts index 52b5478..68ba07e 100644 --- a/src/main/provider-account-service.ts +++ b/src/main/provider-account-service.ts @@ -82,9 +82,13 @@ export async function getProviderAccountSnapshots( }); const snapshots = await Promise.all( - providers.flatMap((provider) => - providerAccountTargets(provider).map((target) => resolveProviderAccountSnapshot(config, target, options)) - ) + providers.flatMap((provider) => { + const targets = providerAccountTargets(provider); + if (targets.length > 0) { + return targets.map((target) => resolveProviderAccountSnapshot(config, target, options)); + } + return providerAccountUnavailableSnapshots(provider).map((snapshot) => Promise.resolve(snapshot)); + }) ); return snapshots.filter((snapshot): snapshot is ProviderAccountSnapshot => Boolean(snapshot)); } @@ -125,6 +129,17 @@ export async function testProviderAccountConnector(request: ProviderAccountTestR type: "http-json" }; const payload = await fetchJson(connector.endpoint, provider, connector.auth, connector.headers, connector.method, connector.body); + if (connector.parser === "kimi-code-usages") { + const meters = kimiCodeUsageMeters(payload); + return { + meters, + message: meters.length === 0 ? "No usage data available." : undefined, + paths: flattenJsonPaths(payload), + payload, + status: statusFromMeters(meters, [], 1) + }; + } + const meters = connector.mapping.meters .map((meter) => mappedMeterFromPayload(meter, payload)) .filter((meter): meter is ProviderAccountMeter => Boolean(meter)); @@ -298,6 +313,50 @@ function providerAccountTargets(provider: GatewayProviderConfig): ProviderAccoun return providerAccount && providerApiKey(provider) ? [{ account: providerAccount, provider }] : []; } +function providerAccountUnavailableSnapshots(provider: GatewayProviderConfig): ProviderAccountSnapshot[] { + const credentials = activeProviderCredentials(provider); + if (credentials.length > 0) { + return credentials + .map((credential) => providerAccountUnavailableSnapshot(provider, credential.account ?? provider.account, credential)) + .filter((snapshot): snapshot is ProviderAccountSnapshot => Boolean(snapshot)); + } + + const snapshot = providerAccountUnavailableSnapshot(provider, provider.account); + return snapshot ? [snapshot] : []; +} + +function providerAccountUnavailableSnapshot( + provider: GatewayProviderConfig, + account: ProviderAccountConfig | undefined, + credential?: ProviderCredentialConfig +): ProviderAccountSnapshot | undefined { + const providerName = provider.name.trim(); + if (!providerName || !account?.enabled || !providerAccountConnectorsAreDefaultStandard(account.connectors ?? [])) { + return undefined; + } + if (effectiveProviderAccountConfig(provider, account)) { + return undefined; + } + + const message = "No supported account usage endpoint is available for this provider. Configure an HTTP JSON connector or disable account balance."; + return { + credentialId: credential ? providerCredentialRuntimeId(provider, credential) : undefined, + credentialLabel: credential?.name ?? credential?.label ?? credential?.id, + errors: [ + { + message, + source: "unsupported" + } + ], + message, + meters: [], + provider: providerName, + source: "unsupported", + status: "unsupported", + updatedAt: new Date().toISOString() + }; +} + function effectiveProviderAccount(provider: GatewayProviderConfig): ProviderAccountConfig | undefined { return effectiveProviderAccountConfig(provider, provider.account); } @@ -447,6 +506,16 @@ async function resolveHttpJsonConnector( ...(connector.headers ?? {}), ...(request.headers ?? {}) }, connector.method, connector.body); + if (connector.parser === "kimi-code-usages") { + const meters = kimiCodeUsageMeters(payload); + return { + errors: [], + message: meters.length === 0 ? "No usage data available." : undefined, + meters, + source: "http-json" + }; + } + const meters = connector.mapping.meters .map((meter) => mappedMeterFromPayload(meter, payload)) .filter((meter): meter is ProviderAccountMeter => Boolean(meter)); @@ -620,6 +689,172 @@ function normalizeRemoteSnapshot( }; } +function kimiCodeUsageMeters(payload: unknown): ProviderAccountMeter[] { + if (!isRecord(payload)) { + return []; + } + + const meters: ProviderAccountMeter[] = []; + const usage = isRecord(payload.usage) ? kimiCodeUsageMeter(payload.usage, "weekly_quota", "Weekly quota") : undefined; + if (usage) { + meters.push(usage); + } + + if (Array.isArray(payload.limits)) { + const seenIds = new Set(meters.map((meter) => meter.id)); + payload.limits.forEach((item, index) => { + if (!isRecord(item)) { + return; + } + const detail = isRecord(item.detail) ? item.detail : item; + const window = isRecord(item.window) ? item.window : {}; + const label = kimiCodeUsageLimitLabel(item, detail, window, index); + const meter = kimiCodeUsageMeter(detail, uniqueKimiCodeUsageMeterId(kimiCodeUsageMeterId(item, detail, label, index), seenIds), label, item); + if (meter) { + seenIds.add(meter.id); + meters.push(meter); + } + }); + } + + return meters; +} + +function kimiCodeUsageMeter( + data: Record, + id: string, + defaultLabel: string, + fallbackData?: Record +): ProviderAccountMeter | undefined { + const limit = normalizeNumber(data.limit); + let used = normalizeNumber(data.used); + let remaining = normalizeNumber(data.remaining); + if (used === undefined && remaining !== undefined && limit !== undefined) { + used = limit - remaining; + } + if (remaining === undefined && used !== undefined && limit !== undefined) { + remaining = limit - used; + } + if (limit === undefined && used === undefined && remaining === undefined) { + return undefined; + } + + const label = readString(data.name) || readString(data.title) || defaultLabel; + const resetAt = kimiCodeUsageResetAt(data) ?? (fallbackData ? kimiCodeUsageResetAt(fallbackData) : undefined); + if (limit !== undefined && limit > 0) { + const remainingRatio = remaining !== undefined + ? Math.max(0, Math.min(1, remaining / limit)) + : used !== undefined + ? Math.max(0, Math.min(1, (limit - used) / limit)) + : undefined; + const remainingPercent = remainingRatio === undefined ? undefined : remainingRatio * 100; + return { + id, + kind: "quota", + label, + limit: 100, + remaining: remainingPercent, + resetAt, + source: "http-json", + unit: "%", + used: remainingPercent === undefined ? undefined : 100 - remainingPercent + }; + } + + return { + id, + kind: "quota", + label, + limit, + remaining, + resetAt, + source: "http-json", + unit: "quota", + used + }; +} + +function kimiCodeUsageLimitLabel( + item: Record, + detail: Record, + window: Record, + index: number +): string { + const named = readString(item.name) || readString(detail.name) || readString(item.title) || readString(detail.title) || readString(item.scope) || readString(detail.scope); + if (named) { + return named; + } + + const duration = normalizeNumber(window.duration) ?? normalizeNumber(item.duration) ?? normalizeNumber(detail.duration); + const timeUnit = (readString(window.timeUnit) || readString(item.timeUnit) || readString(detail.timeUnit) || "").toUpperCase(); + if (duration && duration > 0) { + if (timeUnit.includes("MINUTE")) { + return duration >= 60 && duration % 60 === 0 ? `${duration / 60}h quota` : `${duration}m quota`; + } + if (timeUnit.includes("HOUR")) { + return `${duration}h quota`; + } + if (timeUnit.includes("DAY")) { + return `${duration}d quota`; + } + return `${duration}s quota`; + } + + return `Limit #${index + 1}`; +} + +function kimiCodeUsageMeterId( + item: Record, + detail: Record, + label: string, + index: number +): string { + const explicit = readString(item.id) || readString(detail.id) || readString(item.name) || readString(detail.name) || readString(item.scope) || readString(detail.scope); + return providerCredentialSlug(explicit || label || `limit-${index + 1}`) || `limit-${index + 1}`; +} + +function uniqueKimiCodeUsageMeterId(id: string, seenIds: Set): string { + if (!seenIds.has(id)) { + return id; + } + let index = 2; + while (seenIds.has(`${id}-${index}`)) { + index += 1; + } + return `${id}-${index}`; +} + +function kimiCodeUsageResetAt(data: Record): string | undefined { + for (const key of ["reset_at", "resetAt", "reset_time", "resetTime"]) { + const value = data[key]; + const timestamp = typeof value === "number" + ? value + : typeof value === "string" && /^\d+$/.test(value.trim()) + ? Number(value) + : undefined; + if (timestamp !== undefined && Number.isFinite(timestamp)) { + const milliseconds = timestamp < 1_000_000_000_000 ? timestamp * 1000 : timestamp; + const date = new Date(milliseconds); + return Number.isNaN(date.getTime()) ? undefined : date.toISOString(); + } + + const dateString = readString(value); + if (dateString) { + const date = new Date(dateString); + return Number.isNaN(date.getTime()) ? dateString : date.toISOString(); + } + } + + for (const key of ["reset_in", "resetIn", "ttl", "window"]) { + const seconds = normalizeNumber(data[key]); + if (seconds && seconds > 0) { + return new Date(Date.now() + seconds * 1000).toISOString(); + } + } + + return undefined; +} + function normalizeRemoteErrors(value: unknown, source: ProviderAccountConnectorSource): ProviderAccountConnectorError[] | undefined { if (!Array.isArray(value)) { return undefined; @@ -773,14 +1008,16 @@ function localAgentProviderPluginMatches(plugin: unknown, provider: GatewayProvi function localCodexAccountCredential(plugin: Record): { apiKey?: string; headers?: Record } { const codexOauth = isRecord(plugin.codexOauth) ? plugin.codexOauth : {}; const codexAuth = readCodexAuth(); + // Imported plugins contain a point-in-time access token. Prefer the live Codex + // auth file so account checks follow tokens refreshed by Codex CLI/App. const apiKey = + codexAuth?.accessToken || readString(codexOauth.accessToken) || - readString(codexOauth.access_token) || - codexAuth?.accessToken; + readString(codexOauth.access_token); const accountId = + codexAuth?.accountId || readString(codexOauth.accountId) || - readString(codexOauth.account_id) || - codexAuth?.accountId; + readString(codexOauth.account_id); const headers = { ...localProviderPluginAuthHeaders(plugin), ...(accountId ? { "ChatGPT-Account-Id": accountId } : {}), diff --git a/src/main/provider-model-catalog.ts b/src/main/provider-model-catalog.ts index 55cea18..e9e465c 100644 --- a/src/main/provider-model-catalog.ts +++ b/src/main/provider-model-catalog.ts @@ -35,6 +35,7 @@ const presetCatalogProviderIds: Record = { bailian: ["alibaba-cn"], deepseek: ["deepseek"], gemini: ["google"], + "kimi-coding": ["kimi-for-coding"], mistral: ["mistral"], moonshot: ["moonshotai-cn"], openai: ["openai"], @@ -46,10 +47,29 @@ const presetCatalogProviderIds: Record = { "zhipu-cn-general": ["zhipuai"] }; +const presetCatalogModelOverrides: Record; models: string[]; provider?: string; providerName?: string }> = { + "kimi-coding": { + modelDisplayNames: { + "kimi-for-coding": "K2.7 Code" + }, + models: ["kimi-for-coding"], + provider: "kimi-for-coding", + providerName: "Kimi Code" + } +}; + let catalogIndex: CatalogIndex | undefined; export function getProviderCatalogModels(request: ProviderCatalogModelsRequest): ProviderCatalogModelsResult { const index = loadCatalogIndex(); + const modelOverride = providerCatalogModelOverride(request); + if (modelOverride) { + return { + loadedFrom: index.loadedFrom, + ...modelOverride + }; + } + const match = findBestCatalogProviderMatch(index.providers, request); if (!match) { return { @@ -67,6 +87,34 @@ export function getProviderCatalogModels(request: ProviderCatalogModelsRequest): }; } +function providerCatalogModelOverride(request: ProviderCatalogModelsRequest): ProviderCatalogModelsResult | undefined { + const providerPresetId = request.providerPresetId?.trim() || ""; + const providerPresetOverride = presetCatalogModelOverrides[providerPresetId]; + if (providerPresetOverride) { + return { + matchedBy: "provider-id", + modelDisplayNames: providerPresetOverride.modelDisplayNames, + models: providerPresetOverride.models, + provider: providerPresetOverride.provider, + providerName: providerPresetOverride.providerName + }; + } + + const baseUrlPresetId = request.baseUrl ? findProviderPresetByBaseUrl(request.baseUrl)?.id ?? "" : ""; + const baseUrlOverride = presetCatalogModelOverrides[baseUrlPresetId]; + if (baseUrlOverride) { + return { + matchedBy: "base-url", + modelDisplayNames: baseUrlOverride.modelDisplayNames, + models: baseUrlOverride.models, + provider: baseUrlOverride.provider, + providerName: baseUrlOverride.providerName + }; + } + + return undefined; +} + function loadCatalogIndex(): CatalogIndex { if (catalogIndex) { return catalogIndex; diff --git a/src/main/provider-probe.ts b/src/main/provider-probe.ts index 9f3b5dc..48032f6 100644 --- a/src/main/provider-probe.ts +++ b/src/main/provider-probe.ts @@ -34,12 +34,14 @@ type FetchJsonResult = { type ModelProbeResult = { baseUrl?: string; + modelDisplayNames?: Record; models: string[]; source?: ModelSource; }; type ModelFetchResult = { baseUrl?: string; + modelDisplayNames?: Record; models: string[]; }; @@ -225,6 +227,7 @@ async function resolveGatewayProviderProbe(request: GatewayProviderProbeRequest) return { capabilities: capabilitiesFromProtocolResults(protocolResults), detectedProtocol, + modelDisplayNames: modelProbe.modelDisplayNames, modelSource: modelProbe.source, models: modelProbe.models, normalizedBaseUrl: detectedProtocol @@ -402,11 +405,11 @@ async function fetchModelsForSource(parsed: ParsedProviderUrl, source: ModelSour }, method: "GET" }); - const models = parseModelIds(result.payload, "openai"); - if (models.length > 0) { + const modelList = parseModelList(result.payload, "openai"); + if (modelList.models.length > 0) { return { baseUrl, - models + ...modelList }; } } @@ -424,11 +427,11 @@ async function fetchModelsForSource(parsed: ParsedProviderUrl, source: ModelSour }, method: "GET" }); - const models = parseModelIds(result.payload, "anthropic"); - if (models.length > 0) { + const modelList = parseModelList(result.payload, "anthropic"); + if (modelList.models.length > 0) { return { baseUrl, - models + ...modelList }; } } @@ -446,7 +449,7 @@ async function fetchModelsForSource(parsed: ParsedProviderUrl, source: ModelSour }); return { baseUrl: parsed.geminiBaseUrl, - models: parseModelIds(result.payload, "gemini") + ...parseModelList(result.payload, "gemini") }; } @@ -731,17 +734,41 @@ function geminiHeaders(apiKey: string | undefined): Record { : {}; } -function parseModelIds(payload: unknown, source: ModelSource): string[] { +function parseModelList(payload: unknown, source: ModelSource): Pick { if (!isRecord(payload)) { - return []; + return { + models: [] + }; } const items = Array.isArray(payload.data) ? payload.data : Array.isArray(payload.models) ? payload.models : []; - const models = items - .map((item) => readModelId(item, source)) - .filter((item): item is string => Boolean(item)); + const models: string[] = []; + const modelDisplayNames: Record = {}; - return uniqueStrings(models); + for (const item of items) { + const model = readModelId(item, source); + if (!model) { + continue; + } + models.push(model); + + const displayName = readModelDisplayName(item); + if (displayName && displayName !== model) { + modelDisplayNames[model] = displayName; + } + } + + const uniqueModels = uniqueStrings(models); + const uniqueDisplayNames = Object.fromEntries( + uniqueModels + .map((model) => [model, modelDisplayNames[model]] as const) + .filter((entry): entry is [string, string] => Boolean(entry[1])) + ); + + return { + modelDisplayNames: Object.keys(uniqueDisplayNames).length > 0 ? uniqueDisplayNames : undefined, + models: uniqueModels + }; } function readModelId(value: unknown, source: ModelSource): string | undefined { @@ -771,6 +798,13 @@ function readModelId(value: unknown, source: ModelSource): string | undefined { return rawId; } +function readModelDisplayName(value: unknown): string | undefined { + if (!isRecord(value)) { + return undefined; + } + return readString(value.display_name) || readString(value.displayName) || readString(value.label); +} + function stripGeminiModelPrefix(value: string): string { return value.replace(/^models\//i, ""); } diff --git a/src/main/zcode-profile-config.ts b/src/main/zcode-profile-config.ts index 073eba2..8ac037c 100644 --- a/src/main/zcode-profile-config.ts +++ b/src/main/zcode-profile-config.ts @@ -23,6 +23,8 @@ type ZcodeGatewayConfigValues = { const legacyZcodeTomlConfigFile = "~/.zcode/config.toml"; const defaultZcodeConfigFile = "~/.zcode/cli/config.json"; +const originalBackupSuffix = ".ccr-original"; +const originalMissingSuffix = ".ccr-original-missing"; export function resolveZcodeConfigFile(profile: Pick): string { const configured = profile.configFile?.trim(); @@ -227,6 +229,9 @@ function writeJsonFile(file: string, value: Record, options: { if (previous === content) { return { changed: false, file }; } + if (options.backup !== false) { + ensureOriginalSnapshot(file, previous); + } const backupFile = options.backup === false || previous === undefined ? undefined : backupFilePath(file); if (backupFile) { copyFileSync(file, backupFile); @@ -252,6 +257,19 @@ function backupFilePath(file: string): string { return `${file}.ccr-backup-${timestamp}`; } +function ensureOriginalSnapshot(file: string, previous: string | undefined): void { + const originalBackup = `${file}${originalBackupSuffix}`; + const originalMissing = `${file}${originalMissingSuffix}`; + if (existsSync(originalBackup) || existsSync(originalMissing)) { + return; + } + if (previous === undefined) { + writeFileSync(originalMissing, "", "utf8"); + return; + } + copyFileSync(file, originalBackup); +} + function isLegacyZcodeTomlConfigFile(value: string): boolean { return value === legacyZcodeTomlConfigFile || resolveUserPath(value) === path.join(os.homedir(), ".zcode", "config.toml"); diff --git a/src/renderer/pages/home/App.tsx b/src/renderer/pages/home/App.tsx index de581e4..6173bd2 100644 --- a/src/renderer/pages/home/App.tsx +++ b/src/renderer/pages/home/App.tsx @@ -6,7 +6,7 @@ import { ClaudeDesignRoutingDraft, ClaudeDesignRoutingRuleDraft, cloneConfig, createApiKeyDraft, createApiKeyEditDraft, createApiKeyList, createClaudeDesignRoutingDraft, createClaudeDesignRoutingRuleDraft, createCursorProxyRoutingDraft, createCursorProxyRoutingRuleDraft, createEmptyAgentAnalysis, copyTextToClipboard, createEmptyRequestLogPage, createEmptyUsageStats, createExtensionInstallDraft, createGeneratedApiKey, createPluginSettingsDraft, createProfileDraft, - createProfileDraftFromProfile, createProviderConfigFromDeepLink, createProviderDraft, createProviderDraftFromProvider, createRoutingRuleDraft, createRoutingRuleDraftFromRule, + createProfileDraftFromProfile, createProviderDraft, createProviderDraftFromDeepLinkPayload, createProviderDraftFromProvider, createRoutingRuleDraft, createRoutingRuleDraftFromRule, createVirtualModelDraft, createVirtualModelDraftFromProfile, customProviderPresetId, DEFAULT_TRAY_WIDGETS, detectSystemLanguage, detectSystemTheme, enforceSingleEnabledGlobalProfilePerAgent, ExtensionConfigTarget, ExtensionDeleteTarget, ExtensionInstallDraft, ExtensionSource, fallbackAgentAnalysis, fallbackConfig, @@ -17,7 +17,7 @@ import { isCursorProxyPluginConfig, isMacPlatform, isPlainRecord, isProfileDraftSubmittable, isProviderNameDuplicate, isProviderProbeCandidateReady, isTraySupportedPlatform, isRoutingRewriteDraftRowValid, - LayoutGroup, mergeProviderCapabilities, mergeProviderModelLists, + LayoutGroup, mergeModelDisplayNames, mergeProviderCapabilities, mergeProviderModelLists, navigation, NavigationId, normalizeApiKeys, normalizeBotGatewaySavedConfigs, normalizeConfig, normalizeLanguagePreference, normalizeObservabilityConfig, normalizeOverviewWidgets, normalizeProfileItem, normalizeProfileScope, normalizeProviderBaseUrl, normalizeRouterFallbackConfig, normalizeThemePreference, normalizeTrayBalanceProgressConfig, normalizeTrayIconPreference, normalizeTrayWidgets, normalizeTrayWindowModules, normalizeVirtualModelDraftPatch, numberValue, OnboardingReadinessOptions, OnboardingStepId, onboardingStepOrder, @@ -25,11 +25,10 @@ import { providerCredentialsFromDraft, persistLanguagePreference, PluginMarketplaceEntry, PluginRoutingConfigTarget, pluginSettingsConfigFromDraft, PluginSettingsDraft, presetCapabilitiesFromDraft, probeProviderCandidates, probeProviderDeepLinkPayload, profileAgentLabel, profileEnvRowsForAgent, ProfileConfig, ProfileOpenSurface, ProfileRuntimeStatus, profileConfigFromDraft, providerAccountApiKeySafetyIssue, - providerDeepLinkDisplayIcon, - profileOpenCommandFallback, profileOpenSurfaces, ProviderAccountSnapshot, providerApiKeySafetyIssue, ProviderConnectivityCheckReport, ProviderDeepLinkRequest, providerIdentitySafetyIssue, providerProbeCandidates, + profileOpenCommandFallback, profileOpenSurfaces, ProviderAccountSnapshot, providerApiKeySafetyIssue, ProviderConnectivityCheckReport, ProviderDeepLinkPayload, ProviderDeepLinkRequest, providerIdentitySafetyIssue, providerProbeCandidates, providerProbeCandidatesApiKeySafetyIssue, providerProbeHasSupportedProtocol, providerProbeInputKey, providerSelectableProtocolsFromProbe, ProxyCertificateStatus, ProxyNetworkSnapshot, proxyRestartMessage, ProxyStatus, readLanguagePreference, RequestLogListFilter, RequestLogPage, ResolvedLanguage, - ResolvedTheme, resolvePluginInstallPlan, resolveProviderDeepLinkCatalogModels, resolveProviderDeepLinkIcon, RouterRule, ServerActionBusy, SettingsPageId, + ResolvedTheme, resolvePluginInstallPlan, resolveProviderDeepLinkCatalogModels, RouterRule, ServerActionBusy, SettingsPageId, routingRewriteFromDraftRow, setProviderPresets, splitLines, translateAppErrorMessage, translateProxyCertificateMessage, translateText, TrayBalanceProgressConfig, TrayWidgetConfig, uniqueRoutingRuleId, updateApiKeyEditableConfig, UsageStatsFilter, UsageStatsRange, UsageStatsSnapshot, useEffect, useMemo, useReducedMotion, useRef, useState, validateVirtualModelDraft, ViewId, @@ -199,11 +198,11 @@ function App() { const [providerProbeLoading, setProviderProbeLoading] = useState(false); const [providerConnectivityProbe, setProviderConnectivityProbe] = useState(); const [providerConnectivityLoading, setProviderConnectivityLoading] = useState(false); + const [providerImportOpen, setProviderImportOpen] = useState(false); + const [providerImportPayload, setProviderImportPayload] = useState(); const [providerDeepLinkRequest, setProviderDeepLinkRequest] = useState(); const [providerDeepLinkBusy, setProviderDeepLinkBusy] = useState(false); const [providerDeepLinkError, setProviderDeepLinkError] = useState(""); - const [providerDeepLinkIconLoading, setProviderDeepLinkIconLoading] = useState(false); - const [providerDeepLinkModelsLoading, setProviderDeepLinkModelsLoading] = useState(false); const [proxyCertificateChecking, setProxyCertificateChecking] = useState(false); const [proxyEnablePending, setProxyEnablePending] = useState(false); const [providerProbeError, setProviderProbeError] = useState(""); @@ -339,7 +338,7 @@ function App() { }, []); useEffect(() => { - if (!updateDialogOpen || !window.ccr) { + if (!window.ccr) { return; } @@ -366,7 +365,7 @@ function App() { disposed = true; unsubscribe(); }; - }, [updateDialogOpen]); + }, [appInfo.version]); useEffect(() => { if (!window.ccr) { @@ -377,6 +376,8 @@ function App() { providerProbeRequestId.current += 1; providerConnectivityRequestId.current += 1; setProviderAddOpen(false); + setProviderImportOpen(false); + setProviderImportPayload(undefined); setProviderEditIndex(undefined); setProviderProbe(undefined); setProviderConnectivityProbe(undefined); @@ -386,7 +387,6 @@ function App() { setProviderDeepLinkRequest(request); setProviderDeepLinkError(""); setProviderDeepLinkBusy(false); - setProviderDeepLinkModelsLoading(false); setActiveView("providers"); }; @@ -405,89 +405,12 @@ function App() { }, []); useEffect(() => { - const request = providerDeepLinkRequest; - const payload = request?.provider; - providerDeepLinkIconRequestId.current += 1; - const requestId = providerDeepLinkIconRequestId.current; - - if (!request || !payload || !providerPresetsLoaded || providerDeepLinkDisplayIcon(payload)) { - setProviderDeepLinkIconLoading(false); + const payload = providerDeepLinkRequest?.provider; + if (!payload || !configLoaded || !providerPresetsLoaded) { return; } - - setProviderDeepLinkIconLoading(true); - void resolveProviderDeepLinkIcon(payload) - .then((resolution) => { - if (providerDeepLinkIconRequestId.current !== requestId || !resolution.persistentIcon) { - return; - } - setProviderDeepLinkRequest((current) => { - if (!current?.provider || current.id !== request.id) { - return current; - } - if (current.provider.icon?.trim()) { - return current; - } - return { - ...current, - provider: { - ...current.provider, - icon: resolution.persistentIcon - } - }; - }); - }) - .finally(() => { - if (providerDeepLinkIconRequestId.current === requestId) { - setProviderDeepLinkIconLoading(false); - } - }); - }, [providerDeepLinkRequest?.id, providerDeepLinkRequest?.provider?.baseUrl, providerDeepLinkRequest?.provider?.icon, providerPresetsLoaded]); - - useEffect(() => { - const request = providerDeepLinkRequest; - const payload = request?.provider; - providerDeepLinkCatalogModelsRequestId.current += 1; - const requestId = providerDeepLinkCatalogModelsRequestId.current; - const hasApiKey = Boolean(payload?.apiKey?.trim()); - - if (!request || !payload || (!hasApiKey && payload.models.length > 0) || !providerPresetsLoaded) { - setProviderDeepLinkModelsLoading(false); - return; - } - - const modelsPromise = hasApiKey - ? probeProviderDeepLinkPayload(payload).then((probe) => mergeProviderModelLists(probe?.models ?? [])) - : resolveProviderDeepLinkCatalogModels(payload); - - setProviderDeepLinkModelsLoading(true); - void modelsPromise - .then((models) => { - if (providerDeepLinkCatalogModelsRequestId.current !== requestId || models.length === 0) { - return; - } - setProviderDeepLinkRequest((current) => { - if (!current?.provider || current.id !== request.id || (!hasApiKey && current.provider.models.length > 0)) { - return current; - } - return { - ...current, - provider: { - ...current.provider, - models - } - }; - }); - }) - .catch(() => { - // Model discovery is optional; importing performs the same resolution again. - }) - .finally(() => { - if (providerDeepLinkCatalogModelsRequestId.current === requestId) { - setProviderDeepLinkModelsLoading(false); - } - }); - }, [providerDeepLinkRequest?.id, providerDeepLinkRequest?.provider?.apiKey, providerDeepLinkRequest?.provider?.baseUrl, providerDeepLinkRequest?.provider?.name, providerPresetsLoaded]); + void openImportProviderDialog(payload); + }, [configLoaded, providerDeepLinkRequest?.id, providerDeepLinkRequest?.provider, providerPresetsLoaded]); useEffect(() => { if (!window.ccr) { @@ -717,8 +640,6 @@ function App() { const onboardingProfileDraftSource = useRef(""); const providerProbeRequestId = useRef(0); const providerConnectivityRequestId = useRef(0); - const providerDeepLinkCatalogModelsRequestId = useRef(0); - const providerDeepLinkIconRequestId = useRef(0); const toastTimer = useRef(); const shouldReduceMotion = useReducedMotion(); @@ -887,6 +808,14 @@ function App() { void checkForAppUpdate(); } + function openUpdateDownloadDialog() { + setUpdateDialogOpen(true); + setUpdateActionError(""); + if (updateDialogStatus.canDownload || updateDialogStatus.state === "available") { + void downloadAppUpdate(); + } + } + async function checkForAppUpdate() { if (updateActionBusyRef.current) { return; @@ -1097,6 +1026,8 @@ function App() { providerProbeRequestId.current += 1; providerConnectivityRequestId.current += 1; setProviderEditIndex(undefined); + setProviderImportOpen(false); + setProviderImportPayload(undefined); setProviderDraft(createProviderDraft(draftConfig.Providers)); setProviderProbe(undefined); setProviderConnectivityProbe(undefined); @@ -1117,6 +1048,8 @@ function App() { providerProbeRequestId.current += 1; providerConnectivityRequestId.current += 1; setProviderEditIndex(index); + setProviderImportOpen(false); + setProviderImportPayload(undefined); setProviderDraft(createProviderDraftFromProvider(provider)); setProviderProbe(undefined); setProviderConnectivityProbe(undefined); @@ -1126,6 +1059,63 @@ function App() { setProviderAddOpen(true); } + async function openImportProviderDialog(payload: ProviderDeepLinkPayload) { + if (!providerPresetsLoaded) { + return; + } + const requestId = providerProbeRequestId.current + 1; + providerProbeRequestId.current = requestId; + providerConnectivityRequestId.current += 1; + setProviderDeepLinkBusy(true); + let nextPayload = payload; + let catalogModelDisplayNames: Record | undefined; + let probe: GatewayProviderProbeResult | undefined; + if (nextPayload.models.length === 0) { + const catalogModels = await resolveProviderDeepLinkCatalogModels(nextPayload); + if (providerProbeRequestId.current !== requestId) { + setProviderDeepLinkBusy(false); + return; + } + catalogModelDisplayNames = catalogModels.modelDisplayNames; + if (catalogModels.models.length > 0) { + nextPayload = { + ...nextPayload, + models: catalogModels.models + }; + } + } + probe = await probeProviderDeepLinkPayload(nextPayload); + if (providerProbeRequestId.current !== requestId) { + setProviderDeepLinkBusy(false); + return; + } + if (nextPayload.apiKey?.trim() && probe?.models.length) { + nextPayload = { + ...nextPayload, + models: probe.models + }; + } + + const initialDraftFromPayload = createProviderDraftFromDeepLinkPayload(nextPayload, draftConfig.Providers); + const initialDraft = { + ...initialDraftFromPayload, + modelDisplayNames: mergeModelDisplayNames(initialDraftFromPayload.modelDisplayNames, catalogModelDisplayNames) + }; + setProviderEditIndex(undefined); + setProviderImportOpen(true); + setProviderImportPayload(nextPayload); + setProviderDraft(probe ? applyProviderProbeResult(initialDraft, probe) : initialDraft); + setProviderProbe(probe); + setProviderConnectivityProbe(undefined); + setProviderProbeError(""); + setProviderProbeLoading(false); + setProviderConnectivityLoading(false); + setProviderDeepLinkRequest(undefined); + setProviderDeepLinkError(""); + setProviderDeepLinkBusy(false); + setProviderAddOpen(true); + } + function updateProviderDraft(patch: Partial, resetProbe = false) { const shouldResetProtocolProbe = resetProbe && (patch.baseUrl !== undefined || patch.presetId !== undefined || patch.protocol !== undefined); const shouldResetConnectivityProbe = resetProbe || @@ -1148,6 +1138,7 @@ function App() { return { ...next, + modelDisplayNames: patch.modelDisplayNames, modelsText: mergeProviderModelLists(current.selectedModels, splitLines(next.modelsText)).join("\n"), selectedModels: [], selectedProtocols: patch.selectedProtocols ?? current.selectedProtocols @@ -1430,6 +1421,7 @@ function App() { type: protocol }; const importedProviderPlugins = materializeProviderPluginTemplates(providerDraft.providerPlugins, providerName, protocol); + const wasImport = providerImportOpen; const next = buildConfigUpdate((config) => { if (providerEditIndex === undefined) { @@ -1446,7 +1438,12 @@ function App() { setConfigDraft(next); if (await persistConfig(next, setProviderProbeError)) { setProviderEditIndex(undefined); + setProviderImportOpen(false); + setProviderImportPayload(undefined); setProviderAddOpen(false); + if (wasImport) { + showToast(`${copy.text["Imported provider"] ?? "Imported provider"} ${provider.name}`.trim()); + } if (activeView === "onboarding") { setOnboardingStep(getDefaultOnboardingStep(next, onboardingReadiness)); } @@ -1461,10 +1458,15 @@ function App() { return; } + if (request.provider) { + await openImportProviderDialog(request.provider); + return; + } + setProviderDeepLinkBusy(true); setProviderDeepLinkError(""); try { - if (!request.provider && request.manifest) { + if (request.manifest) { if (!window.ccr?.fetchProviderManifest) { throw new Error("Request failed."); } @@ -1477,61 +1479,7 @@ function App() { return; } - let payload = request.provider; - if (!payload) { - setProviderDeepLinkBusy(false); - return; - } - const identityIssue = providerIdentitySafetyIssue({ - baseUrl: payload.baseUrl, - name: payload.name - }); - if (identityIssue) { - throw new Error(identityIssue.message); - } - const iconResolution = await resolveProviderDeepLinkIcon(payload); - if (iconResolution.persistentIcon && iconResolution.persistentIcon !== payload.icon?.trim()) { - payload = { - ...payload, - icon: iconResolution.persistentIcon - }; - } - if (payload.models.length === 0) { - const catalogModels = await resolveProviderDeepLinkCatalogModels(payload); - if (catalogModels.length > 0) { - payload = { - ...payload, - models: catalogModels - }; - } - } - const probe = await probeProviderDeepLinkPayload(payload); - if (payload.apiKey?.trim() && probe?.models.length) { - payload = { - ...payload, - models: probe.models - }; - } - let importedProviderName = payload.name?.trim() || ""; - const next = buildConfigUpdate((config) => { - const provider = createProviderConfigFromDeepLink(payload, config.Providers, probe); - importedProviderName = provider.name; - config.Providers.push(provider); - if (!config.preferredProvider) { - config.preferredProvider = provider.name; - } - return config; - }); - setConfigDraft(next); - const saved = await persistConfig(next, setProviderDeepLinkError); setProviderDeepLinkBusy(false); - if (saved) { - setProviderDeepLinkRequest(undefined); - showToast(`${copy.text["Imported provider"] ?? "Imported provider"} ${importedProviderName}`.trim()); - if (activeView === "onboarding") { - setOnboardingStep(getDefaultOnboardingStep(next, onboardingReadiness)); - } - } } catch (error) { setProviderDeepLinkError(formatError(error)); setProviderDeepLinkBusy(false); @@ -2802,7 +2750,7 @@ function App() { isMac={isMac} needsTrafficLightSafeArea={needsTrafficLightSafeArea} networkCaptureEnabled={networkCaptureEnabled} - onCheckUpdate={openUpdateDialog} + onDownloadUpdate={openUpdateDownloadDialog} onOpenSettings={openSettingsDialog} onSelectNavigationItem={selectNavigationItem} onToggleSidebar={() => setSidebarOpen((current) => !current)} @@ -2811,6 +2759,7 @@ function App() { sidebarOpen={sidebarOpen} toggleGatewayService={toggleGatewayService} updateActionBusy={Boolean(updateActionBusy)} + updateStatus={updateDialogStatus} visibleNavigation={visibleNavigation} viewProps={{ apiKeys: { @@ -3044,17 +2993,16 @@ function App() { onStopApp: () => void stopProfileApp(profileOpenDialog.profile), profile: profileOpenDialog.profile } : undefined} - providerDeepLink={providerDeepLinkRequest ? { + providerDeepLink={providerDeepLinkRequest && !providerDeepLinkRequest.provider ? { busy: providerDeepLinkBusy, error: providerDeepLinkError, - iconLoading: providerDeepLinkIconLoading, onClose: () => { if (!providerDeepLinkBusy) { setProviderDeepLinkRequest(undefined); } }, onSubmit: confirmProviderDeepLinkImport, - modelsLoading: providerDeepLinkModelsLoading, + presetsLoaded: providerPresetsLoaded, request: providerDeepLinkRequest } : undefined} providerDelete={providerDeleteItem ? { @@ -3068,18 +3016,23 @@ function App() { connectivityProbe: providerConnectivityProbe, draft: providerDraft, error: providerProbeError, + importProvider: providerImportOpen ? providerImportPayload : undefined, onChange: updateProviderDraft, mode: providerEditIndex === undefined ? "add" : "edit", onClose: () => { setProviderAddOpen(false); setProviderEditIndex(undefined); + setProviderImportOpen(false); + setProviderImportPayload(undefined); }, onCheck: checkProviderDraft, onSubmit: submitProviderDraft, probe: providerProbe, probeLoading: providerProbeLoading, providerPlugins: draftConfig.providerPlugins ?? [], - providers: draftConfig.Providers + providers: draftConfig.Providers, + submitLabel: providerImportOpen ? t("Import") : undefined, + title: providerImportOpen ? t("Import Provider") : undefined } : undefined} routingDelete={routingDeleteRule ? { onClose: () => setRoutingDeleteIndex(undefined), diff --git a/src/renderer/pages/home/components/dashboard.tsx b/src/renderer/pages/home/components/dashboard.tsx index ec214d7..ac4aec4 100644 --- a/src/renderer/pages/home/components/dashboard.tsx +++ b/src/renderer/pages/home/components/dashboard.tsx @@ -1930,7 +1930,7 @@ function ProviderAccountsOverview({
{providerAccountSnapshotLabel(account)}
- {providerAccountShowSource(dimensions) ?
{meter ? t(meter.label) : account.source}
: null} + {providerAccountShowSource(dimensions) && meter ?
{t(meter.label)}
: null}
{providerAccountShowStatus(dimensions) ? {account.status} : null} @@ -1950,7 +1950,7 @@ function ProviderAccountsOverview({
{providerAccountSnapshotLabel(account)}
- {providerAccountShowSource(dimensions) ?
{meter ? t(meter.label) : account.source}
: null} + {providerAccountShowSource(dimensions) && meter ?
{t(meter.label)}
: null}
{meter ? formatProviderAccountMeterValue(meter) : account.status}
@@ -1995,7 +1995,6 @@ function ProviderAccountSinglePanel({
{providerAccountSnapshotLabel(account)}
- {providerAccountShowSource(dimensions) ?
{account.source}
: null}
{providerAccountShowStatus(dimensions) ? {account.status} : null}
@@ -2039,7 +2038,6 @@ function ProviderAccountSummaryCard({
{providerAccountSnapshotLabel(account)}
- {providerAccountShowSource(dimensions) ?
{account.source}
: null}
{providerAccountShowStatus(dimensions) ? {account.status} : null}
diff --git a/src/renderer/pages/home/components/layout.tsx b/src/renderer/pages/home/components/layout.tsx index bbc3fea..2dfca05 100644 --- a/src/renderer/pages/home/components/layout.tsx +++ b/src/renderer/pages/home/components/layout.tsx @@ -1,9 +1,9 @@ import type { ComponentProps } from "react"; import { AnimatedIconSwap, AnimatePresence, AppConfig, AppCopy, Button, cn, EndpointTitleBar, - GatewayStatus, listSpringTransition, LucideIcon, motion, motionEase, + AppUpdateStatus, Download, GatewayStatus, listSpringTransition, LucideIcon, motion, motionEase, LoaderCircle, NavigationId, PanelLeftClose, PanelLeftOpen, - reducedMotionTransition, RefreshCw, ServiceControlButton, Settings, ViewId, + reducedMotionTransition, ServiceControlButton, Settings, ViewId, ViewMotionShell, viewUsesInternalScroll } from "../shared"; import { ApiKeysView } from "./api-keys"; @@ -63,7 +63,7 @@ export function MainLayout({ needsTrafficLightSafeArea, agentAnalysisEnabled, networkCaptureEnabled, - onCheckUpdate, + onDownloadUpdate, onOpenSettings, onSelectNavigationItem, onToggleSidebar, @@ -71,6 +71,7 @@ export function MainLayout({ sidebarOpen, toggleGatewayService, updateActionBusy, + updateStatus, viewProps, requestLogsEnabled, visibleNavigation @@ -85,7 +86,7 @@ export function MainLayout({ isMac: boolean; needsTrafficLightSafeArea: boolean; networkCaptureEnabled: boolean; - onCheckUpdate: () => void; + onDownloadUpdate: () => void; onOpenSettings: () => void; onSelectNavigationItem: (id: NavigationId) => void; onToggleSidebar: () => void; @@ -93,13 +94,20 @@ export function MainLayout({ sidebarOpen: boolean; toggleGatewayService: () => void; updateActionBusy: boolean; + updateStatus: AppUpdateStatus; viewProps: MainViewProps; requestLogsEnabled: boolean; visibleNavigation: MainNavigationItem[]; }) { const windowControlSafeAreaWidth = isMac ? 152 : 88; - const checkForUpdatesLabel = copy.text["Check for updates"] ?? "Check for updates"; - const checkingForUpdatesLabel = copy.text["Checking for updates"] ?? "Checking for updates"; + const showUpdateDownloadButton = + updateStatus.supported && + (updateStatus.state === "available" || updateStatus.state === "downloading" || updateStatus.state === "downloaded"); + const updateDownloadLabel = updateStatus.state === "downloaded" + ? copy.text["Update ready to install"] ?? "Update ready to install" + : updateStatus.state === "downloading" + ? copy.text["Downloading update"] ?? "Downloading update" + : copy.text["Download update"] ?? "Download update"; return ( <> @@ -124,6 +132,19 @@ export function MainLayout({ onClick={toggleGatewayService} state={gatewayStatus.state} /> + {showUpdateDownloadButton ? ( + + ) : null}
-
; @@ -349,9 +351,9 @@ function ProviderAccountListCell({ provider, snapshots }: { provider: GatewayPro {providerAccountSnapshotCredentialLabel(snapshot)}
) : null} - {!meter ? ( + {!meter && fallbackText ? (
- {snapshot.message ?? snapshot.errors?.[0]?.message ?? snapshot.source} + {t(fallbackText)}
) : null}
@@ -422,6 +424,7 @@ export function ProviderDeepLinkDialog({ modelsLoading = false, onClose, onSubmit, + presetsLoaded = true, request }: { busy: boolean; @@ -430,6 +433,7 @@ export function ProviderDeepLinkDialog({ modelsLoading?: boolean; onClose: () => void; onSubmit: () => Promise; + presetsLoaded?: boolean; request: ProviderDeepLinkRequest; }) { const t = useAppText(); @@ -437,6 +441,7 @@ export function ProviderDeepLinkDialog({ const manifest = request.manifest; const displayName = provider ? provider.name?.trim() || inferProviderNameFromBaseUrl(provider.baseUrl) : ""; const providerPreset = provider ? resolveProviderDeepLinkPreset(provider) : undefined; + const showExternalProviderWarnings = Boolean(provider && presetsLoaded && !providerPreset); const providerIconUrl = provider ? providerDeepLinkDisplayIcon(provider) : ""; const modelPreview = provider?.models.slice(0, 8) ?? []; const actionLoading = busy || Boolean(provider && (iconLoading || modelsLoading)); @@ -456,19 +461,23 @@ export function ProviderDeepLinkDialog({ {provider ? (
-
-
- - {t("External provider link")} + {showExternalProviderWarnings ? ( +
+
+ + {t("External provider link")} +
+
+ {t("This provider link came from an external website. Review details before importing.")} +
-
- {t("This provider link came from an external website. Review details before importing.")} + ) : null} + {showExternalProviderWarnings ? ( +
+ + {t("Only enter an API key issued for this endpoint. Official provider keys must only be used with official endpoints.")}
-
-
- - {t("Only enter an API key issued for this endpoint. Official provider keys must only be used with official endpoints.")} -
+ ) : null}
@@ -763,6 +772,89 @@ function ProviderPresetIcon({ className, iconUrl: explicitIconUrl, preset }: { c ); } +function ProviderImportHeader({ + draft, + provider, + preset +}: { + draft: AddProviderDraft; + provider: ProviderDeepLinkPayload; + preset?: ProviderPreset; +}) { + const t = useAppText(); + const baseUrl = draft.baseUrl.trim() || provider.baseUrl; + const displayName = draft.name.trim() || provider.name?.trim() || inferProviderNameFromBaseUrl(baseUrl); + const iconUrl = draft.icon.trim() || providerDeepLinkDisplayIcon(provider); + const platformUrl = providerImportPlatformUrl(provider, baseUrl, preset); + + function openPlatform() { + if (!platformUrl) { + return; + } + if (window.ccr?.openExternal) { + void window.ccr.openExternal(platformUrl).catch(() => undefined); + return; + } + window.open(platformUrl, "_blank", "noopener,noreferrer"); + } + + return ( +
+ +
+
{displayName}
+
{baseUrl}
+
+ {platformUrl ? ( + + ) : null} +
+ ); +} + +function providerImportPlatformUrl(provider: ProviderDeepLinkPayload, baseUrl: string, preset: ProviderPreset | undefined): string | undefined { + return normalizedHttpUrl(provider.source) ?? normalizedHttpUrl(preset?.websiteUrl) ?? providerBaseOrigin(baseUrl || provider.baseUrl); +} + +function providerBaseOrigin(value: string): string | undefined { + const url = normalizedHttpUrl(value); + if (!url) { + return undefined; + } + + try { + return new URL(url).origin; + } catch { + return undefined; + } +} + +function normalizedHttpUrl(value: string | undefined): string | undefined { + const trimmed = value?.trim(); + if (!trimmed) { + return undefined; + } + + try { + const url = new URL(providerUrlWithDefaultScheme(trimmed)); + if (!["http:", "https:"].includes(url.protocol)) { + return undefined; + } + return url.toString(); + } catch { + return undefined; + } +} + function providerPresetOptionMatchesQuery( option: ProviderPresetComboboxOption, query: string @@ -828,7 +920,7 @@ function LocalAgentProviderImportPanel({ }; }, [mode, providerPlugins, providers]); - if (mode !== "add" || (!loading && candidates.length === 0 && !error)) { + if (mode !== "add" || (candidates.length === 0 && !error)) { return null; } @@ -984,6 +1076,7 @@ export function AddProviderForm({ error, connectivityLoading = false, connectivityProbe, + importProvider, mode, onCheck, onChange, @@ -997,6 +1090,7 @@ export function AddProviderForm({ connectivityProbe?: GatewayProviderProbeResult; draft: AddProviderDraft; error: string; + importProvider?: ProviderDeepLinkPayload; mode: "add" | "edit"; onCheck?: () => Promise; onChange: (patch: Partial, resetProbe?: boolean) => void; @@ -1014,6 +1108,7 @@ export function AddProviderForm({ const hasModelCatalog = Boolean(probe?.models.length); const selectedPreset = findProviderPreset(draft.presetId); const customEndpoint = draft.presetId === customProviderPresetId; + const importMode = Boolean(importProvider); const showBaseUrl = customEndpoint || mode === "edit"; const detectedProtocol = probe?.detectedProtocol ?? draft.protocol; const detectedBaseUrl = probe?.normalizedBaseUrl || draft.baseUrl; @@ -1083,6 +1178,7 @@ export function AddProviderForm({ ...createDefaultProviderAccountDraft(), baseUrl: "", icon: "", + modelDisplayNames: undefined, modelSearch: "", presetId, providerPlugins: [], @@ -1097,6 +1193,7 @@ export function AddProviderForm({ ...createDefaultProviderAccountDraft(), baseUrl: "", icon: "", + modelDisplayNames: undefined, modelSearch: "", presetId, providerPlugins: [], @@ -1114,6 +1211,7 @@ export function AddProviderForm({ ...accountDraft, baseUrl: endpoint?.baseUrl ?? "", icon: "", + modelDisplayNames: preset?.defaultModelDisplayNames, modelSearch: "", modelsText: draft.modelsText.trim() || preset?.defaultModels?.join("\n") || "", name: mode === "add" && preset && generatedName ? uniqueProviderName(providers, t(preset.name)) : draft.name, @@ -1128,19 +1226,25 @@ export function AddProviderForm({ return ( <>
- - - - + {importProvider ? ( + + ) : ( + <> + + + + + + )} onChange({ name: event.target.value })} /> @@ -1172,7 +1276,7 @@ export function AddProviderForm({ {safetyIssue.message}
) : null} - {selectedPreset && !showBaseUrl ? ( + {selectedPreset && !showBaseUrl && !importMode ? (
@@ -1184,6 +1288,7 @@ export function AddProviderForm({ {hasModelCatalog && probe ? (
onChange({ modelSearch })} onSelectedChange={(selectedModels) => onChange({ selectedModels })} @@ -1197,6 +1302,7 @@ export function AddProviderForm({
onChange({ modelsText: models.join("\n") })} placeholder={t("Model name")} value={splitLines(draft.modelsText)} @@ -1206,6 +1312,7 @@ export function AddProviderForm({ ) : ( onChange({ modelsText: models.join("\n") }, true)} placeholder={t("Model name")} value={splitLines(draft.modelsText)} @@ -1869,6 +1976,7 @@ export function AddProviderDialog({ connectivityProbe, draft, error, + importProvider, mode, onCheck, onChange, @@ -1877,13 +1985,16 @@ export function AddProviderDialog({ probe, probeLoading, providerPlugins = [], - providers + providers, + submitLabel, + title }: { canSubmit: boolean; connectivityLoading?: boolean; connectivityProbe?: GatewayProviderProbeResult; draft: AddProviderDraft; error: string; + importProvider?: ProviderDeepLinkPayload; mode: "add" | "edit"; onCheck?: (models: string[]) => Promise; onChange: (patch: Partial, resetProbe?: boolean) => void; @@ -1893,15 +2004,18 @@ export function AddProviderDialog({ probeLoading: boolean; providerPlugins?: unknown[]; providers: GatewayProviderConfig[]; + submitLabel?: string; + title?: string; }) { const t = useAppText(); const [checkConfirmOpen, setCheckConfirmOpen] = useState(false); const [checkConfirmBusy, setCheckConfirmBusy] = useState(false); const [iconDetecting, setIconDetecting] = useState(false); + const [submitting, setSubmitting] = useState(false); const [checkModelSelection, setCheckModelSelection] = useState([]); const [checkResult, setCheckResult] = useState(); const checkModels = mergeProviderModelLists(draft.selectedModels, splitLines(draft.modelsText)); - const submitLoading = probeLoading || connectivityLoading || iconDetecting; + const submitLoading = probeLoading || connectivityLoading || iconDetecting || submitting; const submitDisabled = !canSubmit || submitLoading; function openCheckConfirm() { @@ -1931,22 +2045,31 @@ export function AddProviderDialog({ setCheckResult(undefined); } - function submit() { + async function submit() { if (submitDisabled) { return; } - void onSubmit(); + setSubmitting(true); + try { + const saved = await onSubmit(); + if (!saved) { + setSubmitting(false); + } + } catch (error) { + setSubmitting(false); + throw error; + } } return ( <> - !open && onClose()}> + !open && !submitting && onClose()}>
- {mode === "edit" ? t("Edit Provider") : t("Add Provider")} + {title ?? (mode === "edit" ? t("Edit Provider") : t("Add Provider"))}
-
@@ -1957,6 +2080,7 @@ export function AddProviderDialog({ connectivityProbe={connectivityProbe} draft={draft} error={error} + importProvider={importProvider} mode={mode} onCheck={onCheck ? async () => openCheckConfirm() : undefined} onChange={onChange} @@ -1969,14 +2093,14 @@ export function AddProviderDialog({ - -
@@ -2153,11 +2277,13 @@ function ProviderConnectivityResultGroup({ function ModelTagInput({ ariaLabel, + displayNames, onChange, placeholder, value }: { ariaLabel: string; + displayNames?: Record; onChange: (value: string[]) => void; placeholder: string; value: string[]; @@ -2197,7 +2323,9 @@ function ModelTagInput({
{models.map((model) => ( - {model} + + {displayNames?.[model] ?? model} + ); diff --git a/src/renderer/pages/home/components/providers.tsx b/src/renderer/pages/home/components/providers.tsx index 24e18d3..153e912 100644 --- a/src/renderer/pages/home/components/providers.tsx +++ b/src/renderer/pages/home/components/providers.tsx @@ -12,7 +12,7 @@ import { providerAccountSnapshotCredentialLabel, providerAccountSnapshotLabel, ProviderAccountTestPath, ProviderAccountTestResult, providerBaseUrl, providerCapabilitiesSummary, ProviderCredentialDraft, ProviderDeepLinkPayload, ProviderDeepLinkRequest, providerDraftSafetyIssue, providerCredentialDraftPatchFromJson, providerHttpJsonConnectorFromDraft, ProviderConnectivityCheckReport, providerDeepLinkDisplayIcon, providerListItemKey, providerMatchesQuery, ProviderPreset, providerPresetIconUrls, providerProbeHasSupportedProtocol, - providerSelectableProtocolsFromProbe, providerUsageFieldPatch, ProviderUsageFieldTarget, providerUsageMethodOptions, Search, SelectControl, + providerModelDisplayName, providerModelDisplayTitle, providerSelectableProtocolsFromProbe, providerUsageFieldPatch, ProviderUsageFieldTarget, providerUsageMethodOptions, Search, SelectControl, resolveProviderDeepLinkPreset, ShieldCheck, splitLines, splitModelTagInput, Switch, Textarea, translatedProviderProtocolLabel, translateOptions, translateProbeProtocolMessage, Trash2, uniqueProviderName, uniqueProviderProtocols, useAppErrorText, useAppText, useEffect, useMemo, useRef, useState, X, isPlainRecord @@ -216,16 +216,17 @@ export function ProvidersView({ accountSnapshots, addProvider, editProvider, not
{provider.models.map((model) => { const modelKey = `${itemKey}:${model}`; + const displayName = providerModelDisplayName(provider, model); return ( ); })} @@ -308,8 +309,8 @@ export function ModelsView({ config }: { config: AppConfig }) { key={row.key} >
-
- {row.model} +
+ {row.displayName ?? row.model}
@@ -521,7 +522,9 @@ export function ProviderDeepLinkDialog({
{modelPreview.map((model) => ( - {model} + + {provider.modelDisplayNames?.[model] ?? model} + ))} {provider.models.length > modelPreview.length ? ( @@ -943,6 +946,7 @@ function LocalAgentProviderImportPanel({ baseUrl: result.provider.baseUrl, credentials: [], icon: result.provider.icon ?? "", + modelDisplayNames: result.provider.modelDisplayNames, modelSearch: "", modelsText: result.provider.models.join("\n"), name: result.provider.name?.trim() || inferProviderNameFromBaseUrl(result.provider.baseUrl), @@ -2321,22 +2325,25 @@ function ModelTagInput({ /> {models.length > 0 ? (
- {models.map((model) => ( - - - {displayNames?.[model] ?? model} - - - - ))} + {models.map((model) => { + const displayName = displayNames?.[model] ?? model; + return ( + + + {displayName} + + + + ); + })}
) : null} @@ -2401,7 +2408,7 @@ function ModelMultiSelect({ checked && "border-primary bg-accent" )} key={model} - title={displayName ? `${displayName} (${model})` : model} + title={displayName ?? model} > toggleModel(model)} /> {displayName ?? model} diff --git a/src/renderer/pages/home/shared/profiles.ts b/src/renderer/pages/home/shared/profiles.ts index cc1055f..297a693 100644 --- a/src/renderer/pages/home/shared/profiles.ts +++ b/src/renderer/pages/home/shared/profiles.ts @@ -415,6 +415,7 @@ export function normalizeProfileClientModel(value: string | undefined): string { } export type ProfileModelProviderOption = { + modelDisplayNames?: Record; models: string[]; name: string; }; @@ -433,6 +434,7 @@ export function profileModelProviderOptions( const providerOptions = providers .filter((provider) => provider.name?.trim() && Array.isArray(provider.models)) .map((provider) => ({ + modelDisplayNames: profileModelDisplayNamesForModels(provider.modelDisplayNames, provider.models), models: uniqueStrings(provider.models.filter(Boolean)), name: provider.name.trim() })) @@ -485,10 +487,11 @@ export function profileModelDisplayValue( } const normalized = normalizeProfileClientModel(value); if (parsedValue.provider && parsedValue.model) { - return `${parsedValue.provider}/${parsedValue.model}`; + const provider = profileModelProviderOptions(providers, virtualModelProfiles).find((item) => item.name === parsedValue.provider); + return `${parsedValue.provider}/${profileModelOptionDisplayName(provider, parsedValue.model)}`; } const provider = profileModelProviderOptions(providers, virtualModelProfiles).find((item) => item.models.includes(normalized)); - return provider ? `${provider.name}/${normalized}` : normalized; + return provider ? `${provider.name}/${profileModelOptionDisplayName(provider, normalized)}` : normalized; } export function profileModelProviderMatchesQuery(provider: ProfileModelProviderOption, query: string): boolean { @@ -498,16 +501,36 @@ export function profileModelProviderMatchesQuery(provider: ProfileModelProviderO } return ( provider.name.toLowerCase().includes(normalizedQuery) || - provider.models.some((model) => model.toLowerCase().includes(normalizedQuery)) + provider.models.some((model) => + model.toLowerCase().includes(normalizedQuery) || + profileModelOptionDisplayName(provider, model).toLowerCase().includes(normalizedQuery) + ) ); } -export function profileModelMatchesQuery(providerName: string, model: string, query: string): boolean { +export function profileModelMatchesQuery(providerName: string, model: string, query: string, displayName?: string): boolean { const normalizedQuery = query.trim().toLowerCase(); if (!normalizedQuery) { return true; } - return providerName.toLowerCase().includes(normalizedQuery) || model.toLowerCase().includes(normalizedQuery); + return providerName.toLowerCase().includes(normalizedQuery) || + model.toLowerCase().includes(normalizedQuery) || + (displayName ?? "").toLowerCase().includes(normalizedQuery); +} + +export function profileModelOptionDisplayName(provider: ProfileModelProviderOption | undefined, model: string): string { + return provider?.modelDisplayNames?.[model]?.trim() || model; +} + +function profileModelDisplayNamesForModels( + value: Record | undefined, + models: string[] +): Record | undefined { + const modelIds = new Set(models); + const entries = Object.entries(value ?? {}) + .map(([rawModel, rawDisplayName]) => [rawModel.trim(), rawDisplayName.trim()] as const) + .filter(([model, displayName]) => model && displayName && model !== displayName && modelIds.has(model)); + return entries.length > 0 ? Object.fromEntries(entries) : undefined; } export type BotGatewayAuthInputType = "text" | "password"; diff --git a/src/renderer/pages/home/shared/providers.ts b/src/renderer/pages/home/shared/providers.ts index 97bbb77..b58d908 100644 --- a/src/renderer/pages/home/shared/providers.ts +++ b/src/renderer/pages/home/shared/providers.ts @@ -390,12 +390,23 @@ export const localAgentProviderIconUrls: Record }; export function createModelCatalogItems(config: AppConfig): ModelCatalogItem[] { - const providerModels = config.Providers.flatMap((provider) => mergeProviderModelLists(provider.models)); + const providerModels: string[] = []; + const displayNames: Record = {}; + for (const provider of config.Providers) { + for (const model of mergeProviderModelLists(provider.models)) { + providerModels.push(model); + const displayName = provider.modelDisplayNames?.[model]?.trim(); + if (displayName && !displayNames[model]) { + displayNames[model] = displayName; + } + } + } const virtualModels = (config.virtualModelProfiles ?? []) .filter(virtualModelIsCatalogVisible) .flatMap(virtualModelCatalogNames); return uniqueStrings([...providerModels, ...virtualModels]).map((model, index) => ({ + ...(displayNames[model] ? { displayName: displayNames[model] } : {}), key: `model:${index}:${model}`, model })); @@ -451,7 +462,7 @@ export function modelCatalogItemMatchesQuery(row: ModelCatalogItem, query: strin return true; } - return row.model.toLowerCase().includes(query); + return row.model.toLowerCase().includes(query) || (row.displayName ?? "").toLowerCase().includes(query); } export function createRouteModelOptions(providers: GatewayProviderConfig[]): Array<{ label: string; value: string }> { @@ -462,7 +473,7 @@ export function createRouteModelOptions(providers: GatewayProviderConfig[]): Arr return provider.models .filter(Boolean) .map((model) => ({ - label: `${provider.name}, ${model}`, + label: `${provider.name}, ${providerModelDisplayName(provider, model)}`, value: `${provider.name},${model}` })); }); @@ -867,7 +878,10 @@ export function createProviderDraftFromDeepLinkPayload( baseUrl, credentials: [], icon: payload.icon?.trim() || "", - modelDisplayNames: providerPresetModelDisplayNames(preset), + modelDisplayNames: modelDisplayNamesForModels( + mergeModelDisplayNames(providerPresetModelDisplayNames(preset), payload.modelDisplayNames), + models + ), modelSearch: "", modelsText: models.join("\n"), name: uniqueProviderName(providers, baseName), @@ -926,6 +940,7 @@ export function createProviderConfigFromDeepLink( api_key: apiKey, capabilities: capabilities.length > 0 ? capabilities : undefined, icon: payload.icon?.trim() || undefined, + modelDisplayNames: modelDisplayNamesForModels(mergeModelDisplayNames(payload.modelDisplayNames, probe?.modelDisplayNames), models), models, name, type: protocol @@ -973,7 +988,10 @@ export function createProviderDraftFromProvider(provider: GatewayProviderConfig) baseUrl, credentials: (provider.credentials ?? []).map(providerCredentialDraftFromConfig), icon: provider.icon ?? "", - modelDisplayNames: providerPresetModelDisplayNames(preset), + modelDisplayNames: modelDisplayNamesForModels( + mergeModelDisplayNames(providerPresetModelDisplayNames(preset), provider.modelDisplayNames), + provider.models + ), modelSearch: "", modelsText: provider.models.join("\n"), name: provider.name, @@ -1519,10 +1537,12 @@ export function createProviderInstallLinkFromDraft(draft: AddProviderDraft, prob return accountKeySafetyIssue.message; } + const modelDisplayNames = modelDisplayNamesForModels(draft.modelDisplayNames, models); const payload: ProviderDeepLinkPayload = { ...(account ? { account } : {}), baseUrl, ...(draft.icon.trim() ? { icon: draft.icon.trim() } : {}), + ...(modelDisplayNames ? { modelDisplayNames } : {}), models, name: providerName, protocol @@ -1870,6 +1890,25 @@ export function mergeModelDisplayNames( return Object.keys(merged).length > 0 ? merged : undefined; } +export function modelDisplayNamesForModels( + value: Record | undefined, + models: string[] +): Record | undefined { + const modelIds = new Set(models); + const entries = Object.entries(value ?? {}) + .map(([rawModel, rawDisplayName]) => [rawModel.trim(), rawDisplayName.trim()] as const) + .filter(([model, displayName]) => model && displayName && model !== displayName && modelIds.has(model)); + return entries.length > 0 ? Object.fromEntries(entries) : undefined; +} + +export function providerModelDisplayName(provider: GatewayProviderConfig, model: string): string { + return provider.modelDisplayNames?.[model]?.trim() || model; +} + +export function providerModelDisplayTitle(provider: GatewayProviderConfig, model: string): string { + return providerModelDisplayName(provider, model); +} + export function pickRecommendedProviderModels(models: string[], protocol?: GatewayProviderProtocol): string[] { const candidates = mergeProviderModelLists(models); if (candidates.length === 0) { @@ -1965,7 +2004,8 @@ export function providerMatchesQuery(provider: GatewayProviderConfig, query: str providerBaseUrl(provider), providerCapabilitiesSummary(provider), ...(provider.capabilities ?? []).map((capability) => capability.baseUrl), - ...provider.models + ...provider.models, + ...provider.models.map((model) => providerModelDisplayName(provider, model)) ] .filter(Boolean) .some((value) => value.toLowerCase().includes(query)); diff --git a/src/renderer/pages/home/shared/types.ts b/src/renderer/pages/home/shared/types.ts index e49271a..8e1d68e 100644 --- a/src/renderer/pages/home/shared/types.ts +++ b/src/renderer/pages/home/shared/types.ts @@ -694,6 +694,7 @@ export type ExtensionListItem = { }; export type ModelCatalogItem = { + displayName?: string; key: string; model: string; }; diff --git a/src/shared/app.ts b/src/shared/app.ts index 632823d..5d2ea3a 100644 --- a/src/shared/app.ts +++ b/src/shared/app.ts @@ -78,6 +78,7 @@ export type GatewayProviderConfig = { extraHeaders?: unknown; icon?: string; id?: string; + modelDisplayNames?: Record; models: string[]; name: string; provider?: string; @@ -221,6 +222,7 @@ export type ProviderDeepLinkPayload = { apiKey?: string; baseUrl: string; icon?: string; + modelDisplayNames?: Record; models: string[]; name?: string; protocol?: GatewayProviderProtocol; @@ -250,6 +252,7 @@ export type LocalAgentProviderCandidate = { id: string; importable: boolean; kind: LocalAgentProviderKind; + modelDisplayNames?: Record; models: string[]; name: string; protocol: GatewayProviderProtocol; diff --git a/src/shared/deep-link.ts b/src/shared/deep-link.ts index df2cbef..36fba47 100644 --- a/src/shared/deep-link.ts +++ b/src/shared/deep-link.ts @@ -147,6 +147,7 @@ export function parseProviderDeepLinkPayload(rawUrl: string): ProviderDeepLinkPa firstStringParam(params, ["protocol"]) ?? firstPayloadString(payload, ["protocol"]) ); const models = readDeepLinkModels(params, payload); + const modelDisplayNames = readDeepLinkModelDisplayNames(params, payload, models); const account = readDeepLinkAccount(params, payload); const source = boundedString( firstStringParam(params, ["source"]) ?? @@ -159,6 +160,7 @@ export function parseProviderDeepLinkPayload(rawUrl: string): ProviderDeepLinkPa ...(apiKey ? { apiKey } : {}), baseUrl, ...(icon ? { icon } : {}), + ...(modelDisplayNames ? { modelDisplayNames } : {}), models, ...(name ? { name } : {}), ...(protocol ? { protocol } : {}), @@ -216,6 +218,7 @@ function parseProviderPayloadFields( firstStringParam(params, ["protocol"]) ?? firstPayloadString(payload, ["protocol"]) ); const models = readDeepLinkModels(params, payload); + const modelDisplayNames = readDeepLinkModelDisplayNames(params, payload, models); const account = readDeepLinkAccount(params, payload); const source = boundedString( firstStringParam(params, ["source"]) ?? @@ -230,6 +233,7 @@ function parseProviderPayloadFields( ...(apiKey ? { apiKey } : {}), baseUrl, ...(icon ? { icon } : {}), + ...(modelDisplayNames ? { modelDisplayNames } : {}), models, ...(name ? { name } : {}), ...(protocol ? { protocol } : {}), @@ -515,11 +519,61 @@ function payloadModels(payload: Record | undefined): string[] { } const value = payload.models; if (Array.isArray(value)) { - return value.filter((item): item is string => typeof item === "string"); + return value + .map((item) => typeof item === "string" ? item : readPayloadModelId(item)) + .filter((item): item is string => Boolean(item)); } return typeof value === "string" ? [value] : []; } +function readDeepLinkModelDisplayNames( + params: URLSearchParams, + payload: Record | undefined, + models: string[] +): Record | undefined { + const modelIds = new Set(models); + const displayNames: Record = {}; + const addDisplayName = (rawModel: unknown, rawDisplayName: unknown) => { + const model = typeof rawModel === "string" ? rawModel.trim() : ""; + const displayName = typeof rawDisplayName === "string" ? rawDisplayName.trim() : ""; + if (!model || !displayName || model === displayName || !modelIds.has(model)) { + return; + } + if (displayName.length > maxModelLength) { + throw new Error("Model display name is too long."); + } + displayNames[model] = displayName; + }; + + const explicit = parseJsonValueParam(params, payload, ["modelDisplayNames", "model_display_names"]); + if (isRecord(explicit)) { + for (const [model, displayName] of Object.entries(explicit)) { + addDisplayName(model, displayName); + } + } + + const payloadModelList = Array.isArray(payload?.models) ? payload.models : []; + for (const item of payloadModelList) { + if (!isRecord(item)) { + continue; + } + addDisplayName(readPayloadModelId(item), readPayloadModelDisplayName(item)); + } + + return Object.keys(displayNames).length > 0 ? displayNames : undefined; +} + +function readPayloadModelId(value: unknown): string | undefined { + if (!isRecord(value)) { + return undefined; + } + return firstPayloadString(value, ["id", "slug", "model", "name"]); +} + +function readPayloadModelDisplayName(value: Record): string | undefined { + return firstPayloadString(value, ["display_name", "displayName", "label", "name"]); +} + function splitModelValue(value: string): string[] { return value .split(/[\n,]+/) From 589f29931991a87f288a0756c84facafdcbaf233 Mon Sep 17 00:00:00 2001 From: musi Date: Mon, 29 Jun 2026 22:49:39 +0800 Subject: [PATCH 09/94] Refine provider docs and gateway API key handling --- .../en/configuration/provider-deeplink.md | 12 +- .../zh/configuration/provider-deeplink.md | 12 +- docs/src/layouts/DocsLayout.astro | 4 +- docs/src/styles/global.css | 6 + src/main/config.ts | 41 +++-- src/main/plugins/service.ts | 45 ++++- src/main/presets/anthropic/index.ts | 3 +- src/main/presets/bailian/index.ts | 3 +- src/main/presets/deepseek/index.ts | 3 +- src/main/presets/gemini/index.ts | 3 +- src/main/presets/index.ts | 5 +- src/main/presets/kimi-coding/index.ts | 8 +- src/main/presets/mistral/index.ts | 3 +- src/main/presets/moonshot/index.ts | 72 ++++++-- src/main/presets/openai/index.ts | 3 +- src/main/presets/openrouter/index.ts | 3 +- src/main/presets/siliconflow/index.ts | 3 +- src/main/presets/zai-global-coding/index.ts | 3 +- src/main/presets/zai-global-general/index.ts | 3 +- src/main/presets/zhipu-cn-coding/index.ts | 3 +- src/main/presets/zhipu-cn-general/index.ts | 3 +- src/main/provider-model-catalog.ts | 1 + src/main/web-client-bridge.ts | 47 +++++- src/main/web-management-server.ts | 155 +++++++++++++++++- src/renderer/pages/home/App.tsx | 26 ++- .../pages/home/components/dashboard.tsx | 123 ++++++++++++-- .../pages/home/components/providers.tsx | 151 +++++++++++++++-- src/renderer/pages/home/shared/i18n.tsx | 10 +- src/renderer/pages/home/shared/options.ts | 1 + src/renderer/pages/home/shared/providers.ts | 5 +- .../gateway/claude-code-router-plugin.ts | 64 +++++++- src/server/gateway/service.ts | 5 +- src/server/proxy/certificates.ts | 30 +++- src/shared/provider-presets.ts | 1 + 34 files changed, 750 insertions(+), 110 deletions(-) diff --git a/docs/src/content/docs/en/configuration/provider-deeplink.md b/docs/src/content/docs/en/configuration/provider-deeplink.md index f4a3818..b1b9a43 100644 --- a/docs/src/content/docs/en/configuration/provider-deeplink.md +++ b/docs/src/content/docs/en/configuration/provider-deeplink.md @@ -2,12 +2,12 @@ title: One click import pageTitle: One click import eyebrow: Detailed Configuration -lead: Use ccr://provider links to hand provider configuration to CCR, or open the import confirmation page with preset provider buttons. +lead: Quickly add common model providers, review the details, and save them without filling everything in by hand. --- ## One-Click Import -The buttons below open the CCR desktop app's provider import confirmation dialog. Preset buttons do not include API keys; custom provider links may include one. Always confirm the provider name, Base URL, protocol, and models before importing a key. +Choose a provider below to get started. CCR shows what will be added before saving it; when using a custom entry point, make sure the source is one you trust.
@@ -50,9 +50,13 @@ The buttons below open the CCR desktop app's provider import confirmation dialog MistralChat Completions - + - Moonshot KimiChat Completions + Kimi API (China)China platform + + + + Kimi API (Global)Global platform diff --git a/docs/src/content/docs/zh/configuration/provider-deeplink.md b/docs/src/content/docs/zh/configuration/provider-deeplink.md index d51e045..bdbb48f 100644 --- a/docs/src/content/docs/zh/configuration/provider-deeplink.md +++ b/docs/src/content/docs/zh/configuration/provider-deeplink.md @@ -2,12 +2,12 @@ title: 一键导入供应商 pageTitle: 一键导入供应商 eyebrow: 详细配置 -lead: 使用 ccr://provider 链接把供应商配置带入 CCR,并通过现有 preset 供应商按钮快速打开导入确认页。 +lead: 快速添加常见模型供应商,确认无误后即可保存,减少手动配置的繁琐步骤。 --- ## 一键导入 -下面的按钮会打开 CCR 桌面 App 的供应商导入确认页。预设按钮不会携带 API Key;自定义供应商链接可以携带 Key。导入前始终确认供应商名称、Base URL、协议和模型。 +选择下面的供应商即可开始添加。CCR 会先显示即将添加的内容,确认无误后再保存;使用自定义入口时,请确保来源可信。
@@ -50,9 +50,13 @@ lead: 使用 ccr://provider 链接把供应商配置带入 CCR,并通过现有 Mistral 官方Chat Completions - + - 月之暗面 KimiChat Completions + Kimi API(国内)国内平台 + + + + Kimi API(海外)海外平台 diff --git a/docs/src/layouts/DocsLayout.astro b/docs/src/layouts/DocsLayout.astro index ea5dde8..c803dc3 100644 --- a/docs/src/layouts/DocsLayout.astro +++ b/docs/src/layouts/DocsLayout.astro @@ -80,7 +80,7 @@ const resolvedNavItems = navItems.map((item, index) => { }; }); const homeHref = withBase(locale === "en" ? "/en/" : "/"); -const faviconHref = withBase("/favicon.svg"); +const faviconHref = withBase("/ccr-icon.png"); const logoSrc = withBase("/logo.png"); const sidebarIcons = { rocket: Rocket, @@ -177,7 +177,7 @@ const sidebarCloseLabel = locale === "zh" ? "关闭目录" : "Close navigation"; } })(); - + { resolvedLanguageOptions.map((option) => ( { const gatewayConfig = picked.gateway ?? {}; const corePort = gatewayConfig.corePort ?? nextPort(port); const configFileApiKeys = normalizeApiKeys(picked.APIKEYS, picked.APIKEY).filter((apiKey) => !isDefaultSeedApiKey(apiKey)); - const persistedApiKeys = await loadPersistedApiKeys(); - const apiKeys = uniqueApiKeyConfigs([...persistedApiKeys, ...configFileApiKeys]); + const persistedApiKeys = (await loadPersistedApiKeys()).filter((apiKey) => !isDefaultSeedApiKey(apiKey)); + const loadedApiKeys = uniqueApiKeyConfigs([...persistedApiKeys, ...configFileApiKeys]); + const apiKeys = ensureGatewayApiKeys(loadedApiKeys); const config: AppConfig = withSingleEnabledGlobalProfiles({ ...DEFAULT_CONFIG, ...picked, @@ -413,11 +415,11 @@ export async function loadAppConfig(): Promise { }, routerEndpoint: endpoint }); - const shouldMigrateApiKeys = hasConfigFileApiKeys(rawValue) || configFileApiKeys.length > 0; - if (shouldMigrateApiKeys) { + const shouldPersistApiKeys = loadedApiKeys.length === 0 || hasConfigFileApiKeys(rawValue) || configFileApiKeys.length > 0; + if (shouldPersistApiKeys) { await replacePersistedApiKeys(apiKeys); } - if (loadedRawConfig.source !== "sqlite" || shouldMigrateApiKeys) { + if (loadedRawConfig.source !== "sqlite" || shouldPersistApiKeys) { await writeSanitizedConfig(config); } return config; @@ -427,10 +429,16 @@ export async function loadAppConfig(): Promise { console.warn(`[config] Failed to load API keys: ${formatError(storeError)}`); return [] as ApiKeyConfig[]; }); + const apiKeys = ensureGatewayApiKeys(persistedApiKeys.filter((apiKey) => !isDefaultSeedApiKey(apiKey))); + if (persistedApiKeys.length === 0) { + await replacePersistedApiKeys(apiKeys).catch((storeError) => { + console.warn(`[config] Failed to persist generated API key: ${formatError(storeError)}`); + }); + } return { ...DEFAULT_CONFIG, - APIKEY: persistedApiKeys[0]?.key ?? "", - APIKEYS: persistedApiKeys + APIKEY: apiKeys[0]?.key ?? "", + APIKEYS: apiKeys }; } } @@ -438,7 +446,7 @@ export async function loadAppConfig(): Promise { export async function saveAppConfig(config: AppConfig): Promise { const normalizedConfig = withSingleEnabledGlobalProfiles(config); assertProviderApiKeysAreSafe(normalizedConfig); - const apiKeys = normalizeApiKeys(normalizedConfig.APIKEYS, normalizedConfig.APIKEY).filter((apiKey) => !isDefaultSeedApiKey(apiKey)); + const apiKeys = ensureGatewayApiKeys(normalizeApiKeys(normalizedConfig.APIKEYS, normalizedConfig.APIKEY).filter((apiKey) => !isDefaultSeedApiKey(apiKey))); await replacePersistedApiKeys(apiKeys); await writeSanitizedConfig({ ...normalizedConfig, @@ -564,7 +572,7 @@ function providerCredentialApiKey(credential: ProviderCredentialConfig): string } export async function saveApiKeysConfig(apiKeys: ApiKeyConfig[]): Promise { - const normalized = normalizeApiKeys(apiKeys, undefined).filter((apiKey) => !isDefaultSeedApiKey(apiKey)); + const normalized = ensureGatewayApiKeys(normalizeApiKeys(apiKeys, undefined).filter((apiKey) => !isDefaultSeedApiKey(apiKey))); await replacePersistedApiKeys(normalized); return loadAppConfig(); } @@ -2516,6 +2524,19 @@ function normalizeApiKeys(value: ApiKeyConfig[] | undefined, legacyKey: string | return uniqueApiKeyConfigs([...(value ?? []), ...(legacyKey ? [createApiKeyConfig(legacyKey, value?.length ?? 0)] : [])]); } +function ensureGatewayApiKeys(apiKeys: ApiKeyConfig[]): ApiKeyConfig[] { + return apiKeys.length ? apiKeys : [createGeneratedGatewayApiKey()]; +} + +function createGeneratedGatewayApiKey(): ApiKeyConfig { + return { + createdAt: new Date().toISOString(), + id: GENERATED_GATEWAY_API_KEY_ID, + key: `sk-ccr-${randomBytes(32).toString("base64url")}`, + name: "Local Gateway" + }; +} + function createApiKeyConfig(key: string, index: number): ApiKeyConfig { return { createdAt: new Date(0).toISOString(), diff --git a/src/main/plugins/service.ts b/src/main/plugins/service.ts index 16da667..83dfcdc 100644 --- a/src/main/plugins/service.ts +++ b/src/main/plugins/service.ts @@ -523,14 +523,49 @@ async function loadPluginModule(modulePath: string): Promise { } function resolvePluginModule(modulePath: string): string { - const expanded = expandHome(modulePath); + const resolved = requireFromHere.resolve(resolveLocalModulePath(modulePath, "Plugin module")); + assertJavaScriptModulePath(resolved, "Plugin module"); + return resolved; +} + +function resolveLocalModulePath(value: string, label: string): string { + const trimmed = value.trim(); + if (!trimmed) { + throw new Error(`${label} path is required.`); + } + + const expanded = expandHome(trimmed); if (path.isAbsolute(expanded)) { - return requireFromHere.resolve(expanded); + return expanded; } - if (expanded.startsWith(".")) { - return requireFromHere.resolve(path.resolve(CONFIGDIR, expanded)); + if (isProtocolSpecifier(expanded)) { + throw new Error(`${label} must be a local JavaScript file path, not a URL or protocol specifier.`); } - return requireFromHere.resolve(expanded, { paths: [CONFIGDIR, process.cwd()] }); + if (!expanded.startsWith(".")) { + throw new Error(`${label} must be an explicit local JavaScript path. Package specifiers are not loaded from configuration.`); + } + + const resolved = path.resolve(CONFIGDIR, expanded); + if (!isPathInside(resolved, CONFIGDIR)) { + throw new Error(`${label} relative paths must stay inside the CCR config directory.`); + } + return resolved; +} + +function assertJavaScriptModulePath(resolved: string, label: string): void { + const extension = path.extname(resolved).toLowerCase(); + if (![".cjs", ".js", ".mjs"].includes(extension)) { + throw new Error(`${label} must resolve to a JavaScript module file.`); + } +} + +function isProtocolSpecifier(value: string): boolean { + return /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value); +} + +function isPathInside(file: string, root: string): boolean { + const relative = path.relative(root, file); + return relative === "" || (Boolean(relative) && !relative.startsWith("..") && !path.isAbsolute(relative)); } function normalizeLoadedPlugin(moduleValue: unknown): LoadedPlugin { diff --git a/src/main/presets/anthropic/index.ts b/src/main/presets/anthropic/index.ts index 1408c31..7cdc93b 100644 --- a/src/main/presets/anthropic/index.ts +++ b/src/main/presets/anthropic/index.ts @@ -14,5 +14,6 @@ export const anthropicProviderPreset: ProviderPreset = { name: "Anthropic", officialApiKeyPatterns: [ { flags: "i", source: "^sk-ant-[a-z0-9_-]+$" } - ] + ], + websiteUrl: "https://www.anthropic.com/" }; diff --git a/src/main/presets/bailian/index.ts b/src/main/presets/bailian/index.ts index f9c596e..f52da99 100644 --- a/src/main/presets/bailian/index.ts +++ b/src/main/presets/bailian/index.ts @@ -10,5 +10,6 @@ export const bailianProviderPreset: ProviderPreset = { } ], id: "bailian", - name: "Alibaba Bailian" + name: "Alibaba Bailian", + websiteUrl: "https://bailian.console.aliyun.com/" }; diff --git a/src/main/presets/deepseek/index.ts b/src/main/presets/deepseek/index.ts index dfbc177..ee87dc6 100644 --- a/src/main/presets/deepseek/index.ts +++ b/src/main/presets/deepseek/index.ts @@ -47,5 +47,6 @@ export const deepSeekProviderPreset: ProviderPreset = { } ], id: "deepseek", - name: "DeepSeek" + name: "DeepSeek", + websiteUrl: "https://www.deepseek.com/" }; diff --git a/src/main/presets/gemini/index.ts b/src/main/presets/gemini/index.ts index 808a043..432d4ff 100644 --- a/src/main/presets/gemini/index.ts +++ b/src/main/presets/gemini/index.ts @@ -13,5 +13,6 @@ export const geminiProviderPreset: ProviderPreset = { name: "Google Gemini", officialApiKeyPatterns: [ { flags: "i", source: "^AIza[a-z0-9_-]{20,}$" } - ] + ], + websiteUrl: "https://gemini.google.com/" }; diff --git a/src/main/presets/index.ts b/src/main/presets/index.ts index 2c48c59..62cec8e 100644 --- a/src/main/presets/index.ts +++ b/src/main/presets/index.ts @@ -4,7 +4,7 @@ import { deepSeekProviderPreset } from "./deepseek"; import { geminiProviderPreset } from "./gemini"; import { kimiCodingProviderPreset } from "./kimi-coding"; import { mistralProviderPreset } from "./mistral"; -import { moonshotProviderPreset } from "./moonshot"; +import { moonshotChinaProviderPreset, moonshotGlobalProviderPreset } from "./moonshot"; import { openaiProviderPreset } from "./openai"; import { openRouterProviderPreset } from "./openrouter"; import { siliconFlowProviderPreset } from "./siliconflow"; @@ -35,7 +35,8 @@ export const providerPresets: ProviderPreset[] = [ zaiGlobalCodingProviderPreset, zaiGlobalGeneralProviderPreset, mistralProviderPreset, - moonshotProviderPreset, + moonshotChinaProviderPreset, + moonshotGlobalProviderPreset, bailianProviderPreset, siliconFlowProviderPreset ]; diff --git a/src/main/presets/kimi-coding/index.ts b/src/main/presets/kimi-coding/index.ts index 9ff6dd8..6848c28 100644 --- a/src/main/presets/kimi-coding/index.ts +++ b/src/main/presets/kimi-coding/index.ts @@ -26,14 +26,16 @@ export const kimiCodingProviderPreset: ProviderPreset = { endpoints: [ { baseUrl: "https://api.kimi.com/coding/v1", - protocols: ["openai_chat_completions"] + protocols: ["openai_chat_completions"], + websiteUrl: "https://www.kimi.com/code?aff=ccr" }, { baseUrl: "https://api.kimi.com/coding/", - protocols: ["anthropic_messages"] + protocols: ["anthropic_messages"], + websiteUrl: "https://www.kimi.com/code?aff=ccr" } ], id: "kimi-coding", name: "Kimi Code - Coding Plan", - websiteUrl: "https://www.kimi.com/code/docs/" + websiteUrl: "https://www.kimi.com/code?aff=ccr" }; diff --git a/src/main/presets/mistral/index.ts b/src/main/presets/mistral/index.ts index 10ec1fa..ff351f4 100644 --- a/src/main/presets/mistral/index.ts +++ b/src/main/presets/mistral/index.ts @@ -41,5 +41,6 @@ export const mistralProviderPreset: ProviderPreset = { } ], id: "mistral", - name: "Mistral" + name: "Mistral", + websiteUrl: "https://mistral.ai/" }; diff --git a/src/main/presets/moonshot/index.ts b/src/main/presets/moonshot/index.ts index c7c9125..dd2c72f 100644 --- a/src/main/presets/moonshot/index.ts +++ b/src/main/presets/moonshot/index.ts @@ -1,7 +1,7 @@ import type { ProviderAccountConfig } from "../../../shared/app"; import type { ProviderPreset } from "../../../shared/provider-presets"; -const moonshotProviderAccountConfig: ProviderAccountConfig = { +const moonshotGlobalProviderAccountConfig: ProviderAccountConfig = { connectors: [ { auth: "provider-api-key", @@ -37,20 +37,70 @@ const moonshotProviderAccountConfig: ProviderAccountConfig = { enabled: true }; -export const moonshotProviderPreset: ProviderPreset = { - account: moonshotProviderAccountConfig, - aliases: ["kimi", "moonshot"], +const moonshotChinaProviderAccountConfig: ProviderAccountConfig = { + connectors: [ + { + auth: "provider-api-key", + endpoint: "https://api.moonshot.cn/v1/users/me/balance", + mapping: { + meters: [ + { + id: "balance", + kind: "balance", + label: "Balance", + remaining: "$.data.available_balance", + unit: "CNY" + }, + { + id: "voucher_balance", + kind: "balance", + label: "Voucher balance", + remaining: "$.data.voucher_balance", + unit: "CNY" + }, + { + id: "cash_balance", + kind: "balance", + label: "Cash balance", + remaining: "$.data.cash_balance", + unit: "CNY" + } + ] + }, + type: "http-json" + } + ], + enabled: true +}; + +export const moonshotChinaProviderPreset: ProviderPreset = { + account: moonshotChinaProviderAccountConfig, + aliases: ["kimi", "kimi api", "moonshot", "moonshot kimi"], + defaultModels: ["moonshot-v1-8k"], + endpoints: [ + { + baseUrl: "https://api.moonshot.cn/v1", + protocols: ["openai_chat_completions"], + websiteUrl: "https://platform.kimi.com/?aff=ccr" + } + ], + id: "moonshot", + name: "Kimi API (China)", + websiteUrl: "https://platform.kimi.com/?aff=ccr" +}; + +export const moonshotGlobalProviderPreset: ProviderPreset = { + account: moonshotGlobalProviderAccountConfig, + aliases: ["kimi", "kimi api", "moonshot", "moonshot kimi"], defaultModels: ["moonshot-v1-8k"], endpoints: [ { baseUrl: "https://api.moonshot.ai/v1", - protocols: ["openai_chat_completions"] - }, - { - baseUrl: "https://api.moonshot.cn/v1", - protocols: ["openai_chat_completions"] + protocols: ["openai_chat_completions"], + websiteUrl: "https://platform.kimi.ai/?aff=ccr" } ], - id: "moonshot", - name: "Moonshot Kimi" + id: "moonshot-global", + name: "Kimi API (Global)", + websiteUrl: "https://platform.kimi.ai/?aff=ccr" }; diff --git a/src/main/presets/openai/index.ts b/src/main/presets/openai/index.ts index cec8000..e2d5cf9 100644 --- a/src/main/presets/openai/index.ts +++ b/src/main/presets/openai/index.ts @@ -14,5 +14,6 @@ export const openaiProviderPreset: ProviderPreset = { name: "OpenAI", officialApiKeyPatterns: [ { flags: "i", source: "^sk-(?:proj|svcacct)-[a-z0-9_-]+$" } - ] + ], + websiteUrl: "https://openai.com/" }; diff --git a/src/main/presets/openrouter/index.ts b/src/main/presets/openrouter/index.ts index 1294dae..86d56da 100644 --- a/src/main/presets/openrouter/index.ts +++ b/src/main/presets/openrouter/index.ts @@ -51,5 +51,6 @@ export const openRouterProviderPreset: ProviderPreset = { name: "OpenRouter", officialApiKeyPatterns: [ { flags: "i", source: "^sk-or-v1-[a-z0-9_-]+$" } - ] + ], + websiteUrl: "https://openrouter.ai/" }; diff --git a/src/main/presets/siliconflow/index.ts b/src/main/presets/siliconflow/index.ts index 8436699..737c338 100644 --- a/src/main/presets/siliconflow/index.ts +++ b/src/main/presets/siliconflow/index.ts @@ -47,5 +47,6 @@ export const siliconFlowProviderPreset: ProviderPreset = { } ], id: "siliconflow", - name: "SiliconFlow" + name: "SiliconFlow", + websiteUrl: "https://siliconflow.cn/" }; diff --git a/src/main/presets/zai-global-coding/index.ts b/src/main/presets/zai-global-coding/index.ts index 8e0e41e..d757fcc 100644 --- a/src/main/presets/zai-global-coding/index.ts +++ b/src/main/presets/zai-global-coding/index.ts @@ -58,5 +58,6 @@ export const zaiGlobalCodingProviderPreset: ProviderPreset = { } ], id: "zai-global-coding", - name: "Z.ai (Global) - Coding Plan" + name: "Z.ai (Global) - Coding Plan", + websiteUrl: "https://z.ai/" }; diff --git a/src/main/presets/zai-global-general/index.ts b/src/main/presets/zai-global-general/index.ts index b0d63c5..44612fd 100644 --- a/src/main/presets/zai-global-general/index.ts +++ b/src/main/presets/zai-global-general/index.ts @@ -54,5 +54,6 @@ export const zaiGlobalGeneralProviderPreset: ProviderPreset = { } ], id: "zai-global-general", - name: "Z.ai (Global) - General Endpoint" + name: "Z.ai (Global) - General Endpoint", + websiteUrl: "https://z.ai/" }; diff --git a/src/main/presets/zhipu-cn-coding/index.ts b/src/main/presets/zhipu-cn-coding/index.ts index 17456d3..a28dda1 100644 --- a/src/main/presets/zhipu-cn-coding/index.ts +++ b/src/main/presets/zhipu-cn-coding/index.ts @@ -58,5 +58,6 @@ export const zhipuCnCodingProviderPreset: ProviderPreset = { } ], id: "zhipu-cn-coding", - name: "Zhipu AI (China) - Coding Plan" + name: "Zhipu AI (China) - Coding Plan", + websiteUrl: "https://www.bigmodel.cn/" }; diff --git a/src/main/presets/zhipu-cn-general/index.ts b/src/main/presets/zhipu-cn-general/index.ts index c178ba5..c0c0e52 100644 --- a/src/main/presets/zhipu-cn-general/index.ts +++ b/src/main/presets/zhipu-cn-general/index.ts @@ -54,5 +54,6 @@ export const zhipuCnGeneralProviderPreset: ProviderPreset = { } ], id: "zhipu-cn-general", - name: "Zhipu AI (China) - General Endpoint" + name: "Zhipu AI (China) - General Endpoint", + websiteUrl: "https://www.bigmodel.cn/" }; diff --git a/src/main/provider-model-catalog.ts b/src/main/provider-model-catalog.ts index e9e465c..70e9b48 100644 --- a/src/main/provider-model-catalog.ts +++ b/src/main/provider-model-catalog.ts @@ -38,6 +38,7 @@ const presetCatalogProviderIds: Record = { "kimi-coding": ["kimi-for-coding"], mistral: ["mistral"], moonshot: ["moonshotai-cn"], + "moonshot-global": ["moonshotai"], openai: ["openai"], openrouter: ["openrouter"], siliconflow: ["siliconflow-cn"], diff --git a/src/main/web-client-bridge.ts b/src/main/web-client-bridge.ts index f720d17..a9096f2 100644 --- a/src/main/web-client-bridge.ts +++ b/src/main/web-client-bridge.ts @@ -1,4 +1,8 @@ const rpcEndpoint = "/api/ccr/rpc"; +const webAuthHeader = "x-ccr-web-auth"; +const webAuthQueryParam = "ccr_web_token"; +const webAuthStorageKey = "ccr.webAuthToken"; +const webAuthToken = readWebAuthToken(); type RpcResponse = | { ok: true; value: unknown } @@ -8,7 +12,8 @@ async function rpc(method: string, args: unknown[] = []): Promise { const response = await fetch(rpcEndpoint, { body: JSON.stringify({ args, method }), headers: { - "content-type": "application/json" + "content-type": "application/json", + ...(webAuthToken ? { [webAuthHeader]: webAuthToken } : {}) }, method: "POST" }); @@ -27,6 +32,46 @@ async function rpc(method: string, args: unknown[] = []): Promise { return payload.value; } +function readWebAuthToken(): string { + const tokenFromUrl = readWebAuthTokenFromUrl(); + if (tokenFromUrl) { + writeStoredWebAuthToken(tokenFromUrl); + return tokenFromUrl; + } + return readStoredWebAuthToken(); +} + +function readWebAuthTokenFromUrl(): string { + try { + const url = new URL(window.location.href); + const token = url.searchParams.get(webAuthQueryParam)?.trim() ?? ""; + if (!token) { + return ""; + } + url.searchParams.delete(webAuthQueryParam); + window.history.replaceState(window.history.state, "", `${url.pathname}${url.search}${url.hash}`); + return token; + } catch { + return ""; + } +} + +function readStoredWebAuthToken(): string { + try { + return window.sessionStorage.getItem(webAuthStorageKey)?.trim() ?? ""; + } catch { + return ""; + } +} + +function writeStoredWebAuthToken(token: string): void { + try { + window.sessionStorage.setItem(webAuthStorageKey, token); + } catch { + // The in-memory token still works for the current page load if storage is unavailable. + } +} + function noopSubscription(): () => void { return () => undefined; } diff --git a/src/main/web-management-server.ts b/src/main/web-management-server.ts index c22697f..4af4773 100644 --- a/src/main/web-management-server.ts +++ b/src/main/web-management-server.ts @@ -1,6 +1,8 @@ import { spawn } from "node:child_process"; +import { randomBytes, timingSafeEqual } from "node:crypto"; import { createServer, type IncomingMessage, type Server, type ServerResponse } from "node:http"; import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs"; +import net from "node:net"; import os from "node:os"; import path from "node:path"; import { pathToFileURL } from "node:url"; @@ -80,10 +82,19 @@ type RpcRequest = { type RpcHandler = (...args: unknown[]) => Promise | unknown; +type WebManagementSecurityContext = { + allowIpLiteralHosts: boolean; + allowedHostnames: Set; + authToken: string; + port: number; +}; + const defaultWebHost = "127.0.0.1"; const defaultWebPort = 3458; const onboardingFinishedAtSettingKey = "onboardingFinishedAt"; const maxRpcBodyBytes = 8 * 1024 * 1024; +const webAuthHeader = "x-ccr-web-auth"; +const webAuthQueryParam = "ccr_web_token"; const staticRoot = path.resolve(__dirname, "..", "renderer"); const homeHtmlFile = path.join(staticRoot, "pages", "home", "index.html"); const rendererAssetsRoot = path.join(staticRoot, "assets"); @@ -111,8 +122,14 @@ const pluginMarketplace: PluginMarketplaceEntry[] = [ export async function startWebManagementServer(options: WebManagementServerOptions = {}): Promise { const host = options.host?.trim() || readEnvString("CCR_WEB_HOST") || defaultWebHost; const requestedPort = options.port ?? readEnvPort("CCR_WEB_PORT") ?? defaultWebPort; + const authToken = randomBytes(32).toString("base64url"); + let security: WebManagementSecurityContext | undefined; const server = createServer((request, response) => { - void handleRequest(request, response).catch((error) => { + if (!security) { + sendJson(response, 503, { error: { message: "CCR web management server is not ready." }, ok: false }); + return; + } + void handleRequest(request, response, security).catch((error) => { sendJson(response, 500, { error: { message: formatError(error) }, ok: false }); }); }); @@ -120,7 +137,9 @@ export async function startWebManagementServer(options: WebManagementServerOptio const listenedPort = await listenWithFallback(server, requestedPort, host); const address = server.address(); const port = typeof address === "object" && address ? address.port : listenedPort; - const url = `http://${formatListenHost(host)}:${port}/`; + const baseUrl = `http://${formatListenHost(host)}:${port}/`; + const url = urlWithWebAuthToken(baseUrl, authToken); + security = createWebManagementSecurityContext(host, port, authToken); if (options.startGateway !== false) { await startConfiguredServices("web startup"); @@ -141,10 +160,15 @@ export async function startWebManagementServer(options: WebManagementServerOptio }; } -async function handleRequest(request: IncomingMessage, response: ServerResponse): Promise { +async function handleRequest(request: IncomingMessage, response: ServerResponse, security: WebManagementSecurityContext): Promise { + if (!isAllowedWebRequestHost(request, security)) { + sendText(response, 403, "Forbidden host"); + return; + } + const url = requestUrl(request); if (url.pathname === "/api/ccr/rpc") { - await handleRpcRequest(request, response); + await handleRpcRequest(request, response, security); return; } if (request.method !== "GET" && request.method !== "HEAD") { @@ -162,11 +186,23 @@ async function handleRequest(request: IncomingMessage, response: ServerResponse) sendText(response, 404, "Not found"); } -async function handleRpcRequest(request: IncomingMessage, response: ServerResponse): Promise { +async function handleRpcRequest(request: IncomingMessage, response: ServerResponse, security: WebManagementSecurityContext): Promise { if (request.method !== "POST") { sendJson(response, 405, { error: { message: "RPC only supports POST." }, ok: false }); return; } + if (!isJsonRequest(request)) { + sendJson(response, 415, { error: { message: "RPC requests must use application/json." }, ok: false }); + return; + } + if (!isAllowedWebRequestOrigin(request, security)) { + sendJson(response, 403, { error: { message: "Forbidden RPC origin." }, ok: false }); + return; + } + if (!hasValidWebAuthToken(request, security)) { + sendJson(response, 401, { error: { message: "CCR web authentication token is missing or invalid." }, ok: false }); + return; + } let payload: RpcRequest; try { @@ -498,7 +534,8 @@ function sendBuffer(response: ServerResponse, status: number, body: Buffer, cont response.writeHead(status, { "cache-control": "no-store", "content-length": body.length, - "content-type": contentType + "content-type": contentType, + "x-content-type-options": "nosniff" }); if (headOnly) { response.end(); @@ -512,7 +549,8 @@ function sendJson(response: ServerResponse, status: number, payload: unknown): v response.writeHead(status, { "cache-control": "no-store", "content-length": body.length, - "content-type": "application/json; charset=utf-8" + "content-type": "application/json; charset=utf-8", + "x-content-type-options": "nosniff" }); response.end(body); } @@ -525,6 +563,109 @@ function requestUrl(request: IncomingMessage): URL { return new URL(request.url || "/", `http://${request.headers.host || "127.0.0.1"}`); } +function createWebManagementSecurityContext(host: string, port: number, authToken: string): WebManagementSecurityContext { + const normalizedHost = normalizeHostname(host); + const allowedHostnames = new Set(["localhost", "127.0.0.1", "::1", "0:0:0:0:0:0:0:1"]); + if (normalizedHost) { + allowedHostnames.add(normalizedHost); + } + return { + allowIpLiteralHosts: isWildcardBindHost(normalizedHost), + allowedHostnames, + authToken, + port + }; +} + +function urlWithWebAuthToken(value: string, authToken: string): string { + const url = new URL(value); + url.searchParams.set(webAuthQueryParam, authToken); + return url.toString(); +} + +function isAllowedWebRequestHost(request: IncomingMessage, security: WebManagementSecurityContext): boolean { + const hostname = requestHostname(request); + return Boolean(hostname && isAllowedWebHostname(hostname, security)); +} + +function isAllowedWebRequestOrigin(request: IncomingMessage, security: WebManagementSecurityContext): boolean { + const origin = readHeaderValue(request.headers.origin); + if (origin && !isAllowedWebOriginValue(origin, security)) { + return false; + } + + const referer = readHeaderValue(request.headers.referer); + if (!origin && referer && !isAllowedWebOriginValue(referer, security)) { + return false; + } + + return true; +} + +function isAllowedWebOriginValue(value: string, security: WebManagementSecurityContext): boolean { + try { + const url = new URL(value); + const port = url.port ? Number(url.port) : url.protocol === "http:" ? 80 : url.protocol === "https:" ? 443 : undefined; + return url.protocol === "http:" && + port === security.port && + isAllowedWebHostname(normalizeHostname(url.hostname), security); + } catch { + return false; + } +} + +function isAllowedWebHostname(hostname: string, security: WebManagementSecurityContext): boolean { + const normalized = normalizeHostname(hostname); + return security.allowedHostnames.has(normalized) || + (security.allowIpLiteralHosts && Boolean(net.isIP(normalized))); +} + +function requestHostname(request: IncomingMessage): string | undefined { + const host = readHeaderValue(request.headers.host); + if (!host) { + return undefined; + } + try { + return normalizeHostname(new URL(`http://${host}`).hostname); + } catch { + return undefined; + } +} + +function isJsonRequest(request: IncomingMessage): boolean { + const contentType = readHeaderValue(request.headers["content-type"])?.toLowerCase() ?? ""; + return contentType.split(";")[0]?.trim() === "application/json"; +} + +function hasValidWebAuthToken(request: IncomingMessage, security: WebManagementSecurityContext): boolean { + const token = readHeaderValue(request.headers[webAuthHeader]); + return constantTimeEquals(token, security.authToken); +} + +function constantTimeEquals(value: string | undefined, expected: string): boolean { + if (!value) { + return false; + } + const valueBuffer = Buffer.from(value); + const expectedBuffer = Buffer.from(expected); + return valueBuffer.length === expectedBuffer.length && timingSafeEqual(valueBuffer, expectedBuffer); +} + +function readHeaderValue(value: string | string[] | undefined): string | undefined { + if (Array.isArray(value)) { + return readString(value[0]); + } + return readString(value); +} + +function normalizeHostname(value: string): string { + return value.trim().toLowerCase().replace(/^\[|\]$/g, "").replace(/\.$/, ""); +} + +function isWildcardBindHost(host: string): boolean { + return host === "" || host === "0.0.0.0" || host === "::" || host === "::0"; +} + function readRequestBody(request: IncomingMessage, maxBytes: number): Promise { return new Promise((resolve, reject) => { const chunks: Buffer[] = []; diff --git a/src/renderer/pages/home/App.tsx b/src/renderer/pages/home/App.tsx index d536974..df5de23 100644 --- a/src/renderer/pages/home/App.tsx +++ b/src/renderer/pages/home/App.tsx @@ -147,6 +147,13 @@ function providerNameSlug(value: string): string { .replace(/^-+|-+$/g, "") || "provider"; } +async function loadProviderAccountSnapshots(forceRefresh = false): Promise { + if (!window.ccr) { + return []; + } + return window.ccr.getProviderAccountSnapshots(undefined, forceRefresh ? { forceRefresh: true } : undefined); +} + function extensionActionIndexes(index: number, groupIndexes?: number[]): number[] { const indexes = groupIndexes?.length ? groupIndexes : [index]; return [...new Set(indexes.filter((item) => Number.isInteger(item) && item >= 0))]; @@ -252,6 +259,7 @@ function App() { const [usageRange, setUsageRange] = useState("7d"); const [usageStats, setUsageStats] = useState(fallbackUsageStats); const [providerAccountSnapshots, setProviderAccountSnapshots] = useState([]); + const [providerAccountRefreshing, setProviderAccountRefreshing] = useState(false); const updateActionBusyRef = useRef(false); const resolvedLanguage = languagePreference === "system" ? systemLanguage : languagePreference; const copy = appCopy[resolvedLanguage]; @@ -443,7 +451,7 @@ function App() { let cancelled = false; const refreshProviderAccounts = () => { - void window.ccr?.getProviderAccountSnapshots() + void loadProviderAccountSnapshots() .then((snapshots) => { if (!cancelled) { setProviderAccountSnapshots(snapshots); @@ -463,6 +471,20 @@ function App() { }; }, [draftConfig.Providers]); + async function refreshProviderAccountsNow() { + if (providerAccountRefreshing) { + return; + } + setProviderAccountRefreshing(true); + try { + setProviderAccountSnapshots(await loadProviderAccountSnapshots(true)); + } catch { + setProviderAccountSnapshots([]); + } finally { + setProviderAccountRefreshing(false); + } + } + const requestLogsEnabled = Boolean(draftConfig.observability.requestLogs); const agentAnalysisEnabled = Boolean(draftConfig.observability.agentAnalysis); const agentAnalysisFilterKey = JSON.stringify({ @@ -2813,6 +2835,8 @@ function App() { onWidgetsChange: changeOverviewWidgets, overviewWidgets: normalizeOverviewWidgets(draftConfig.overviewWidgets), providerAccounts: providerAccountSnapshots, + providerAccountRefreshing, + refreshProviderAccounts: () => void refreshProviderAccountsNow(), setUsageRange, usageRange, usageStats diff --git a/src/renderer/pages/home/components/dashboard.tsx b/src/renderer/pages/home/components/dashboard.tsx index ac4aec4..da62c80 100644 --- a/src/renderer/pages/home/components/dashboard.tsx +++ b/src/renderer/pages/home/components/dashboard.tsx @@ -8,11 +8,11 @@ import { DragEndEvent, DragOverEvent, DragOverlay, DragStartEvent, Field, formatAxisNumber, formatBytes, formatCompactNumber, formatDuration, formatLogDateTime, formatPercent, formatProviderAccountMeterTitle, formatProviderAccountMeterValue, formatStatusBucketDate, formatStatusCodeCounts, formatSystemStatusRange, formatToolCounts, formatUsdCost, KeyboardSensor, - LabelList, LayoutGroup, Line, MeasuringStrategy, MetricCard, MetricTone, + LabelList, LayoutGroup, Line, LoaderCircle, MeasuringStrategy, MetricCard, MetricTone, metricToneBar, metricToneStroke, motion, normalizeAgentFilterValue, normalizeOverviewWidget, normalizeOverviewWidgets, OverviewMetricKind, overviewMetricOptions, overviewWidgetCollisionDetection, OverviewWidgetConfig, OverviewWidgetSize, overviewWidgetSizeOptions, OverviewWidgetType, OverviewWidgetVariant, Pencil, Pie, PieChart, Plus, - PointerSensor, primaryProviderAccountMeter, providerAccountBadgeVariant, providerAccountMeterProgress, providerAccountMetersForDisplay, providerAccountProgressClass, + PointerSensor, primaryProviderAccountMeter, providerAccountMeterProgress, providerAccountMetersForDisplay, providerAccountProgressClass, providerAccountSnapshotKey, providerAccountSnapshotLabel, ProviderAccountMeter, ProviderAccountSnapshot, ReactNode, ReactPointerEvent, rectSortingStrategy, RefreshCw, Select, SelectControl, SortableContext, sortableKeyboardCoordinates, systemStatusIconClass, systemStatusPointTooltip, systemStatusSegmentClass, @@ -26,6 +26,8 @@ export function OverviewView({ onWidgetsChange, overviewWidgets, providerAccounts, + providerAccountRefreshing = false, + refreshProviderAccounts, setUsageRange, usageRange, usageStats @@ -33,6 +35,8 @@ export function OverviewView({ onWidgetsChange: (widgets: OverviewWidgetConfig[]) => void; overviewWidgets: OverviewWidgetConfig[]; providerAccounts: ProviderAccountSnapshot[]; + providerAccountRefreshing?: boolean; + refreshProviderAccounts?: () => void | Promise; setUsageRange: (range: UsageStatsRange) => void; usageRange: UsageStatsRange; usageStats: UsageStatsSnapshot; @@ -248,6 +252,8 @@ export function OverviewView({ > void | Promise; usageRange: UsageStatsRange; usageStats: UsageStatsSnapshot; widget: OverviewWidgetConfig; @@ -567,6 +579,8 @@ function OverviewWidgetDragOverlay({
void | Promise; usageRange: UsageStatsRange; usageStats: UsageStatsSnapshot; widget: OverviewWidgetConfig; @@ -816,7 +834,7 @@ function OverviewWidgetRenderer({ if (widget.type === "system-status") { content = ; } else if (widget.type === "account-balance") { - content = ; + content = ; } else if (widget.type === "metric") { content = ; } else if (widget.type === "usage-trend") { @@ -952,19 +970,19 @@ function UsageTrendWidget({ } dataKey="requestCount" /> - + ) : null} {variant === "area" ? ( <> - + ) : null} {variant === "line" ? ( <> - + ) : null} {variant === "bar" ? ( @@ -1185,7 +1203,7 @@ function TokenMixOverviewWidget({ const tokenMix = [ { color: "#2563eb", name: t("Input"), value: totals.inputTokens }, { color: "#d97706", name: t("Output"), value: totals.outputTokens }, - { color: "#be123c", name: t("Cache"), value: totals.cacheTokens } + { color: overviewCacheColor, name: t("Cache"), value: totals.cacheTokens } ]; const total = tokenMix.reduce((sum, item) => sum + item.value, 0); const showLegend = dimensions.height >= 2 && dimensions.width >= 2; @@ -1665,6 +1683,8 @@ function overviewWidgetOverlaySizeClass(size: OverviewWidgetSize): string { type OverviewWidgetDimensions = { height: 1 | 2 | 3 | 4; width: 1 | 2 | 3 | 4 }; +const overviewCacheColor = "#6366f1"; + function overviewWidgetDimensions(size: OverviewWidgetSize): OverviewWidgetDimensions { const [widthText, heightText] = size.split(":"); const width = overviewWidgetDimensionValue(widthText); @@ -1752,7 +1772,7 @@ function overviewMetricDatum(metric: OverviewMetricKind, totals: UsageTotals, tr return { label: translate("Output tokens"), ratio: totals.totalTokens > 0 ? totals.outputTokens / totals.totalTokens : 0, tone: "amber", value: formatCompactNumber(totals.outputTokens) }; } if (metric === "cache-tokens") { - return { label: translate("Cache tokens"), ratio: totals.totalTokens > 0 ? totals.cacheTokens / totals.totalTokens : 0, tone: "rose", value: formatCompactNumber(totals.cacheTokens) }; + return { label: translate("Cache tokens"), ratio: totals.totalTokens > 0 ? totals.cacheTokens / totals.totalTokens : 0, tone: "indigo", value: formatCompactNumber(totals.cacheTokens) }; } if (metric === "cache-ratio") { return { label: translate("Cache ratio"), ratio: totals.cacheRatio, tone: "indigo", value: formatPercent(totals.cacheRatio) }; @@ -1897,11 +1917,15 @@ function ProviderAccountsOverview({ accountProvider, accounts, dimensions, + onRefresh, + refreshing = false, variant = "cards" }: { accountProvider?: string; accounts: ProviderAccountSnapshot[]; dimensions: OverviewWidgetDimensions; + onRefresh?: () => void | Promise; + refreshing?: boolean; variant?: OverviewAccountVariant; }) { const t = useAppText(); @@ -1921,7 +1945,7 @@ function ProviderAccountsOverview({ {t("No account balance connectors configured")}
) : isSingleAccount ? ( - + ) : variant === "compact" ? (
{visibleAccounts.map((account) => { @@ -1931,10 +1955,11 @@ function ProviderAccountsOverview({
{providerAccountSnapshotLabel(account)}
{providerAccountShowSource(dimensions) && meter ?
{t(meter.label)}
: null} + {providerAccountShowRefreshTime(dimensions) ?
{formatProviderAccountRefreshTime(account, t)}
: null}
-
- {providerAccountShowStatus(dimensions) ? {account.status} : null} - {meter ?
{formatProviderAccountMeterValue(meter)}
: null} +
+ {providerAccountShowRefresh(dimensions) ? : null} + {meter ?
{formatProviderAccountMeterValue(meter)}
: null}
); @@ -1951,8 +1976,12 @@ function ProviderAccountsOverview({
{providerAccountSnapshotLabel(account)}
{providerAccountShowSource(dimensions) && meter ?
{t(meter.label)}
: null} + {providerAccountShowRefreshTime(dimensions) ?
{formatProviderAccountRefreshTime(account, t)}
: null} +
+
+ {meter ? {formatProviderAccountMeterValue(meter)} : null} + {providerAccountShowRefresh(dimensions) ? : null}
-
{meter ? formatProviderAccountMeterValue(meter) : account.status}
{progress !== undefined ? (
@@ -1966,7 +1995,7 @@ function ProviderAccountsOverview({ ) : (
{visibleAccounts.map((account) => { - return ; + return ; })}
)} @@ -1978,10 +2007,14 @@ function ProviderAccountsOverview({ function ProviderAccountSinglePanel({ account, dimensions, + onRefresh, + refreshing = false, variant }: { account: ProviderAccountSnapshot; dimensions: OverviewWidgetDimensions; + onRefresh?: () => void | Promise; + refreshing?: boolean; variant: OverviewAccountVariant; }) { const t = useAppText(); @@ -1995,8 +2028,9 @@ function ProviderAccountSinglePanel({
{providerAccountSnapshotLabel(account)}
+ {providerAccountShowRefreshTime(dimensions) ?
{formatProviderAccountRefreshTime(account, t)}
: null}
- {providerAccountShowStatus(dimensions) ? {account.status} : null} + {providerAccountShowRefresh(dimensions) ? : null}
{showQuotaVisual ? ( @@ -2021,10 +2055,14 @@ function ProviderAccountSinglePanel({ function ProviderAccountSummaryCard({ account, dimensions, + onRefresh, + refreshing = false, variant }: { account: ProviderAccountSnapshot; dimensions: OverviewWidgetDimensions; + onRefresh?: () => void | Promise; + refreshing?: boolean; variant: OverviewAccountVariant; }) { const t = useAppText(); @@ -2038,8 +2076,9 @@ function ProviderAccountSummaryCard({
{providerAccountSnapshotLabel(account)}
+ {providerAccountShowRefreshTime(dimensions) ?
{formatProviderAccountRefreshTime(account, t)}
: null}
- {providerAccountShowStatus(dimensions) ? {account.status} : null} + {providerAccountShowRefresh(dimensions) ? : null}
{showQuotaVisual ? (
@@ -2065,6 +2104,52 @@ function ProviderAccountSummaryCard({ ); } +function ProviderAccountRefreshButton({ + account, + onRefresh, + refreshing = false +}: { + account: ProviderAccountSnapshot; + onRefresh?: () => void | Promise; + refreshing?: boolean; +}) { + const t = useAppText(); + const label = refreshing ? t("Refreshing account") : t("Refresh account"); + return ( + + ); +} + +function formatProviderAccountRefreshTime(account: ProviderAccountSnapshot, t: (value: string) => string): string { + return `${t("Last updated")}: ${formatProviderAccountUpdatedAt(account.updatedAt) || "-"}`; +} + +function formatProviderAccountUpdatedAt(value: string): string { + const date = new Date(value); + if (!Number.isFinite(date.getTime())) { + return ""; + } + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, "0"); + const day = String(date.getDate()).padStart(2, "0"); + const hours = String(date.getHours()).padStart(2, "0"); + const minutes = String(date.getMinutes()).padStart(2, "0"); + const seconds = String(date.getSeconds()).padStart(2, "0"); + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; +} + function ProviderAccountMeterLine({ account, dimensions, @@ -2394,7 +2479,11 @@ function providerAccountShowSource(dimensions: OverviewWidgetDimensions): boolea return dimensions.height >= 2 && dimensions.width >= 2; } -function providerAccountShowStatus(dimensions: OverviewWidgetDimensions): boolean { +function providerAccountShowRefreshTime(dimensions: OverviewWidgetDimensions): boolean { + return dimensions.height >= 2 && dimensions.width >= 2; +} + +function providerAccountShowRefresh(dimensions: OverviewWidgetDimensions): boolean { return dimensions.width >= 2; } diff --git a/src/renderer/pages/home/components/providers.tsx b/src/renderer/pages/home/components/providers.tsx index 153e912..4cd8d89 100644 --- a/src/renderer/pages/home/components/providers.tsx +++ b/src/renderer/pages/home/components/providers.tsx @@ -629,6 +629,8 @@ function ProviderPresetCombobox({ const filteredOptions = normalizedQuery ? options.filter((option) => providerPresetOptionMatchesQuery(option, normalizedQuery)) : options; + const selectedExternalUrl = providerPresetOptionPlatformUrl(selected); + const selectedDetail = providerPresetOptionDetail(selected, t); useEffect(() => { if (!open) { @@ -662,29 +664,66 @@ function ProviderPresetCombobox({ setOpen(false); } + function toggleOpen() { + setOpen((current) => !current); + } + + function openSelectedExternalUrl() { + if (!selectedExternalUrl) { + return; + } + openExternalUrl(selectedExternalUrl); + } + return (
- + +
+
{selected ? selected.label : t("Select preset provider")}
+ {selectedDetail ? ( +
{selectedDetail}
+ ) : null} +
+ {selectedExternalUrl ? ( + + ) : null} +
{open ? ( @@ -794,11 +833,7 @@ function ProviderImportHeader({ if (!platformUrl) { return; } - if (window.ccr?.openExternal) { - void window.ccr.openExternal(platformUrl).catch(() => undefined); - return; - } - window.open(platformUrl, "_blank", "noopener,noreferrer"); + openExternalUrl(platformUrl); } return ( @@ -825,7 +860,15 @@ function ProviderImportHeader({ } function providerImportPlatformUrl(provider: ProviderDeepLinkPayload, baseUrl: string, preset: ProviderPreset | undefined): string | undefined { - return normalizedHttpUrl(provider.source) ?? normalizedHttpUrl(preset?.websiteUrl) ?? providerBaseOrigin(baseUrl || provider.baseUrl); + return normalizedHttpUrl(provider.source) ?? providerPresetWebsiteUrlForBaseUrl(preset, baseUrl || provider.baseUrl); +} + +function openExternalUrl(url: string) { + if (window.ccr?.openExternal) { + void window.ccr.openExternal(url).catch(() => undefined); + return; + } + window.open(url, "_blank", "noopener,noreferrer"); } function providerBaseOrigin(value: string): string | undefined { @@ -874,6 +917,76 @@ function providerPresetOptionMatchesQuery( return haystack.includes(query); } +function providerPresetOptionDetail(option: ProviderPresetComboboxOption | undefined, t: (value: string) => string): string { + if (!option) { + return ""; + } + if (option.preset) { + return primaryProviderPresetEndpoint(option.preset)?.baseUrl ?? option.preset.websiteUrl ?? ""; + } + if (option.value === customProviderPresetId) { + return t("API endpoint"); + } + return ""; +} + +function providerPresetOptionPlatformUrl(option: ProviderPresetComboboxOption | undefined): string | undefined { + if (!option?.preset) { + return undefined; + } + return providerPresetWebsiteUrlForEndpoint(option.preset, primaryProviderPresetEndpoint(option.preset)); +} + +function providerPresetWebsiteUrlForBaseUrl(preset: ProviderPreset | undefined, baseUrl: string): string | undefined { + if (!preset) { + return undefined; + } + const normalizedBaseUrl = baseUrl.trim(); + const endpoint = normalizedBaseUrl + ? preset.endpoints.find((item) => item.baseUrl.trim() === normalizedBaseUrl) + : undefined; + return providerPresetWebsiteUrlForEndpoint(preset, endpoint); +} + +function providerPresetWebsiteUrlForEndpoint( + preset: ProviderPreset, + endpoint: ReturnType | undefined +): string | undefined { + return normalizedHttpUrl(endpoint?.websiteUrl) ?? normalizedHttpUrl(preset.websiteUrl); +} + +function providerDraftNameShouldFollowPreset( + name: string, + previousPreset: ProviderPreset | undefined, + t: (value: string) => string +): boolean { + const trimmed = name.trim(); + if (!trimmed || /^provider-\d+$/i.test(trimmed)) { + return true; + } + if (!previousPreset) { + return false; + } + return [previousPreset.name, t(previousPreset.name)].some((baseName) => + providerNameMatchesGeneratedPresetName(trimmed, baseName) + ); +} + +function providerNameMatchesGeneratedPresetName(name: string, baseName: string): boolean { + const normalizedName = name.trim().toLowerCase(); + const normalizedBaseName = baseName.trim().toLowerCase(); + if (!normalizedBaseName) { + return false; + } + if (normalizedName === normalizedBaseName) { + return true; + } + if (!normalizedName.startsWith(`${normalizedBaseName} `)) { + return false; + } + return /^\d+$/.test(normalizedName.slice(normalizedBaseName.length + 1)); +} + function LocalAgentProviderImportPanel({ mode, onChange, @@ -1209,7 +1322,8 @@ export function AddProviderForm({ const preset = findProviderPreset(presetId); const endpoint = preset ? primaryProviderPresetEndpoint(preset) : undefined; - const generatedName = !draft.name.trim() || /^provider-\d+$/i.test(draft.name.trim()); + const previousPreset = findProviderPreset(draft.presetId); + const generatedName = providerDraftNameShouldFollowPreset(draft.name, previousPreset, t); const accountDraft = createProviderAccountDraftFromConfig(defaultProviderAccountConfigForPreset(presetId)); onChange({ ...accountDraft, @@ -1240,13 +1354,14 @@ export function AddProviderForm({ providerPlugins={[...providerPlugins, ...draft.providerPlugins]} providers={providers} /> - +
+ {t("Select preset provider")} - +
)} diff --git a/src/renderer/pages/home/shared/i18n.tsx b/src/renderer/pages/home/shared/i18n.tsx index 596d195..bedf620 100644 --- a/src/renderer/pages/home/shared/i18n.tsx +++ b/src/renderer/pages/home/shared/i18n.tsx @@ -268,6 +268,9 @@ export const appCopy: Record = { "Account component": "Account component", "All accounts": "All accounts", "All credentials": "All credentials", + "Last updated": "Last updated", + "Refresh account": "Refresh account", + "Refreshing account": "Refreshing account", "Add widget": "Add widget", "Analysis component": "Analysis component", "Arc": "Arc", @@ -953,6 +956,9 @@ export const appCopy: Record = { "Account component": "账户组件", "All accounts": "所有账户", "All credentials": "全部凭据", + "Last updated": "上次更新", + "Refresh account": "刷新账户", + "Refreshing account": "正在刷新账户", "Add widget": "添加组件", "Analysis component": "分析组件", "Arc": "弧形", @@ -1385,11 +1391,13 @@ export const appCopy: Record = { "Priority spillover": "优先级溢出", "Ready": "可导入", "Scanning local agent logins": "正在扫描本机 Agent 登录态", - "Select preset provider": "选择预设供应商", + "Select preset provider": "选择 预设供应商", "ZCode login detected. Click Import to add it as a gateway provider.": "已检测到 ZCode 登录态。点击导入即可添加为网关供应商。", "ZCode login was detected, but its local credential is encrypted and cannot be imported automatically.": "已检测到 ZCode 登录态,但本机凭据已加密,无法自动导入。", "ZCode login was detected, but no usable provider API key was found in ZCode config.": "已检测到 ZCode 登录态,但 ZCode 配置中没有找到可用的供应商 API key。", "ZCode provider API key detected in local ZCode config. Click Import to add it as a gateway provider.": "已在本机 ZCode 配置中检测到供应商 API key。点击导入即可添加为网关供应商。", + "Kimi API (China)": "Kimi API(国内)", + "Kimi API (Global)": "Kimi API(海外)", "Kimi Code - Coding Plan": "Kimi Code - Coding Plan", "Zhipu AI (China)": "智谱 AI (国内)", "Zhipu AI (China) - Coding Plan": "智谱 AI (国内) - Coding Plan", diff --git a/src/renderer/pages/home/shared/options.ts b/src/renderer/pages/home/shared/options.ts index 03b1643..e0578e2 100644 --- a/src/renderer/pages/home/shared/options.ts +++ b/src/renderer/pages/home/shared/options.ts @@ -328,6 +328,7 @@ export const providerPresetIconUrls: Record = { "kimi-coding": moonshotProviderIconUrl, mistral: mistralProviderIconUrl, moonshot: moonshotProviderIconUrl, + "moonshot-global": moonshotProviderIconUrl, openai: openaiProviderIconUrl, openrouter: openrouterProviderIconUrl, siliconflow: siliconflowProviderIconUrl, diff --git a/src/renderer/pages/home/shared/providers.ts b/src/renderer/pages/home/shared/providers.ts index b58d908..71864da 100644 --- a/src/renderer/pages/home/shared/providers.ts +++ b/src/renderer/pages/home/shared/providers.ts @@ -1571,7 +1571,10 @@ export function cloneProviderAccountConnectors(connectors: ProviderAccountConnec return JSON.parse(JSON.stringify(connectors)) as ProviderAccountConnectorConfig[]; } -export function defaultProviderAccountConfigForPreset(_presetId: string | undefined): ProviderAccountConfig | undefined { +export function defaultProviderAccountConfigForPreset(presetId: string | undefined): ProviderAccountConfig | undefined { + if (presetId === "kimi-coding") { + return cloneProviderAccountConfig(findProviderPreset(presetId)?.account ?? defaultProviderAccountConfig); + } // Keep the advanced settings default on the standard endpoint; main resolves preset-specific connectors at runtime. return cloneProviderAccountConfig(defaultProviderAccountConfig); } diff --git a/src/server/gateway/claude-code-router-plugin.ts b/src/server/gateway/claude-code-router-plugin.ts index c33d28b..ab47884 100644 --- a/src/server/gateway/claude-code-router-plugin.ts +++ b/src/server/gateway/claude-code-router-plugin.ts @@ -1,6 +1,9 @@ import { createRequire } from "node:module"; import { EventEmitter } from "node:events"; +import os from "node:os"; +import path from "node:path"; import type { AppConfig, RouterConfig, RouterFallbackConfig, RouterRule, RouterRuleCondition, RouterRuleRewrite } from "../../shared/app"; +import { CONFIGDIR } from "../../main/constants"; type HeaderValue = string | string[] | undefined; @@ -99,8 +102,9 @@ export class ClaudeCodeRouterPlugin { } try { - delete requireFromHere.cache[requireFromHere.resolve(routerPath)]; - const loaded = requireFromHere(routerPath) as unknown; + const resolvedRouterPath = resolveCustomRouterModule(routerPath); + delete requireFromHere.cache[resolvedRouterPath]; + const loaded = requireFromHere(resolvedRouterPath) as unknown; const customRouter = typeof loaded === "function" ? loaded : readDefaultFunction(loaded); if (!customRouter) { request.log.warn(`Custom router does not export a function: ${routerPath}`); @@ -115,6 +119,62 @@ export class ClaudeCodeRouterPlugin { } } +function resolveCustomRouterModule(routerPath: string): string { + const resolved = requireFromHere.resolve(resolveLocalModulePath(routerPath, "Custom router")); + assertJavaScriptModulePath(resolved, "Custom router"); + return resolved; +} + +function resolveLocalModulePath(value: string, label: string): string { + const trimmed = value.trim(); + if (!trimmed) { + throw new Error(`${label} path is required.`); + } + + const expanded = expandHome(trimmed); + if (path.isAbsolute(expanded)) { + return expanded; + } + if (isProtocolSpecifier(expanded)) { + throw new Error(`${label} must be a local JavaScript file path, not a URL or protocol specifier.`); + } + if (!expanded.startsWith(".")) { + throw new Error(`${label} must be an explicit local JavaScript path. Package specifiers are not loaded from configuration.`); + } + + const resolved = path.resolve(CONFIGDIR, expanded); + if (!isPathInside(resolved, CONFIGDIR)) { + throw new Error(`${label} relative paths must stay inside the CCR config directory.`); + } + return resolved; +} + +function assertJavaScriptModulePath(resolved: string, label: string): void { + const extension = path.extname(resolved).toLowerCase(); + if (![".cjs", ".js", ".mjs"].includes(extension)) { + throw new Error(`${label} must resolve to a JavaScript module file.`); + } +} + +function expandHome(value: string): string { + if (value === "~") { + return os.homedir(); + } + if (value.startsWith("~/")) { + return path.join(os.homedir(), value.slice(2)); + } + return value; +} + +function isProtocolSpecifier(value: string): boolean { + return /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value); +} + +function isPathInside(file: string, root: string): boolean { + const relative = path.relative(root, file); + return relative === "" || (Boolean(relative) && !relative.startsWith("..") && !path.isAbsolute(relative)); +} + function resolveConfiguredRouteDecision( request: MutableRequestLike, config: AppConfig, diff --git a/src/server/gateway/service.ts b/src/server/gateway/service.ts index a824fa0..7dac5f1 100644 --- a/src/server/gateway/service.ts +++ b/src/server/gateway/service.ts @@ -3139,12 +3139,9 @@ function applyCors(response: ServerResponse, config?: AppConfig): void { async function authorize(request: IncomingMessage, response: ServerResponse, config: AppConfig): Promise { let apiKeys = await configuredApiKeys(config); if (apiKeys.length === 0) { - if (isLoopbackBindHost(config.gateway.host)) { - return { ok: true }; - } sendJson(response, 403, { error: { - message: "Configure at least one CCR API key before listening on a non-loopback gateway host." + message: "CCR API key is not initialized. Save a gateway API key or restart CCR to generate one." } }); return { ok: false }; diff --git a/src/server/proxy/certificates.ts b/src/server/proxy/certificates.ts index 29e4b4e..ea5ae91 100644 --- a/src/server/proxy/certificates.ts +++ b/src/server/proxy/certificates.ts @@ -1,4 +1,4 @@ -import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; import { createHash, randomBytes } from "node:crypto"; import net from "node:net"; import os from "node:os"; @@ -7,6 +7,9 @@ import forge from "node-forge"; import { CERTDIR, PROXY_CA_CERT_DER_FILE, PROXY_CA_CERT_FILE, PROXY_CA_KEY_FILE } from "../../main/constants"; const pki = forge.pki; +const certificateDirectoryMode = 0o700; +const certificateFileMode = 0o644; +const privateKeyFileMode = 0o600; export type CertificateAuthority = { cert: forge.pki.Certificate; @@ -25,8 +28,10 @@ type SubjectAltName = { }; export function ensureProxyCertificateAuthority(): void { - mkdirSync(CERTDIR, { recursive: true }); + mkdirSync(CERTDIR, { mode: certificateDirectoryMode, recursive: true }); + securePathPermissions(CERTDIR, certificateDirectoryMode); if (existsSync(PROXY_CA_CERT_FILE) && existsSync(PROXY_CA_KEY_FILE)) { + secureCertificateAuthorityFilePermissions(); ensureProxyCertificateDerFile(); return; } @@ -68,8 +73,9 @@ export function ensureProxyCertificateAuthority(): void { ]); cert.sign(keys.privateKey, forge.md.sha256.create()); - writeFileSync(PROXY_CA_CERT_FILE, pki.certificateToPem(cert), "utf8"); - writeFileSync(PROXY_CA_KEY_FILE, pki.privateKeyToPem(keys.privateKey), "utf8"); + writeFileSync(PROXY_CA_CERT_FILE, pki.certificateToPem(cert), { encoding: "utf8", mode: certificateFileMode }); + writeFileSync(PROXY_CA_KEY_FILE, pki.privateKeyToPem(keys.privateKey), { encoding: "utf8", mode: privateKeyFileMode }); + secureCertificateAuthorityFilePermissions(); ensureProxyCertificateDerFile(); } @@ -214,7 +220,21 @@ function ensureProxyCertificateDerFile(): boolean { function writeProxyCertificateDerFile(cert: forge.pki.Certificate): void { const der = forge.asn1.toDer(pki.certificateToAsn1(cert)).getBytes(); - writeFileSync(PROXY_CA_CERT_DER_FILE, Buffer.from(der, "binary")); + writeFileSync(PROXY_CA_CERT_DER_FILE, Buffer.from(der, "binary"), { mode: certificateFileMode }); + securePathPermissions(PROXY_CA_CERT_DER_FILE, certificateFileMode); +} + +function secureCertificateAuthorityFilePermissions(): void { + securePathPermissions(PROXY_CA_CERT_FILE, certificateFileMode); + securePathPermissions(PROXY_CA_KEY_FILE, privateKeyFileMode); + securePathPermissions(PROXY_CA_CERT_DER_FILE, certificateFileMode); +} + +function securePathPermissions(file: string, mode: number): void { + if (process.platform === "win32" || !existsSync(file)) { + return; + } + chmodSync(file, mode); } function createSerialNumber(): string { diff --git a/src/shared/provider-presets.ts b/src/shared/provider-presets.ts index f33d067..e004fff 100644 --- a/src/shared/provider-presets.ts +++ b/src/shared/provider-presets.ts @@ -4,6 +4,7 @@ export type ProviderPresetEndpoint = { baseUrl: string; label?: string; protocols: GatewayProviderProtocol[]; + websiteUrl?: string; }; export type ProviderOfficialKeyPattern = { From 608915c03732ee007eade82a1e6bc54743aeeb12 Mon Sep 17 00:00:00 2001 From: musi Date: Mon, 29 Jun 2026 22:53:27 +0800 Subject: [PATCH 10/94] chore: release 3.0.3 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3208688..e282583 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "claude-code-router", - "version": "3.0.2", + "version": "3.0.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "claude-code-router", - "version": "3.0.2", + "version": "3.0.3", "license": "MIT", "dependencies": { "@the-next-ai/ai-gateway": "^1.0.2", diff --git a/package.json b/package.json index ab86d4f..cc38e7c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "claude-code-router", - "version": "3.0.2", + "version": "3.0.3", "license": "MIT", "description": "Local Claude Code Router gateway with CLI and web management UI.", "repository": { From a784eba8370730e45b173a74037d5c08c4703d60 Mon Sep 17 00:00:00 2001 From: musi Date: Tue, 30 Jun 2026 09:19:53 +0800 Subject: [PATCH 11/94] Document SQLite config migration and add test script --- README.md | 8 +- README_zh.md | 8 +- build/run-tests.mjs | 33 +++ build/test.mjs | 44 +++ docs/src/content/docs/en/configuration.md | 4 +- .../en/configuration/configuration-file.md | 15 +- .../docs/en/configuration/extensions.md | 4 +- docs/src/content/docs/en/index.md | 2 +- docs/src/content/docs/zh/configuration.md | 4 +- .../docs/zh/configuration/config-file.md | 15 +- .../docs/zh/configuration/extensions.md | 4 +- docs/src/content/docs/zh/index.md | 2 +- docs/src/i18n/content.ts | 8 +- docs/src/pages/configuration/[slug].astro | 2 +- docs/src/pages/en/configuration/[slug].astro | 2 +- package.json | 1 + src/main/config.ts | 151 +--------- src/main/ipc.ts | 3 +- src/main/main-app.ts | 5 +- src/main/model-catalog-file.ts | 45 +++ src/main/preload.ts | 3 + src/main/provider-model-catalog.ts | 29 +- src/main/provider-probe.ts | 53 +++- src/main/request-log-store.ts | 68 ++++- src/main/tray-controller.ts | 26 +- src/main/usage-store.ts | 274 ++++++++++++++--- src/main/web-client-bridge.ts | 1 + src/main/web-management-server.ts | 4 +- src/renderer/pages/home/App.tsx | 33 +-- .../pages/home/components/network-logs.tsx | 59 +++- src/renderer/pages/home/shared/fallbacks.ts | 157 +--------- src/renderer/pages/home/shared/polling.ts | 53 ++++ src/renderer/types/electron.d.ts | 3 + src/server/gateway/model-catalog.ts | 29 +- src/shared/app.ts | 4 + src/shared/default-config.ts | 166 +++++++++++ src/shared/ipc-channels.ts | 1 + tests/bot-gateway-env.test.mjs | 111 +++++++ tests/deep-link.test.mjs | 126 ++++++++ tests/model-catalog-file.test.mjs | 61 ++++ tests/profile-launch-core.test.mjs | 104 +++++++ tests/provider-preset-utils.test.mjs | 83 ++++++ tests/provider-url.test.mjs | 40 +++ tests/request-log-store.test.mjs | 275 ++++++++++++++++++ tests/usage-activity.test.mjs | 34 +++ tests/usage-normalization.test.mjs | 56 ++++ tests/usage-store.test.mjs | 105 +++++++ 47 files changed, 1858 insertions(+), 460 deletions(-) create mode 100644 build/run-tests.mjs create mode 100644 build/test.mjs create mode 100644 src/main/model-catalog-file.ts create mode 100644 src/renderer/pages/home/shared/polling.ts create mode 100644 src/shared/default-config.ts create mode 100644 tests/bot-gateway-env.test.mjs create mode 100644 tests/deep-link.test.mjs create mode 100644 tests/model-catalog-file.test.mjs create mode 100644 tests/profile-launch-core.test.mjs create mode 100644 tests/provider-preset-utils.test.mjs create mode 100644 tests/provider-url.test.mjs create mode 100644 tests/request-log-store.test.mjs create mode 100644 tests/usage-activity.test.mjs create mode 100644 tests/usage-normalization.test.mjs create mode 100644 tests/usage-store.test.mjs diff --git a/README.md b/README.md index 941b7ad..255c556 100644 --- a/README.md +++ b/README.md @@ -67,9 +67,11 @@ The web management UI listens on `http://127.0.0.1:3458` by default. Use `ccr st - Windows: `Claude Code Router_.exe` - Linux: `Claude Code Router_.AppImage` 3. Install and launch **Claude Code Router**. -4. On first launch, CCR creates its local configuration: - - macOS/Linux: `~/.claude-code-router/config.json` - - Windows: `%APPDATA%\Claude Code Router\config.json` +4. On first launch, CCR creates its local configuration database: + - macOS/Linux: `~/.claude-code-router/config.sqlite` + - Windows: `%APPDATA%\Claude Code Router\config.sqlite` + +CCR stores runtime configuration in SQLite. A legacy `config.json` is read only once for migration when no SQLite config exists. CCR starts two local services when the gateway is enabled: diff --git a/README_zh.md b/README_zh.md index b450f38..de8ee68 100644 --- a/README_zh.md +++ b/README_zh.md @@ -67,9 +67,11 @@ Web 管理端默认监听 `http://127.0.0.1:3458`。可以用 `ccr start --host - Windows:`Claude Code Router_.exe` - Linux:`Claude Code Router_.AppImage` 3. 安装并启动 **Claude Code Router**。 -4. 首次启动后,CCR 会创建本地配置: - - macOS/Linux:`~/.claude-code-router/config.json` - - Windows:`%APPDATA%\Claude Code Router\config.json` +4. 首次启动后,CCR 会创建本地配置数据库: + - macOS/Linux:`~/.claude-code-router/config.sqlite` + - Windows:`%APPDATA%\Claude Code Router\config.sqlite` + +CCR 的运行配置存储在 SQLite 中。旧版 `config.json` 只会在没有 SQLite 配置时作为迁移来源读取一次。 启用网关后,CCR 会启动两个本地服务: diff --git a/build/run-tests.mjs b/build/run-tests.mjs new file mode 100644 index 0000000..086d390 --- /dev/null +++ b/build/run-tests.mjs @@ -0,0 +1,33 @@ +import electron from "electron"; +import { spawn } from "node:child_process"; +import { mkdtempSync, rmSync } from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const projectRoot = path.resolve(__dirname, ".."); +const testHome = mkdtempSync(path.join(os.tmpdir(), "ccr-test-home-")); + +const child = spawn(electron, ["--test", "dist/tests/*.js"], { + cwd: projectRoot, + env: { + ...process.env, + CCR_INTERNAL_APP_DATA_DIR: path.join(testHome, "app-data"), + CCR_INTERNAL_HOME_DIR: testHome, + CCR_INTERNAL_USER_DATA_DIR: path.join(testHome, "user-data"), + HOME: testHome, + ELECTRON_RUN_AS_NODE: "1" + }, + shell: process.platform === "win32", + stdio: "inherit" +}); + +child.on("exit", (code, signal) => { + rmSync(testHome, { force: true, recursive: true }); + if (signal) { + process.kill(process.pid, signal); + return; + } + process.exit(code ?? 1); +}); diff --git a/build/test.mjs b/build/test.mjs new file mode 100644 index 0000000..09e17dd --- /dev/null +++ b/build/test.mjs @@ -0,0 +1,44 @@ +import esbuild from "esbuild"; +import { mkdirSync, readdirSync, rmSync, statSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const projectRoot = path.resolve(__dirname, ".."); +const outdir = path.join(projectRoot, "dist", "tests"); + +rmSync(outdir, { force: true, recursive: true }); +mkdirSync(outdir, { recursive: true }); + +const entryPoints = findTestFiles(path.join(projectRoot, "tests")); + +await esbuild.build({ + absWorkingDir: projectRoot, + bundle: true, + entryNames: "[name]", + entryPoints, + external: [ + "better-sqlite3", + "electron" + ], + format: "cjs", + legalComments: "none", + logLevel: "info", + outdir, + platform: "node", + target: "node22" +}); + +function findTestFiles(dir) { + const files = []; + for (const name of readdirSync(dir)) { + const file = path.join(dir, name); + const stat = statSync(file); + if (stat.isDirectory()) { + files.push(...findTestFiles(file)); + } else if (name.endsWith(".test.mjs")) { + files.push(file); + } + } + return files.sort(); +} diff --git a/docs/src/content/docs/en/configuration.md b/docs/src/content/docs/en/configuration.md index 7f6c4e7..dd828f8 100644 --- a/docs/src/content/docs/en/configuration.md +++ b/docs/src/content/docs/en/configuration.md @@ -2,7 +2,7 @@ title: Claude Code Router Detailed Configuration pageTitle: Detailed Configuration eyebrow: Detailed Configuration -lead: Configure providers, routing, Agent Config, Fusion, Bots, and the config file location in detail. +lead: Configure providers, routing, Agent Config, Fusion, Bots, and the config database location in detail. --- ## Page Structure @@ -19,7 +19,7 @@ Detailed configuration docs are split into standalone pages. Every left-sidebar | Agent Config | Agent launch method, model, scope, multi-instance launching, and Bot binding | | Extension Mechanism | Wrapper plugins, core gateway plugins, custom extension creation, and debugging | | Bots And IM Agent Relay | Bot forwarding, handoff mode, and platform pages | -| Config File Location | Config file location maintained by the desktop app | +| Config Database Location | SQLite config database location maintained by the desktop app | ## Content Relationships diff --git a/docs/src/content/docs/en/configuration/configuration-file.md b/docs/src/content/docs/en/configuration/configuration-file.md index a4f0f4f..5543fc1 100644 --- a/docs/src/content/docs/en/configuration/configuration-file.md +++ b/docs/src/content/docs/en/configuration/configuration-file.md @@ -1,16 +1,17 @@ --- -title: Config File Location -pageTitle: Config File Location +title: Config Database Location +pageTitle: Config Database Location eyebrow: Detailed Configuration -lead: Locate the JSON configuration file maintained by the CCR desktop app. +lead: Locate the SQLite configuration database maintained by the CCR desktop app. --- ## Default Locations -- **macOS**: `~/Library/Application Support/claude-code-router/config.json` -- **Windows**: `%APPDATA%/claude-code-router/config.json` -- **Linux**: `~/.config/claude-code-router/config.json` +- **macOS/Linux**: `~/.claude-code-router/config.sqlite` +- **Windows**: `%APPDATA%\Claude Code Router\config.sqlite` ## Applying Changes -Restart CCR after editing the file directly. +CCR stores runtime configuration in SQLite. A legacy `config.json` is read only once as a migration source when no SQLite config exists; after migration, editing `config.json` does not affect the current configuration. + +Use the desktop UI to change configuration, or export a backup from **Settings**. Do not edit `config.sqlite` directly while CCR is running; SQLite also maintains companion `config.sqlite-wal` and `config.sqlite-shm` files in the same directory. diff --git a/docs/src/content/docs/en/configuration/extensions.md b/docs/src/content/docs/en/configuration/extensions.md index ceccdea..a5e7912 100644 --- a/docs/src/content/docs/en/configuration/extensions.md +++ b/docs/src/content/docs/en/configuration/extensions.md @@ -177,7 +177,7 @@ The recommended flow is through the desktop UI: 4. Save the config. 5. Open **Server** and restart the gateway. -You can also edit the config file directly: +CCR stores runtime configuration in SQLite, so add extensions through the UI instead of editing the legacy JSON config file. The extension entry has this shape: ```json { @@ -194,7 +194,7 @@ You can also edit the config file directly: } ``` -Restart CCR after editing the config file directly. See [Config File Location](/en/configuration/configuration-file/). +Restart the gateway after saving the extension config. See [Config Database Location](/en/configuration/configuration-file/). The local directory picker recognizes entry metadata from: diff --git a/docs/src/content/docs/en/index.md b/docs/src/content/docs/en/index.md index 0091b7d..a628432 100644 --- a/docs/src/content/docs/en/index.md +++ b/docs/src/content/docs/en/index.md @@ -26,7 +26,7 @@ The top navigation is split into four standalone pages: | --- | --- | | [Documentation](./) | Product positioning, architecture overview, and reading path | | [Quick Start](guides/) | From installation and provider setup to connecting an agent | -| [Detailed Configuration](configuration/) | Providers, routing, Agent Config, Fusion, Bots, and config file location | +| [Detailed Configuration](configuration/) | Providers, routing, Agent Config, Fusion, Bots, and config database location | | [Q&A](troubleshooting/) | Request logs, observability panel, and common questions | Bot platform guides are child pages under Detailed Configuration. Each platform has its own page so platform dashboard fields, callback URLs, signatures, and FAQs can be expanded independently. diff --git a/docs/src/content/docs/zh/configuration.md b/docs/src/content/docs/zh/configuration.md index 26b0a78..8cc3c8e 100644 --- a/docs/src/content/docs/zh/configuration.md +++ b/docs/src/content/docs/zh/configuration.md @@ -2,7 +2,7 @@ title: Claude Code Router 详细配置 pageTitle: 详细配置 eyebrow: 详细配置 -lead: 深入配置供应商、路由、Agent配置、Fusion、Bot 和配置文件位置。这里是按功能查字段和扩展能力的地方。 +lead: 深入配置供应商、路由、Agent配置、Fusion、Bot 和配置数据库位置。这里是按功能查字段和扩展能力的地方。 --- ## 页面结构 @@ -19,7 +19,7 @@ lead: 深入配置供应商、路由、Agent配置、Fusion、Bot 和配置文 | Agent配置 | Agent 启动方式、模型、作用范围、多开和 Bot 绑定 | | 扩展机制 | Wrapper plugin、Core gateway plugin、自定义扩展创建和调试 | | Bot 与 IM 接力 Agent | Bot 转发、接力模式和平台页面 | -| 配置文件位置 | 桌面 App 维护的配置文件位置 | +| 配置数据库位置 | 桌面 App 维护的 SQLite 配置数据库位置 | ## 内容关系 diff --git a/docs/src/content/docs/zh/configuration/config-file.md b/docs/src/content/docs/zh/configuration/config-file.md index c60f808..fe34677 100644 --- a/docs/src/content/docs/zh/configuration/config-file.md +++ b/docs/src/content/docs/zh/configuration/config-file.md @@ -1,16 +1,17 @@ --- -title: 配置文件位置 -pageTitle: 配置文件位置 +title: 配置数据库位置 +pageTitle: 配置数据库位置 eyebrow: 详细配置 -lead: 找到 CCR 桌面 App 默认维护的配置文件。 +lead: 找到 CCR 桌面 App 默认维护的 SQLite 配置数据库。 --- ## 默认位置 -- macOS:`~/Library/Application Support/claude-code-router/config.json` -- Windows:`%APPDATA%/claude-code-router/config.json` -- Linux:`~/.config/claude-code-router/config.json` +- macOS/Linux:`~/.claude-code-router/config.sqlite` +- Windows:`%APPDATA%\Claude Code Router\config.sqlite` ## 生效方式 -直接编辑配置文件后,需要重启 CCR。 +CCR 的运行配置存储在 SQLite 中。旧版 `config.json` 只会在没有 SQLite 配置时作为迁移来源读取一次,迁移完成后继续编辑 `config.json` 不会影响当前配置。 + +建议通过桌面 UI 修改配置,或在 **Settings** 中导出备份。不要在 CCR 运行时直接编辑 `config.sqlite`;SQLite 还会维护同目录的 `config.sqlite-wal` 和 `config.sqlite-shm` 辅助文件。 diff --git a/docs/src/content/docs/zh/configuration/extensions.md b/docs/src/content/docs/zh/configuration/extensions.md index 5fdf852..56dce99 100644 --- a/docs/src/content/docs/zh/configuration/extensions.md +++ b/docs/src/content/docs/zh/configuration/extensions.md @@ -177,7 +177,7 @@ module.exports = { 4. 保存配置。 5. 打开 **Server** 页面,重启网关。 -也可以直接编辑配置文件: +CCR 的运行配置存储在 SQLite 中,因此推荐通过 UI 添加扩展,而不是编辑旧版 JSON 配置文件。扩展条目的配置结构如下: ```json { @@ -194,7 +194,7 @@ module.exports = { } ``` -直接编辑配置文件后需要重启 CCR。配置文件位置见 [配置文件位置](/configuration/config-file/)。 +保存扩展配置后需要重启网关。配置数据库位置见 [配置数据库位置](/configuration/config-file/)。 本地目录选择器会按顺序识别这些入口信息: diff --git a/docs/src/content/docs/zh/index.md b/docs/src/content/docs/zh/index.md index 698cb07..00aef30 100644 --- a/docs/src/content/docs/zh/index.md +++ b/docs/src/content/docs/zh/index.md @@ -26,7 +26,7 @@ CCR 默认监听本机地址 `http://localhost:8080`。Agent 只要指向这个 | --- | --- | | [文档](./) | 产品定位、架构概览、阅读路径 | | [快速开始](guides/) | 从安装、接供应商,到接入 Agent 的上手流程 | -| [详细配置](configuration/) | 供应商、路由、配置、Fusion、Bot 和配置文件位置 | +| [详细配置](configuration/) | 供应商、路由、配置、Fusion、Bot 和配置数据库位置 | | [Q&A](troubleshooting/) | 请求日志、观测面板和常见问题 | Bot 平台教程是「详细配置」分类下的子页面,每个平台有独立页面,方便逐步补齐平台后台字段、回调 URL、签名和 FAQ。 diff --git a/docs/src/i18n/content.ts b/docs/src/i18n/content.ts index 5148abf..be1739b 100644 --- a/docs/src/i18n/content.ts +++ b/docs/src/i18n/content.ts @@ -71,7 +71,7 @@ export const docsContent = { "Agent配置", "扩展机制", "Bot 与 IM 接力 Agent", - "配置文件位置", + "配置数据库位置", ], active: "供应商配置", }, @@ -112,7 +112,7 @@ export const docsContent = { 企业微信: "/bot-与-im-接力-agent/wecom/", 飞书: "/bot-与-im-接力-agent/feishu/", 钉钉: "/bot-与-im-接力-agent/dingtalk/", - 配置文件位置: "/configuration/config-file/", + 配置数据库位置: "/configuration/config-file/", }, sidebarTargets: {}, }, @@ -211,7 +211,7 @@ export const docsContent = { "Agent Config", "Extension Mechanism", "Bots And IM Agent Relay", - "Config File Location", + "Config Database Location", ], active: "Provider Config", }, @@ -242,7 +242,7 @@ export const docsContent = { WeCom: "/en/relay-agents-in-im-with-bots/wecom/", Feishu: "/en/relay-agents-in-im-with-bots/feishu/", DingTalk: "/en/relay-agents-in-im-with-bots/dingtalk/", - "Config File Location": "/en/configuration/configuration-file/", + "Config Database Location": "/en/configuration/configuration-file/", }, sidebarTargets: {}, }, diff --git a/docs/src/pages/configuration/[slug].astro b/docs/src/pages/configuration/[slug].astro index 5e056e9..9253864 100644 --- a/docs/src/pages/configuration/[slug].astro +++ b/docs/src/pages/configuration/[slug].astro @@ -16,7 +16,7 @@ export function getStaticPaths() { extensions: "扩展机制", "bot-relay": "Bot 与 IM 接力 Agent", "bot-setup": "配置步骤", - "config-file": "配置文件位置", + "config-file": "配置数据库位置", }; return Object.entries(zhConfigurationDocs).map(([filePath, mod]) => { diff --git a/docs/src/pages/en/configuration/[slug].astro b/docs/src/pages/en/configuration/[slug].astro index 3c7bd97..4cd9324 100644 --- a/docs/src/pages/en/configuration/[slug].astro +++ b/docs/src/pages/en/configuration/[slug].astro @@ -16,7 +16,7 @@ export function getStaticPaths() { extensions: "Extension Mechanism", bots: "Bots And IM Agent Relay", "bot-setup": "Setup", - "configuration-file": "Config File Location", + "configuration-file": "Config Database Location", }; return Object.entries(enConfigurationDocs).map(([filePath, mod]) => { diff --git a/package.json b/package.json index cc38e7c..0f930d8 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "prepack": "npm run build:assets", "prepublishOnly": "npm run typecheck", "preview": "npm run build:assets && electron .", + "test": "node build/test.mjs && node build/run-tests.mjs", "typecheck": "tsc --noEmit", "rebuild:sqlite3": "electron-rebuild -f -w better-sqlite3" }, diff --git a/src/main/config.ts b/src/main/config.ts index 15de3cf..efaca14 100644 --- a/src/main/config.ts +++ b/src/main/config.ts @@ -4,6 +4,7 @@ import { loadPersistedAppConfig, replacePersistedAppConfig } from "./app-config- import { loadPersistedApiKeys, replacePersistedApiKeys } from "./api-key-store"; import { CONFIG_FILE, GATEWAY_CONFIG_FILE, LEGACY_CONFIG_FILE } from "./constants"; import { CLAUDE_CODE_DEFAULT_ENV, CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY_ENV, DEFAULT_OVERVIEW_WIDGETS, DEFAULT_TRAY_COMPONENT_VARIANTS, DEFAULT_TRAY_WIDGETS, DEFAULT_TRAY_WINDOW_MODULES, OVERVIEW_WIDGET_SIZE_VALUES, ROUTER_FALLBACK_MAX_RETRY_COUNT, TRAY_SINGLETON_WIDGET_TYPES, TRAY_TOP_WIDGET_TYPES, TRAY_WINDOW_MODULE_IDS, enforceSingleEnabledGlobalProfilePerAgent } from "../shared/app"; +import { createDefaultAppConfig } from "../shared/default-config"; import { findProviderPresetByBaseUrl, providerApiKeySafetyIssue, providerEndpointCanReceiveProviderApiKey } from "./presets"; import type { AppConfig, @@ -81,15 +82,6 @@ type RawAppConfigLoadResult = { value: Partial; }; -const DEFAULT_PROXY_TARGETS: ProxyRouteTarget[] = [ - { host: "api.anthropic.com", paths: ["/v1/messages", "/v1/messages/count_tokens"] }, - { host: "api.openai.com", paths: ["/v1/chat/completions", "/v1/responses", "/v1/models"] }, - { host: "generativelanguage.googleapis.com", paths: ["/v1beta/models", "/v1/models"] }, - { host: "openrouter.ai", paths: ["/api/v1/chat/completions", "/api/v1/responses", "/api/v1/models"] }, - { host: "api.deepseek.com", paths: ["/chat/completions", "/v1/chat/completions", "/models", "/v1/models"] }, - { host: "api.mistral.ai", paths: ["/v1/chat/completions", "/v1/models"] } -]; - const REMOVED_LEGACY_ROUTER_RULE_IDS = new Set([ "legacy-subagent", "legacy-background", @@ -100,143 +92,10 @@ const REMOVED_LEGACY_ROUTER_RULE_IDS = new Set([ const INTERNAL_GATEWAY_CORE_HOST = "127.0.0.1"; const GENERATED_GATEWAY_API_KEY_ID = "local-gateway"; -const DEFAULT_CONFIG: AppConfig = { - APIKEY: "", - APIKEYS: [], - API_TIMEOUT_MS: 600000, - CUSTOM_ROUTER_PATH: "", - HOST: "127.0.0.1", - PORT: 3456, - Providers: [], - Router: { - fallback: { - mode: "off", - models: [], - retryCount: 1 - }, - longContextThreshold: 200000, - rules: [] - }, - agent: { - mcpServers: [] - }, - autoStart: false, - botConfigs: [], - botGateway: { - acknowledgeEvents: false, - args: [], - authType: "", - autoStartIntegration: true, - command: "", - createIntegration: false, - credentials: {}, - cwd: "", - enabled: false, - forwardAllAgentMessages: true, - handoff: { - enabled: false, - idleSeconds: 30, - phoneBluetoothTargets: [], - phoneWifiTargets: [], - screenLock: true, - userIdle: true - }, - integrationConfig: {}, - integrationId: "", - platform: "none", - pollIntervalMs: 2000, - requestTimeoutMs: 600000, - sourceDir: "", - startupTimeoutMs: 10000, - stateDir: "", - tenantId: "ccr" - }, - gateway: { - coreHost: INTERNAL_GATEWAY_CORE_HOST, - corePort: 3457, - enabled: true, - generatedConfigFile: GATEWAY_CONFIG_FILE, - host: "127.0.0.1", - port: 3456 - }, - observability: { - agentAnalysis: false, - requestLogs: false - }, - preferredProvider: "", - plugins: [], - overviewWidgets: DEFAULT_OVERVIEW_WIDGETS, - profile: { - claudeCode: { - enabled: true, - model: "", - settingsFile: "~/.claude/settings.json", - smallFastModel: "" - }, - codex: { - cliMiddleware: true, - codexCliPath: "", - codexHome: "", - configFormat: "separate_profile_files", - configFile: "~/.codex/config.toml", - enabled: true, - model: "", - providerId: "claude-code-router", - providerName: "Claude Code Router", - showAllSessions: false - }, - enabled: true, - profiles: [ - { - agent: "claude-code", - enabled: true, - env: { ...CLAUDE_CODE_DEFAULT_ENV }, - id: "default-claude-code", - model: "", - name: "Claude Code", - scope: "global", - settingsFile: "~/.claude/settings.json", - smallFastModel: "", - surface: "auto" - }, - { - agent: "codex", - cliMiddleware: true, - codexCliPath: "", - codexHome: "", - configFormat: "separate_profile_files", - configFile: "~/.codex/config.toml", - enabled: true, - env: {}, - id: "default-codex", - model: "", - name: "Codex", - providerId: "claude-code-router", - providerName: "Claude Code Router", - showAllSessions: false, - scope: "global", - surface: "auto" - } - ] - }, - proxy: { - browserMode: true, - captureNetwork: false, - enabled: false, - host: "127.0.0.1", - mode: "gateway", - port: 7890, - systemProxy: false, - targets: DEFAULT_PROXY_TARGETS - }, - routerEndpoint: "http://127.0.0.1:3456", - theme: "system", - trayComponentVariants: DEFAULT_TRAY_COMPONENT_VARIANTS, - trayIcon: "random", - trayProgressTargetTokens: 100000, - trayWidgets: DEFAULT_TRAY_WIDGETS, - trayWindowModules: DEFAULT_TRAY_WINDOW_MODULES -}; +const DEFAULT_CONFIG: AppConfig = createDefaultAppConfig({ + coreHost: INTERNAL_GATEWAY_CORE_HOST, + generatedConfigFile: GATEWAY_CONFIG_FILE +}); function completeBotGatewayConfig(config: LoadedBotGatewayConfig | undefined): BotGatewayRuntimeConfig { const platform = normalizeBotGatewayPlatform(config?.platform ?? DEFAULT_CONFIG.botGateway.platform); diff --git a/src/main/ipc.ts b/src/main/ipc.ts index 94b24b0..feb36f5 100644 --- a/src/main/ipc.ts +++ b/src/main/ipc.ts @@ -24,7 +24,7 @@ import { getProfileOpenCommand, getProfileRuntimeStatus, openProfileFromCcr, sto import { ensureProxyCertificateAuthority } from "../server/proxy/certificates"; import { proxyService } from "../server/proxy/service"; import { listMcpServerTools } from "../server/mcp/tool-discovery"; -import { getAgentAnalysis, getAgentTracePayload, getRequestLogs } from "./request-log-store"; +import { getAgentAnalysis, getAgentTracePayload, getRequestLogDetail, getRequestLogs } from "./request-log-store"; import trayController from "./tray-controller"; import { appUpdateService } from "./update-service"; import { getUsageStats } from "./usage-store"; @@ -94,6 +94,7 @@ ipcMain.handle(IPC_CHANNELS.appGetProxyCertificateStatus, () => proxyService.get ipcMain.handle(IPC_CHANNELS.appGetProxyNetworkCaptures, () => proxyService.getNetworkCaptures()); ipcMain.handle(IPC_CHANNELS.appGetProxyStatus, () => proxyService.getStatus()); ipcMain.handle(IPC_CHANNELS.appGetPluginMarketplace, () => pluginMarketplace); +ipcMain.handle(IPC_CHANNELS.appGetRequestLogDetail, (_event, request) => getRequestLogDetail(request)); ipcMain.handle(IPC_CHANNELS.appGetRequestLogs, (_event, filter?: RequestLogListFilter) => getRequestLogs(filter)); ipcMain.handle(IPC_CHANNELS.appGetUpdateStatus, () => appUpdateService.getStatus()); ipcMain.handle(IPC_CHANNELS.appGetUsageStats, (_event, range?: UsageStatsRange, filter?: UsageStatsFilter) => getUsageStats(range, filter)); diff --git a/src/main/main-app.ts b/src/main/main-app.ts index 72f63d5..3fedf93 100644 --- a/src/main/main-app.ts +++ b/src/main/main-app.ts @@ -51,7 +51,10 @@ function startPrimaryInstance(): void { void startConfiguredServices("startup"); app.on("activate", () => { - windowsManager.showMainWindow(); + const mainWindow = windowsManager.getWindow("main"); + if (!trayController.consumeMainWindowActivationSuppression() && (!mainWindow || !mainWindow.isVisible())) { + windowsManager.showMainWindow(); + } queueEnsureConfiguredProxyModeActive("activate"); }); }); diff --git a/src/main/model-catalog-file.ts b/src/main/model-catalog-file.ts new file mode 100644 index 0000000..68b5c86 --- /dev/null +++ b/src/main/model-catalog-file.ts @@ -0,0 +1,45 @@ +import { existsSync, readFileSync } from "node:fs"; +import { resolve as pathResolve } from "node:path"; + +export type LoadedModelCatalogPayload = { + loadedFrom: string; + payload: unknown; +}; + +export function loadModelCatalogPayload(): LoadedModelCatalogPayload | undefined { + for (const candidate of modelCatalogPathCandidates()) { + if (!existsSync(candidate)) { + continue; + } + return { + loadedFrom: candidate, + payload: JSON.parse(readFileSync(candidate, "utf8")) as unknown + }; + } + return undefined; +} + +export function modelCatalogPathCandidates(): string[] { + return uniqueStrings([ + process.env.CCR_MODEL_CATALOG_PATH?.trim() || "", + process.env.CCR_MODELS_JSON_PATH?.trim() || "", + pathResolve(process.cwd(), "models.json"), + pathResolve(__dirname, "..", "models.json"), + pathResolve(__dirname, "..", "assets", "models.json"), + pathResolve(__dirname, "..", "..", "..", "models.json") + ]); +} + +function uniqueStrings(values: Array): string[] { + const seen = new Set(); + const strings: string[] = []; + for (const value of values) { + const trimmed = value?.trim(); + if (!trimmed || seen.has(trimmed)) { + continue; + } + seen.add(trimmed); + strings.push(trimmed); + } + return strings; +} diff --git a/src/main/preload.ts b/src/main/preload.ts index c7d117d..53677e9 100644 --- a/src/main/preload.ts +++ b/src/main/preload.ts @@ -58,6 +58,8 @@ import type { ProxyCertificateStatus, ProxyNetworkSnapshot, ProxyStatus, + RequestLogDetailRequest, + RequestLogEntry, RequestLogListFilter, RequestLogPage, UsageStatsFilter, @@ -108,6 +110,7 @@ contextBridge.exposeInMainWorld("ccr", { getProxyCertificateStatus: () => invoke(IPC_CHANNELS.appGetProxyCertificateStatus) as Promise, getProxyNetworkCaptures: () => invoke(IPC_CHANNELS.appGetProxyNetworkCaptures) as Promise, getProxyStatus: () => invoke(IPC_CHANNELS.appGetProxyStatus) as Promise, + getRequestLogDetail: (request: RequestLogDetailRequest) => invoke(IPC_CHANNELS.appGetRequestLogDetail, request) as Promise, getRequestLogs: (filter?: RequestLogListFilter) => invoke(IPC_CHANNELS.appGetRequestLogs, filter) as Promise, getUpdateStatus: () => invoke(IPC_CHANNELS.appGetUpdateStatus) as Promise, getUsageStats: (range?: UsageStatsRange, filter?: UsageStatsFilter) => invoke(IPC_CHANNELS.appGetUsageStats, range, filter) as Promise, diff --git a/src/main/provider-model-catalog.ts b/src/main/provider-model-catalog.ts index 70e9b48..87519c6 100644 --- a/src/main/provider-model-catalog.ts +++ b/src/main/provider-model-catalog.ts @@ -1,7 +1,6 @@ -import { existsSync, readFileSync } from "node:fs"; -import { resolve as pathResolve } from "node:path"; import type { ProviderCatalogModelsRequest, ProviderCatalogModelsResult } from "../shared/app"; import { providerUrlWithDefaultScheme } from "../shared/provider-url"; +import { loadModelCatalogPayload } from "./model-catalog-file"; import { findProviderPreset, findProviderPresetByBaseUrl } from "./presets"; type CatalogProviderEntry = { @@ -121,17 +120,14 @@ function loadCatalogIndex(): CatalogIndex { return catalogIndex; } - for (const candidate of catalogPathCandidates()) { - if (!existsSync(candidate)) { - continue; - } - try { - const payload = JSON.parse(readFileSync(candidate, "utf8")) as unknown; - catalogIndex = buildCatalogIndex(payload, candidate); + try { + const loaded = loadModelCatalogPayload(); + if (loaded) { + catalogIndex = buildCatalogIndex(loaded.payload, loaded.loadedFrom); return catalogIndex; - } catch (error) { - console.warn(`Failed to load provider model catalog from ${candidate}:`, error); } + } catch (error) { + console.warn("Failed to load provider model catalog:", error); } catalogIndex = { @@ -140,17 +136,6 @@ function loadCatalogIndex(): CatalogIndex { return catalogIndex; } -function catalogPathCandidates(): string[] { - return uniqueStrings([ - process.env.CCR_MODEL_CATALOG_PATH?.trim() || "", - process.env.CCR_MODELS_JSON_PATH?.trim() || "", - pathResolve(process.cwd(), "models.json"), - pathResolve(__dirname, "..", "models.json"), - pathResolve(__dirname, "..", "assets", "models.json"), - pathResolve(__dirname, "..", "..", "..", "models.json") - ]); -} - function buildCatalogIndex(payload: unknown, loadedFrom: string): CatalogIndex { const providers = new Map(); const models = isRecord(payload) && Array.isArray(payload.models) ? payload.models : []; diff --git a/src/main/provider-probe.ts b/src/main/provider-probe.ts index 48032f6..91ed2d4 100644 --- a/src/main/provider-probe.ts +++ b/src/main/provider-probe.ts @@ -220,8 +220,10 @@ async function resolveGatewayProviderProbe(request: GatewayProviderProbeRequest) const modelProbe = mode !== "models" || request.skipModelDiscovery ? { models: [] } : await probeModels(parsed, request.apiKey, protocols); - const models = mode === "connectivity" && modelProbe.models.length > 0 ? modelProbe.models : typedModels; - const protocolResults = mode === "models" ? [] : await probeProtocols(parsed, request.apiKey, models, protocols, mode); + const models = (mode === "connectivity" || mode === "models") && modelProbe.models.length > 0 + ? modelProbe.models + : typedModels; + const protocolResults = await probeProtocols(parsed, request.apiKey, models, protocols, mode); const detectedProtocol = detectProtocol(parsed, protocolResults, modelProbe.source, protocols); return { @@ -466,7 +468,7 @@ async function probeProtocols( results.push( mode === "connectivity" ? await probeProtocolConnectivity(parsed, apiKey, models, protocol) - : await probeProtocolSupport(parsed, protocol) + : await probeProtocolSupport(parsed, apiKey, protocol) ); } @@ -475,6 +477,7 @@ async function probeProtocols( async function probeProtocolSupport( parsed: ParsedProviderUrl, + apiKey: string | undefined, protocol: GatewayProviderProtocol ): Promise { const endpoints = endpointsForProtocol(parsed, protocol, undefined); @@ -482,7 +485,7 @@ async function probeProtocolSupport( let firstResult: GatewayProviderProbeProtocolResult | undefined; for (const candidate of endpoints) { - const result = await requestJson(candidate.endpoint, requestForProtocolSupport(protocol)); + const result = await requestJson(candidate.endpoint, requestForProtocolSupport(protocol, apiKey)); const message = readResponseMessage(result); const supported = isProtocolEndpointSupported(result.status, message); const probeResult = { @@ -620,12 +623,12 @@ function requestForProtocol(protocol: GatewayProviderProtocol, model: string, ap }; } -function requestForProtocolSupport(protocol: GatewayProviderProtocol): RequestInit { +function requestForProtocolSupport(protocol: GatewayProviderProtocol, apiKey: string | undefined): RequestInit { return { body: JSON.stringify({}), headers: { "content-type": "application/json", - ...(protocol === "anthropic_messages" ? { "anthropic-version": "2023-06-01" } : {}) + ...headersForProtocol(protocol, apiKey) }, method: "POST" }; @@ -686,10 +689,16 @@ function endpointsForProtocol( } if (protocol === "anthropic_messages") { - return parsed.anthropicBaseUrlCandidates.map((baseUrl) => ({ - baseUrl, - endpoint: `${baseUrl}/v1/messages` - })); + return parsed.anthropicBaseUrlCandidates.flatMap((baseUrl) => uniqueProtocolEndpoints([ + { + baseUrl, + endpoint: `${baseUrl}/v1/messages` + }, + { + baseUrl, + endpoint: `${baseUrl}/messages` + } + ])); } const encodedModel = encodeURIComponent(stripGeminiModelPrefix(model || "model")); @@ -734,6 +743,16 @@ function geminiHeaders(apiKey: string | undefined): Record { : {}; } +function headersForProtocol(protocol: GatewayProviderProtocol, apiKey: string | undefined): Record { + if (protocol === "anthropic_messages") { + return anthropicHeaders(apiKey); + } + if (protocol === "gemini_generate_content") { + return geminiHeaders(apiKey); + } + return openAiHeaders(apiKey); +} + function parseModelList(payload: unknown, source: ModelSource): Pick { if (!isRecord(payload)) { return { @@ -1067,6 +1086,20 @@ function uniqueProtocols(values: GatewayProviderProtocol[]): GatewayProviderProt return values.filter((value, index) => values.indexOf(value) === index); } +function uniqueProtocolEndpoints(values: ProtocolEndpoint[]): ProtocolEndpoint[] { + const seen = new Set(); + const result: ProtocolEndpoint[] = []; + for (const value of values) { + const key = `${value.baseUrl}\n${value.endpoint}`; + if (seen.has(key)) { + continue; + } + seen.add(key); + result.push(value); + } + return result; +} + function uniqueModelSources(values: ModelSource[]): ModelSource[] { return values.filter((value, index) => values.indexOf(value) === index); } diff --git a/src/main/request-log-store.ts b/src/main/request-log-store.ts index 1ebfea5..25c735f 100644 --- a/src/main/request-log-store.ts +++ b/src/main/request-log-store.ts @@ -30,6 +30,8 @@ import type { AgentKind, GatewayProviderProtocol, RequestLogBody, + RequestLogDetailRequest, + RequestLogEntry, RequestLogFilterOptions, RequestLogListFilter, RequestLogPage, @@ -195,6 +197,10 @@ const maxBodyBytes = 2 * 1024 * 1024; const maxAgentAnalysisRows = 5000; const maxAgentSessionDetailRequests = 250; const maxTracePayloadPreviewChars = 1600; +const requestLogBodyMetadataSelect = ` + '' AS request_body_text, + '' AS response_body_text +`; const emptyAgentAnalysisTotals: AgentAnalysisTotals = { avgDurationMs: 0, cacheRatio: 0, @@ -227,10 +233,18 @@ const sensitiveHeaderNames = new Set([ "x-auth-sub" ]); -class RequestLogStore { +type AgentAnalysisCacheEntry = { + filterKey: string; + revision: number; + snapshot: AgentAnalysisSnapshot; +}; + +export class RequestLogStore { private database?: SqlDatabase; private initPromise?: Promise; private lastRetentionCleanupDay?: string; + private revision = 0; + private analysisCache?: AgentAnalysisCacheEntry; constructor(private readonly dbFile: string) {} @@ -371,6 +385,7 @@ class RequestLogStore { responseBody.truncated ? 1 : 0, responseError ?? "" ); + this.revision += 1; } async updateFromRawTrace(input: RequestLogRawTraceUpdateInput): Promise { @@ -520,6 +535,7 @@ class RequestLogStore { } database.prepare(`UPDATE request_logs SET ${sets.join(", ")} WHERE request_id = ?`).run(...params, requestId); + this.revision += 1; return true; } @@ -563,12 +579,11 @@ class RequestLogStore { cost_usd, request_headers, response_headers, - request_body_text, + ${requestLogBodyMetadataSelect}, request_body_encoding, request_body_content_type, request_body_size_bytes, request_body_truncated, - response_body_text, response_body_encoding, response_body_content_type, response_body_size_bytes, @@ -593,10 +608,26 @@ class RequestLogStore { }; } + async getDetail(request: RequestLogDetailRequest): Promise { + const database = await this.getDatabase(); + const requestLogId = normalizeCount(request.id); + if (requestLogId <= 0) { + return undefined; + } + return readRequestLogById(database, requestLogId); + } + async analyze(filter: AgentAnalysisFilter = {}): Promise { const database = await this.getDatabase(); this.pruneOldRequestLogs(database); const now = new Date(); + const filterKey = agentAnalysisCacheKey(filter); + if (this.analysisCache?.revision === this.revision && this.analysisCache.filterKey === filterKey) { + return { + ...this.analysisCache.snapshot, + generatedAt: now.toISOString() + }; + } const range = normalizeAgentAnalysisRange(filter.range); const since = getAgentAnalysisSince(range, now); const rows = queryRows( @@ -665,7 +696,7 @@ class RequestLogStore { ? buildAgentSessionDetail(analysisRequests) : undefined; - return { + const snapshot: AgentAnalysisSnapshot = { agents: buildAgentRows(analysisRequests), clients: buildAgentClientRows(analysisRequests), concurrency: buildAgentConcurrencySeries(range, now, analysisRequests), @@ -682,6 +713,12 @@ class RequestLogStore { tools: buildAgentToolRows(analysisRequests), totals: buildAgentAnalysisTotals(analysisRequests) }; + this.analysisCache = { + filterKey, + revision: this.revision, + snapshot + }; + return snapshot; } async getTracePayload(request: AgentAnalysisTracePayloadRequest): Promise { @@ -840,6 +877,15 @@ export async function getRequestLogs(filter?: RequestLogListFilter): Promise { + try { + return await requestLogStore.getDetail(request); + } catch (error) { + console.warn(`[request-log] Failed to read request log detail: ${formatError(error)}`); + throw error; + } +} + export async function getAgentAnalysis(filter?: AgentAnalysisFilter): Promise { try { return await requestLogStore.analyze(filter); @@ -902,6 +948,15 @@ function toAnalyzedAgentRequest(entry: StoredRequestLogEntry): AnalyzedAgentRequ }; } +function agentAnalysisCacheKey(filter: AgentAnalysisFilter): string { + return JSON.stringify({ + agent: normalizeAgentFilter(filter.agent), + range: normalizeAgentAnalysisRange(filter.range), + sessionAgent: normalizeAgentFilter(filter.sessionAgent), + sessionId: normalizeFilterValue(filter.sessionId) + }); +} + function extractAgentLogDetails(entry: StoredRequestLogEntry): AgentLogDetails { const requestPayloads = parseLogBodyPayloads(entry.requestBody); const responsePayloads = parseLogBodyPayloads(entry.responseBody); @@ -2632,6 +2687,11 @@ function ensureRequestLogSchema(database: SqlDatabase): void { database.exec("CREATE INDEX IF NOT EXISTS request_logs_provider_idx ON request_logs(provider)"); database.exec("CREATE INDEX IF NOT EXISTS request_logs_source_usage_id_idx ON request_logs(source_usage_id)"); database.exec("CREATE INDEX IF NOT EXISTS request_logs_status_idx ON request_logs(ok, status_code)"); + database.exec("CREATE INDEX IF NOT EXISTS request_logs_list_idx ON request_logs(source_usage_id, created_at DESC, id DESC)"); + database.exec("CREATE INDEX IF NOT EXISTS request_logs_credential_created_at_idx ON request_logs(credential_id, created_at DESC)"); + database.exec("CREATE INDEX IF NOT EXISTS request_logs_model_created_at_idx ON request_logs(model, created_at DESC)"); + database.exec("CREATE INDEX IF NOT EXISTS request_logs_provider_created_at_idx ON request_logs(provider, created_at DESC)"); + database.exec("CREATE INDEX IF NOT EXISTS request_logs_status_created_at_idx ON request_logs(ok, created_at DESC)"); } function backfillRequestLogStreamFlags(database: SqlDatabase): void { diff --git a/src/main/tray-controller.ts b/src/main/tray-controller.ts index 0bcf23f..11d5b1e 100644 --- a/src/main/tray-controller.ts +++ b/src/main/tray-controller.ts @@ -14,6 +14,7 @@ const popoverDetailGap = 12; const popoverDetailTopOffset = 0; const popoverDetailWidth = 420; const popoverMargin = 8; +const trayActivationSuppressMs = 750; const trayMenuBarIconSize = 20; const trayWindowBackgroundColor = "#020617"; const trayTokenFallbackTitle = "0 tokens"; @@ -38,6 +39,7 @@ class TrayController { private randomTrayIconDateKey?: string; private resolvedRandomTrayIcon?: TrayMascotIconId; private refreshTimer?: NodeJS.Timeout; + private suppressMainWindowActivationUntil = 0; private tray?: Tray; private trayBalanceProgress?: TrayBalanceProgressConfig; private trayIconPreference: TrayIconPreference = "random"; @@ -52,8 +54,14 @@ class TrayController { const icon = createTrayIcon(this.resolveTrayIconId("random")); this.tray = new Tray(icon.isEmpty() ? nativeImage.createEmpty() : icon); this.applyTrayTitle(trayTokenFallbackTitle); - this.tray.on("click", () => this.togglePopover()); - this.tray.on("right-click", () => this.showContextMenu()); + this.tray.on("click", () => { + this.suppressMainWindowActivation(); + this.togglePopover(); + }); + this.tray.on("right-click", () => { + this.suppressMainWindowActivation(); + this.showContextMenu(); + }); void this.refreshIconFromConfig(); this.unsubscribeUsageUpdates = onUsageRecorded(() => { @@ -102,6 +110,14 @@ class TrayController { void this.refreshTrayTitle(); } + consumeMainWindowActivationSuppression(): boolean { + if (process.platform !== "darwin" || Date.now() > this.suppressMainWindowActivationUntil) { + return false; + } + this.suppressMainWindowActivationUntil = 0; + return true; + } + async refreshIconFromConfig(config?: AppConfig): Promise { if (!supportsTrayPlatform() || !this.tray) { return; @@ -140,6 +156,12 @@ class TrayController { this.showPopover(); } + private suppressMainWindowActivation(): void { + if (process.platform === "darwin") { + this.suppressMainWindowActivationUntil = Date.now() + trayActivationSuppressMs; + } + } + private showPopover(): void { const popover = this.ensurePopover(); this.clearDetailCloseTimer(); diff --git a/src/main/usage-store.ts b/src/main/usage-store.ts index 351d8c5..45cd1f9 100644 --- a/src/main/usage-store.ts +++ b/src/main/usage-store.ts @@ -59,6 +59,11 @@ type UsageStatsQueryOptions = { includeProxy?: boolean; }; +type UsageWhereClause = { + params: SqlValue[]; + where: string; +}; + type StoredUsageEvent = { cacheReadTokens: number; cacheWriteTokens: number; @@ -98,7 +103,7 @@ const emptyTotals: UsageTotals = { totalTokens: 0 }; -class UsageStore { +export class UsageStore { private database?: SqlDatabase; private initPromise?: Promise; @@ -174,27 +179,23 @@ class UsageStore { const database = await this.getDatabase(); const now = new Date(); const since = getRangeSince(range, now); - const query = buildUsageStatsQuery(since, filter); - const events = queryRows(database, query.sql, query.params).map(toStoredUsageEvent); + const query = buildUsageWhereClause(since, filter); return { - clientModels: buildClientModelRows(events), + clientModels: readClientModelRows(database, query), generatedAt: now.toISOString(), - models: buildModelRows(events), - providerModels: buildProviderModelRows(events), + models: readModelRows(database, query), + providerModels: readProviderModelRows(database, query), range, - recentRequests: buildRecentRequestRows(events), - series: buildSeries(range, now, events), - totals: buildTotals(events) + recentRequests: readRecentRequestRows(database, query), + series: readUsageSeries(database, range, now, query), + totals: readUsageTotals(database, query) }; } async getTotalsSince(since: Date, filter: UsageStatsFilter = {}, options: UsageStatsQueryOptions = {}): Promise { const database = await this.getDatabase(); - const query = buildUsageStatsQuery(since, filter, options); - const events = queryRows(database, query.sql, query.params).map(toStoredUsageEvent); - - return buildTotals(events); + return readUsageTotals(database, buildUsageWhereClause(since, filter, options)); } private async getDatabase(): Promise { @@ -277,6 +278,10 @@ function ensureUsageSchema(database: SqlDatabase): void { database.exec("CREATE INDEX IF NOT EXISTS usage_events_credential_id_idx ON usage_events(credential_id)"); database.exec("CREATE INDEX IF NOT EXISTS usage_events_model_idx ON usage_events(model)"); database.exec("CREATE INDEX IF NOT EXISTS usage_events_path_idx ON usage_events(path)"); + database.exec("CREATE INDEX IF NOT EXISTS usage_events_created_filter_idx ON usage_events(created_at, provider, model, credential_id)"); + database.exec("CREATE INDEX IF NOT EXISTS usage_events_provider_created_at_idx ON usage_events(provider, created_at)"); + database.exec("CREATE INDEX IF NOT EXISTS usage_events_model_created_at_idx ON usage_events(model, created_at)"); + database.exec("CREATE INDEX IF NOT EXISTS usage_events_credential_created_at_idx ON usage_events(credential_id, created_at)"); } export async function getUsageStats(range?: UsageStatsRange, filter?: UsageStatsFilter): Promise { @@ -339,11 +344,11 @@ export async function recordGatewayUsageCapture(input: UsageCaptureInput): Promi } } -function buildUsageStatsQuery( +function buildUsageWhereClause( since: Date, filter: UsageStatsFilter, options: UsageStatsQueryOptions = {} -): { params: SqlValue[]; sql: string } { +): UsageWhereClause { const where = ["created_at >= ?"]; const params: SqlValue[] = [since.toISOString()]; const credential = normalizeFilterValue(filter.credential); @@ -368,30 +373,7 @@ function buildUsageStatsQuery( return { params, - sql: ` - SELECT - id, - created_at, - request_id, - client, - method, - path, - model, - provider, - credential_id, - status_code, - duration_ms, - input_tokens, - output_tokens, - cache_read_tokens, - cache_write_tokens, - total_tokens, - cost_usd, - cost_source - FROM usage_events - WHERE ${where.join(" AND ")} - ORDER BY created_at ASC - ` + where: where.join(" AND ") }; } @@ -428,6 +410,220 @@ function toStoredUsageEvent(row: Record): StoredUsageEvent { }; } +const usageTotalsSelect = ` + COUNT(*) AS request_count, + COALESCE(SUM(input_tokens), 0) AS input_tokens, + COALESCE(SUM(output_tokens), 0) AS output_tokens, + COALESCE(SUM(cache_read_tokens), 0) AS cache_read_tokens, + COALESCE(SUM(cache_write_tokens), 0) AS cache_write_tokens, + COALESCE(SUM(CASE + WHEN total_tokens > 0 THEN total_tokens + ELSE input_tokens + output_tokens + cache_read_tokens + cache_write_tokens + END), 0) AS computed_total_tokens, + COALESCE(SUM(COALESCE(cost_usd, 0)), 0) AS cost_usd, + COALESCE(SUM(duration_ms), 0) AS duration_ms, + COALESCE(SUM(CASE WHEN status_code >= 200 AND status_code < 400 THEN 1 ELSE 0 END), 0) AS success_count, + COALESCE(SUM(CASE + WHEN total_tokens - output_tokens > 0 THEN MAX(input_tokens, total_tokens - output_tokens) + ELSE input_tokens + cache_read_tokens + cache_write_tokens + END), 0) AS prompt_tokens +`; + +function readUsageTotals(database: SqlDatabase, query: UsageWhereClause): UsageTotals { + const row = queryRows( + database, + ` + SELECT + ${usageTotalsSelect} + FROM usage_events + WHERE ${query.where} + `, + query.params + )[0]; + return usageTotalsFromRow(row); +} + +function readUsageSeries( + database: SqlDatabase, + range: UsageStatsRange, + now: Date, + query: UsageWhereClause +): UsageSeriesPoint[] { + const unit: "day" | "hour" = range === "today" || range === "24h" ? "hour" : "day"; + const bucketExpression = unit === "hour" + ? "strftime('%Y-%m-%d %H:00', created_at, 'localtime')" + : "strftime('%Y-%m-%d', created_at, 'localtime')"; + const rows = queryRows( + database, + ` + SELECT + ${bucketExpression} AS bucket, + ${usageTotalsSelect} + FROM usage_events + WHERE ${query.where} + GROUP BY bucket + `, + query.params + ); + const totalsByBucket = new Map(rows.map((row) => [String(row.bucket ?? ""), usageTotalsFromRow(row)])); + + return buildBuckets(range, now).map(({ key, label }) => ({ + ...(totalsByBucket.get(key) ?? { ...emptyTotals }), + bucket: key, + label + })); +} + +function readModelRows(database: SqlDatabase, query: UsageWhereClause): UsageComparisonRow[] { + const rows = readUsageGroupRows( + database, + query, + "provider, model", + "provider, model, MAX(credential_id) AS credential_id", + 8 + ).map((row) => ({ + ...usageTotalsFromRow(row), + caption: normalizeLabel(String(row.provider ?? ""), "unknown"), + credentialId: normalizeFilterValue(String(row.credential_id ?? "")), + key: `${normalizeLabel(String(row.provider ?? ""), "unknown")}::${normalizeLabel(String(row.model ?? ""), "unknown")}`, + label: normalizeLabel(String(row.model ?? ""), "unknown"), + maxShare: 0, + model: normalizeLabel(String(row.model ?? ""), "unknown"), + provider: normalizeLabel(String(row.provider ?? ""), "unknown") + })); + return applyMaxShare(rows, (row) => row.totalTokens || row.requestCount); +} + +function readClientModelRows(database: SqlDatabase, query: UsageWhereClause): UsageComparisonRow[] { + const rows = readUsageGroupRows( + database, + query, + "client, provider, credential_id, model", + "client, provider, credential_id, model", + 25 + ).map((row) => { + const client = normalizeLabel(String(row.client ?? ""), "unknown"); + const model = normalizeLabel(String(row.model ?? ""), "unknown"); + const provider = normalizeLabel(String(row.provider ?? ""), "unknown"); + const credentialId = normalizeFilterValue(String(row.credential_id ?? "")) ?? ""; + return { + ...usageTotalsFromRow(row), + caption: credentialId ? `${provider} / ${credentialId} / ${model}` : `${provider} / ${model}`, + client, + credentialId: credentialId || undefined, + key: `${client}::${provider}::${credentialId}::${model}`, + label: client, + maxShare: 0, + model, + provider + }; + }); + return applyMaxShare(rows, (row) => row.totalTokens || row.requestCount); +} + +function readProviderModelRows(database: SqlDatabase, query: UsageWhereClause): UsageComparisonRow[] { + const rows = readUsageGroupRows( + database, + query, + "provider, credential_id, model", + "provider, credential_id, model", + 25 + ).map((row) => { + const model = normalizeLabel(String(row.model ?? ""), "unknown"); + const provider = normalizeLabel(String(row.provider ?? ""), "unknown"); + const credentialId = normalizeFilterValue(String(row.credential_id ?? "")) ?? ""; + return { + ...usageTotalsFromRow(row), + caption: credentialId ? `${credentialId} / ${model}` : model, + credentialId: credentialId || undefined, + key: `${provider}::${credentialId}::${model}`, + label: provider, + maxShare: 0, + model, + provider + }; + }); + return applyMaxShare(rows, (row) => row.totalTokens || row.requestCount); +} + +function readUsageGroupRows( + database: SqlDatabase, + query: UsageWhereClause, + groupBy: string, + selectColumns: string, + limit: number +): Record[] { + return queryRows( + database, + ` + SELECT + ${selectColumns}, + ${usageTotalsSelect} + FROM usage_events + WHERE ${query.where} + GROUP BY ${groupBy} + ORDER BY computed_total_tokens DESC, request_count DESC + LIMIT ? + `, + [...query.params, limit] + ); +} + +function readRecentRequestRows(database: SqlDatabase, query: UsageWhereClause): UsageComparisonRow[] { + const events = queryRows( + database, + ` + SELECT + id, + created_at, + request_id, + client, + method, + path, + model, + provider, + credential_id, + status_code, + duration_ms, + input_tokens, + output_tokens, + cache_read_tokens, + cache_write_tokens, + total_tokens, + cost_usd, + cost_source + FROM usage_events + WHERE ${query.where} + ORDER BY created_at DESC, id DESC + LIMIT 10 + `, + query.params + ).map(toStoredUsageEvent).reverse(); + return buildRecentRequestRows(events); +} + +function usageTotalsFromRow(row: Record | undefined): UsageTotals { + const requestCount = normalizeCount(row?.request_count); + if (requestCount === 0) { + return { ...emptyTotals }; + } + const successfulRequests = normalizeCount(row?.success_count); + const promptTokens = normalizeCount(row?.prompt_tokens); + const cacheTokens = normalizeCount(row?.cache_read_tokens); + return { + avgDurationMs: Math.round(normalizeCount(row?.duration_ms) / requestCount), + cacheRatio: ratio(cacheTokens, promptTokens), + cacheTokens, + costUsd: normalizeCost(row?.cost_usd), + errorCount: requestCount - successfulRequests, + inputTokens: normalizeCount(row?.input_tokens), + outputTokens: normalizeCount(row?.output_tokens), + requestCount, + successRate: successfulRequests / requestCount, + totalTokens: normalizeCount(row?.computed_total_tokens) + }; +} + function buildSeries(range: UsageStatsRange, now: Date, events: StoredUsageEvent[]): UsageSeriesPoint[] { const buckets = buildBuckets(range, now); const grouped = new Map(); diff --git a/src/main/web-client-bridge.ts b/src/main/web-client-bridge.ts index a9096f2..e5bf48c 100644 --- a/src/main/web-client-bridge.ts +++ b/src/main/web-client-bridge.ts @@ -112,6 +112,7 @@ window.ccr = { getProxyCertificateStatus: () => rpc("getProxyCertificateStatus") as ReturnType["getProxyCertificateStatus"]>, getProxyNetworkCaptures: () => rpc("getProxyNetworkCaptures") as ReturnType["getProxyNetworkCaptures"]>, getProxyStatus: () => rpc("getProxyStatus") as ReturnType["getProxyStatus"]>, + getRequestLogDetail: (request) => rpc("getRequestLogDetail", [request]) as ReturnType["getRequestLogDetail"]>, getRequestLogs: (filter) => rpc("getRequestLogs", [filter]) as ReturnType["getRequestLogs"]>, getUpdateStatus: () => rpc("getUpdateStatus") as ReturnType["getUpdateStatus"]>, getUsageStats: (range, filter) => rpc("getUsageStats", [range, filter]) as ReturnType["getUsageStats"]>, diff --git a/src/main/web-management-server.ts b/src/main/web-management-server.ts index 4af4773..40d7f2b 100644 --- a/src/main/web-management-server.ts +++ b/src/main/web-management-server.ts @@ -24,7 +24,7 @@ import { getProfileOpenCommand, getProfileRuntimeStatus, openProfileFromCcr, sto import { ensureProxyCertificateAuthority } from "../server/proxy/certificates"; import { proxyService } from "../server/proxy/service"; import { listMcpServerTools } from "../server/mcp/tool-discovery"; -import { getAgentAnalysis, getAgentTracePayload, getRequestLogs } from "./request-log-store"; +import { getAgentAnalysis, getAgentTracePayload, getRequestLogDetail, getRequestLogs } from "./request-log-store"; import { getUsageStats } from "./usage-store"; import { gatewayService } from "../server/gateway/service"; import { getProviderAccountSnapshots, invalidateProviderAccountSnapshotCache, testProviderAccountConnector } from "./provider-account-service"; @@ -57,6 +57,7 @@ import type { ProviderCatalogModelsRequest, ProviderIconDetectionRequest, ProviderManifestFetchRequest, + RequestLogDetailRequest, RequestLogListFilter, UsageStatsFilter, UsageStatsRange @@ -285,6 +286,7 @@ const rpcHandlers: Record = { getProxyCertificateStatus: () => proxyService.getCertificateStatus(), getProxyNetworkCaptures: () => proxyService.getNetworkCaptures(), getProxyStatus: () => proxyService.getStatus(), + getRequestLogDetail: (request) => getRequestLogDetail(request as RequestLogDetailRequest), getRequestLogs: (filter) => getRequestLogs(filter as RequestLogListFilter | undefined), getUpdateStatus: () => unsupportedUpdateStatus, getUsageStats: (range, filter) => getUsageStats(range as UsageStatsRange | undefined, filter as UsageStatsFilter | undefined), diff --git a/src/renderer/pages/home/App.tsx b/src/renderer/pages/home/App.tsx index df5de23..633aea5 100644 --- a/src/renderer/pages/home/App.tsx +++ b/src/renderer/pages/home/App.tsx @@ -11,7 +11,7 @@ import { enforceSingleEnabledGlobalProfilePerAgent, ExtensionConfigTarget, ExtensionDeleteTarget, ExtensionInstallDraft, ExtensionSource, fallbackAgentAnalysis, fallbackConfig, fallbackGatewayStatus, fallbackInfo, fallbackProxyCertificateStatus, fallbackProxyNetworkSnapshot, fallbackProxyStatus, fallbackRequestLogPage, - fallbackUpdateStatus, fallbackUsageStats, formatAppError, formatJson, formatProxyCertificateInstallMessage, GatewayProviderConfig, + fallbackUpdateStatus, fallbackUsageStats, formatAppError, formatProxyCertificateInstallMessage, GatewayProviderConfig, fusionCustomMcpServerFromDraft, fusionCustomToolConfigFromProfile, GatewayProviderProbeResult, gatewayServiceMessage, GatewayStatus, getDefaultOnboardingStep, isClaudeDesignPluginConfig, isClaudeDesignRoutingDraftValid, isCursorProxyPluginConfig, isMacPlatform, isPlainRecord, isProfileDraftSubmittable, isProviderNameDuplicate, isProviderProbeCandidateReady, @@ -34,6 +34,7 @@ import { useMemo, useReducedMotion, useRef, useState, validateVirtualModelDraft, ViewId, VirtualModelDraft, virtualModelProfileFromDraft } from "./shared"; +import { startVisiblePolling } from "./shared/polling"; import { AppDialogStack, LightToast, MainLayout, OnboardingLayout } from "./components"; @@ -336,10 +337,9 @@ function App() { void window.ccr?.getProxyStatus().then(setProxyStatus); void refreshProfileRuntimeStatus(); }; - refreshRuntimeStatus(); - const timer = window.setInterval(refreshRuntimeStatus, 2000); + const stopPolling = startVisiblePolling(refreshRuntimeStatus, 2000); return () => { - window.clearInterval(timer); + stopPolling(); unsubscribeOpenSettings(); unsubscribeOpenUpdate(); }; @@ -435,11 +435,10 @@ function App() { } }); }; - refreshUsageStats(); - const timer = window.setInterval(refreshUsageStats, 5000); + const stopPolling = startVisiblePolling(refreshUsageStats, 5000); return () => { cancelled = true; - window.clearInterval(timer); + stopPolling(); }; }, [usageRange]); @@ -463,11 +462,10 @@ function App() { } }); }; - refreshProviderAccounts(); - const timer = window.setInterval(refreshProviderAccounts, 30000); + const stopPolling = startVisiblePolling(refreshProviderAccounts, 30000); return () => { cancelled = true; - window.clearInterval(timer); + stopPolling(); }; }, [draftConfig.Providers]); @@ -538,11 +536,11 @@ function App() { }); }; + const stopPolling = startVisiblePolling(() => refreshAgentAnalysis(), 5000, { immediate: false }); refreshAgentAnalysis(true); - const timer = window.setInterval(() => refreshAgentAnalysis(), 5000); return () => { cancelled = true; - window.clearInterval(timer); + stopPolling(); }; }, [activeView, agentAnalysisEnabled, agentAnalysisFilterKey]); @@ -587,11 +585,11 @@ function App() { }); }; + const stopPolling = startVisiblePolling(() => refreshRequestLogs(), 5000, { immediate: false }); refreshRequestLogs(true); - const timer = window.setInterval(() => refreshRequestLogs(), 5000); return () => { cancelled = true; - window.clearInterval(timer); + stopPolling(); }; }, [activeView, requestLogsEnabled, requestLogFilterKey]); @@ -612,15 +610,14 @@ function App() { } }); }; - refreshNetworkCaptures(); - const timer = window.setInterval(refreshNetworkCaptures, 1500); + const stopPolling = startVisiblePolling(refreshNetworkCaptures, 1500); return () => { cancelled = true; - window.clearInterval(timer); + stopPolling(); }; }, [activeView, draftConfig.proxy.captureNetwork]); - const dirty = useMemo(() => formatJson(savedConfig) !== formatJson(draftConfig), [draftConfig, savedConfig]); + const dirty = draftConfig !== savedConfig; const apiKeys = useMemo(() => createApiKeyList(draftConfig), [draftConfig.APIKEY, draftConfig.APIKEYS]); const apiKeyEditItem = apiKeyEditIndex === undefined ? undefined : apiKeys.find((apiKey) => apiKey.index === apiKeyEditIndex); const providerDeleteItem = providerDeleteIndex === undefined ? undefined : draftConfig.Providers[providerDeleteIndex]; diff --git a/src/renderer/pages/home/components/network-logs.tsx b/src/renderer/pages/home/components/network-logs.tsx index 35ee0da..da6e060 100644 --- a/src/renderer/pages/home/components/network-logs.tsx +++ b/src/renderer/pages/home/components/network-logs.tsx @@ -284,6 +284,9 @@ export function LogsView({ }) { const t = useAppText(); const [expandedId, setExpandedId] = useState(); + const [detailById, setDetailById] = useState>({}); + const [detailErrorById, setDetailErrorById] = useState>({}); + const [detailLoadingId, setDetailLoadingId] = useState(); const firstItem = page.total === 0 ? 0 : (page.page - 1) * page.pageSize + 1; const lastItem = Math.min(page.total, page.page * page.pageSize); const hasAnyCredentialInfo = Boolean(filter.credential) || @@ -292,9 +295,36 @@ export function LogsView({ const logTableGridClass = hasAnyCredentialInfo ? "grid-cols-[minmax(0,0.8fr)_minmax(92px,0.38fr)_minmax(98px,0.4fr)_minmax(0,0.78fr)_minmax(120px,0.42fr)_minmax(0,0.68fr)_82px]" : "grid-cols-[minmax(0,0.8fr)_minmax(92px,0.38fr)_minmax(98px,0.4fr)_minmax(0,0.9fr)_minmax(0,0.74fr)_82px]"; + const loadLogDetail = useCallback((id: number) => { + if (detailById[id] || detailLoadingId === id || !window.ccr?.getRequestLogDetail) { + return; + } + setDetailLoadingId(id); + setDetailErrorById((current) => ({ ...current, [id]: "" })); + void window.ccr.getRequestLogDetail({ id }) + .then((detail) => { + if (detail) { + setDetailById((current) => ({ ...current, [id]: detail })); + return; + } + setDetailErrorById((current) => ({ ...current, [id]: t("Request log not found.") })); + }) + .catch((error) => { + setDetailErrorById((current) => ({ ...current, [id]: error instanceof Error ? error.message : String(error) })); + }) + .finally(() => { + setDetailLoadingId((current) => current === id ? undefined : current); + }); + }, [detailById, detailLoadingId, t]); const toggleExpandedLog = useCallback((id: number) => { - setExpandedId((current) => current === id ? undefined : id); - }, []); + setExpandedId((current) => { + const next = current === id ? undefined : id; + if (next !== undefined) { + loadLogDetail(next); + } + return next; + }); + }, [loadLogDetail]); useEffect(() => { if (!expandedId || page.items.some((item) => item.id === expandedId)) { @@ -420,10 +450,12 @@ export function LogsView({ {page.items.map((item, index) => ( {tokenSummary}
{formatDuration(item.durationMs)}
- {expanded ? : null} + {expanded ? : null}
); }); -function LogExpandedDetails({ entry }: { entry: RequestLogEntry }) { +function LogExpandedDetails({ + detailError, + detailLoading, + entry +}: { + detailError?: string; + detailLoading?: boolean; + entry: RequestLogEntry; +}) { const t = useAppText(); const hasCredentialInfo = logHasCredentialInfo(entry); @@ -529,6 +573,11 @@ function LogExpandedDetails({ entry }: { entry: RequestLogEntry }) {
{entry.retryAttempts.length > 0 ? : null} + {detailLoading || detailError ? ( +
+ {detailError || t("Loading full payload...")} +
+ ) : null}
Promise | unknown; + +type PollingOptions = { + immediate?: boolean; +}; + +export function startVisiblePolling(refresh: PollingRefresh, intervalMs: number, options: PollingOptions = {}): () => void { + let cancelled = false; + let inFlight = false; + let pending = false; + + const run = () => { + if (cancelled || document.hidden) { + pending = true; + return; + } + if (inFlight) { + pending = true; + return; + } + + pending = false; + inFlight = true; + void Promise.resolve(refresh()) + .catch(() => { + // Callers own user-facing error state. + }) + .finally(() => { + inFlight = false; + if (pending) { + run(); + } + }); + }; + + const onVisibilityChange = () => { + if (!document.hidden) { + run(); + } + }; + + if (options.immediate !== false) { + run(); + } + const timer = window.setInterval(run, intervalMs); + document.addEventListener("visibilitychange", onVisibilityChange); + + return () => { + cancelled = true; + window.clearInterval(timer); + document.removeEventListener("visibilitychange", onVisibilityChange); + }; +} diff --git a/src/renderer/types/electron.d.ts b/src/renderer/types/electron.d.ts index 4935b74..3ac4e52 100644 --- a/src/renderer/types/electron.d.ts +++ b/src/renderer/types/electron.d.ts @@ -57,6 +57,8 @@ import type { ProxyCertificateStatus, ProxyNetworkSnapshot, ProxyStatus, + RequestLogDetailRequest, + RequestLogEntry, RequestLogListFilter, RequestLogPage, UsageStatsFilter, @@ -95,6 +97,7 @@ declare global { getProxyCertificateStatus: () => Promise; getProxyNetworkCaptures: () => Promise; getProxyStatus: () => Promise; + getRequestLogDetail: (request: RequestLogDetailRequest) => Promise; getRequestLogs: (filter?: RequestLogListFilter) => Promise; getUpdateStatus: () => Promise; getUsageStats: (range?: UsageStatsRange, filter?: UsageStatsFilter) => Promise; diff --git a/src/server/gateway/model-catalog.ts b/src/server/gateway/model-catalog.ts index a176913..5220e95 100644 --- a/src/server/gateway/model-catalog.ts +++ b/src/server/gateway/model-catalog.ts @@ -1,5 +1,4 @@ -import { existsSync, readFileSync } from "node:fs"; -import { resolve as pathResolve } from "node:path"; +import { loadModelCatalogPayload } from "../../main/model-catalog-file"; const claudeCodeDefaultContextTokens = 200_000; let modelCatalogIndex: ModelCatalogIndex | undefined; @@ -94,17 +93,14 @@ function loadModelCatalogIndex(): ModelCatalogIndex { return modelCatalogIndex; } - for (const candidate of modelCatalogPathCandidates()) { - if (!existsSync(candidate)) { - continue; - } - try { - const parsed = JSON.parse(readFileSync(candidate, "utf8")) as unknown; - modelCatalogIndex = buildModelCatalogIndex(parsed, candidate); + try { + const loaded = loadModelCatalogPayload(); + if (loaded) { + modelCatalogIndex = buildModelCatalogIndex(loaded.payload, loaded.loadedFrom); return modelCatalogIndex; - } catch (error) { - console.warn(`Failed to load model catalog from ${candidate}:`, error); } + } catch (error) { + console.warn("Failed to load model catalog:", error); } modelCatalogIndex = { @@ -114,17 +110,6 @@ function loadModelCatalogIndex(): ModelCatalogIndex { return modelCatalogIndex; } -function modelCatalogPathCandidates(): string[] { - return uniqueStrings([ - process.env.CCR_MODEL_CATALOG_PATH?.trim() || "", - process.env.CCR_MODELS_JSON_PATH?.trim() || "", - pathResolve(process.cwd(), "models.json"), - pathResolve(__dirname, "..", "models.json"), - pathResolve(__dirname, "..", "assets", "models.json"), - pathResolve(__dirname, "..", "..", "..", "models.json") - ]); -} - function buildModelCatalogIndex(payload: unknown, loadedFrom: string): ModelCatalogIndex { const byKey = new Map(); const byModelKey = new Map(); diff --git a/src/shared/app.ts b/src/shared/app.ts index 5d2ea3a..2e83828 100644 --- a/src/shared/app.ts +++ b/src/shared/app.ts @@ -1436,6 +1436,10 @@ export type RequestLogListFilter = { status?: RequestLogStatusFilter; }; +export type RequestLogDetailRequest = { + id: number; +}; + export type RequestLogBody = ProxyNetworkBody; export type RequestLogRetryAttempt = { diff --git a/src/shared/default-config.ts b/src/shared/default-config.ts new file mode 100644 index 0000000..1e01a9b --- /dev/null +++ b/src/shared/default-config.ts @@ -0,0 +1,166 @@ +import { + CLAUDE_CODE_DEFAULT_ENV, + DEFAULT_OVERVIEW_WIDGETS, + DEFAULT_TRAY_COMPONENT_VARIANTS, + DEFAULT_TRAY_WIDGETS, + DEFAULT_TRAY_WINDOW_MODULES, + type AppConfig, + type ProxyRouteTarget +} from "./app"; + +export const DEFAULT_PROXY_TARGETS: ProxyRouteTarget[] = [ + { host: "api.anthropic.com", paths: ["/v1/messages", "/v1/messages/count_tokens"] }, + { host: "api.openai.com", paths: ["/v1/chat/completions", "/v1/responses", "/v1/models"] }, + { host: "generativelanguage.googleapis.com", paths: ["/v1beta/models", "/v1/models"] }, + { host: "openrouter.ai", paths: ["/api/v1/chat/completions", "/api/v1/responses", "/api/v1/models"] }, + { host: "api.deepseek.com", paths: ["/chat/completions", "/v1/chat/completions", "/models", "/v1/models"] }, + { host: "api.mistral.ai", paths: ["/v1/chat/completions", "/v1/models"] } +]; + +export type DefaultAppConfigOptions = { + coreHost?: string; + generatedConfigFile: string; +}; + +export function createDefaultAppConfig(options: DefaultAppConfigOptions): AppConfig { + const coreHost = options.coreHost ?? "127.0.0.1"; + return { + APIKEY: "", + APIKEYS: [], + API_TIMEOUT_MS: 600000, + CUSTOM_ROUTER_PATH: "", + HOST: "127.0.0.1", + PORT: 3456, + Providers: [], + Router: { + fallback: { + mode: "off", + models: [], + retryCount: 1 + }, + longContextThreshold: 200000, + rules: [] + }, + agent: { + mcpServers: [] + }, + autoStart: false, + botConfigs: [], + botGateway: { + acknowledgeEvents: false, + args: [], + authType: "", + autoStartIntegration: true, + command: "", + createIntegration: false, + credentials: {}, + cwd: "", + enabled: false, + forwardAllAgentMessages: true, + handoff: { + enabled: false, + idleSeconds: 30, + phoneBluetoothTargets: [], + phoneWifiTargets: [], + screenLock: true, + userIdle: true + }, + integrationConfig: {}, + integrationId: "", + platform: "none", + pollIntervalMs: 2000, + requestTimeoutMs: 600000, + sourceDir: "", + startupTimeoutMs: 10000, + stateDir: "", + tenantId: "ccr" + }, + gateway: { + coreHost, + corePort: 3457, + enabled: true, + generatedConfigFile: options.generatedConfigFile, + host: "127.0.0.1", + port: 3456 + }, + observability: { + agentAnalysis: false, + requestLogs: false + }, + preferredProvider: "", + plugins: [], + profile: { + claudeCode: { + enabled: true, + model: "", + settingsFile: "~/.claude/settings.json", + smallFastModel: "" + }, + codex: { + cliMiddleware: true, + codexCliPath: "", + codexHome: "", + configFormat: "separate_profile_files", + configFile: "~/.codex/config.toml", + enabled: true, + model: "", + providerId: "claude-code-router", + providerName: "Claude Code Router", + showAllSessions: false + }, + enabled: true, + profiles: [ + { + agent: "claude-code", + enabled: true, + env: { ...CLAUDE_CODE_DEFAULT_ENV }, + id: "default-claude-code", + model: "", + name: "Claude Code", + scope: "global", + settingsFile: "~/.claude/settings.json", + smallFastModel: "", + surface: "auto" + }, + { + agent: "codex", + cliMiddleware: true, + codexCliPath: "", + codexHome: "", + configFormat: "separate_profile_files", + configFile: "~/.codex/config.toml", + enabled: true, + env: {}, + id: "default-codex", + model: "", + name: "Codex", + providerId: "claude-code-router", + providerName: "Claude Code Router", + showAllSessions: false, + scope: "global", + surface: "auto" + } + ] + }, + proxy: { + browserMode: true, + captureNetwork: false, + enabled: false, + host: "127.0.0.1", + mode: "gateway", + port: 7890, + systemProxy: false, + targets: DEFAULT_PROXY_TARGETS + }, + providerPlugins: [], + overviewWidgets: DEFAULT_OVERVIEW_WIDGETS, + routerEndpoint: "http://127.0.0.1:3456", + theme: "system", + trayComponentVariants: DEFAULT_TRAY_COMPONENT_VARIANTS, + trayIcon: "random", + trayProgressTargetTokens: 100000, + trayWidgets: DEFAULT_TRAY_WIDGETS, + trayWindowModules: DEFAULT_TRAY_WINDOW_MODULES, + virtualModelProfiles: [] + }; +} diff --git a/src/shared/ipc-channels.ts b/src/shared/ipc-channels.ts index f88003a..1bfe296 100644 --- a/src/shared/ipc-channels.ts +++ b/src/shared/ipc-channels.ts @@ -19,6 +19,7 @@ export const IPC_CHANNELS = { appGetProxyCertificateStatus: "ccr:app:get-proxy-certificate-status", appGetProxyNetworkCaptures: "ccr:app:get-proxy-network-captures", appGetProxyStatus: "ccr:app:get-proxy-status", + appGetRequestLogDetail: "ccr:app:get-request-log-detail", appGetRequestLogs: "ccr:app:get-request-logs", appGetUpdateStatus: "ccr:app:get-update-status", appGetUsageStats: "ccr:app:get-usage-stats", diff --git a/tests/bot-gateway-env.test.mjs b/tests/bot-gateway-env.test.mjs new file mode 100644 index 0000000..ac3e384 --- /dev/null +++ b/tests/bot-gateway-env.test.mjs @@ -0,0 +1,111 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { botGatewayProfileEnv } from "../src/main/bot-gateway-env.ts"; + +function botGateway(overrides = {}) { + return { + acknowledgeEvents: true, + args: ["--stdio"], + authType: "token", + autoStartIntegration: true, + command: "bot-gateway", + createIntegration: true, + credentials: { sendMode: "legacy", token: "secret", webhookUrl: "https://old.example.com" }, + cwd: "/tmp", + enabled: true, + forwardAllAgentMessages: true, + handoff: { + enabled: true, + idleSeconds: 45, + phoneBluetoothTargets: ["bt-phone"], + phoneWifiTargets: ["wifi-phone"], + screenLock: false, + userIdle: true + }, + integrationConfig: { sendMode: "webhook", team: "T1", transport: "http" }, + integrationId: "integration-1", + platform: "slack", + pollIntervalMs: 2500, + requestTimeoutMs: 600000, + sourceDir: "", + startupTimeoutMs: 15000, + stateDir: "", + tenantId: "tenant-1", + ...overrides + }; +} + +const profile = { + agent: "codex", + botConfigId: "saved-bot", + enabled: true, + id: "codex-main", + model: "provider,model", + name: "Codex Main", + surface: "app" +}; + +test("botGatewayProfileEnv disables bot gateway outside app surface", () => { + const env = botGatewayProfileEnv({ botConfigs: [], botGateway: botGateway() }, profile, "cli"); + + assert.deepEqual(env, { + CCR_BOT_GATEWAY_ENABLED: "false", + CODEXL_BOT_GATEWAY_ENABLED: "false" + }); +}); + +test("botGatewayProfileEnv merges saved config and normalizes websocket integration", () => { + const env = botGatewayProfileEnv( + { + botConfigs: [ + { + botGateway: botGateway({ + authType: "appsecret", + credentials: { appId: "app-1", webhookSecret: "drop-me" }, + integrationConfig: { appId: "app-1", transport: "http" }, + platform: "lark", + stateDir: "~/bot-state" + }), + id: "saved-bot", + name: "Saved Bot" + } + ], + botGateway: botGateway({ enabled: false, platform: "none" }) + }, + profile, + "app" + ); + + assert.equal(env.CCR_BOT_GATEWAY_ENABLED, "true"); + assert.equal(env.CCR_BOT_GATEWAY_PLATFORM, "feishu"); + assert.equal(env.CCR_BOT_GATEWAY_AUTH_TYPE, "app_secret"); + assert.equal(env.CCR_BOT_GATEWAY_CREATE_INTEGRATION, "true"); + assert.equal(env.CCR_BOT_GATEWAY_STATE_DIR, `${process.env.HOME}/bot-state`); + assert.equal(env.CCR_BOT_PROFILE_ID, "codex-main"); + assert.equal(env.CCR_BOT_PROFILE_NAME, "Codex Main"); + + const credentials = JSON.parse(env.CCR_BOT_GATEWAY_CREDENTIALS_JSON); + assert.deepEqual(credentials, { appId: "app-1", token: "secret" }); + + const integrationConfig = JSON.parse(env.CCR_BOT_GATEWAY_CONFIG_JSON); + assert.deepEqual(integrationConfig, { appId: "app-1", team: "T1", transport: "websocket" }); +}); + +test("botGatewayProfileEnv disables create integration for QR login platforms", () => { + const env = botGatewayProfileEnv( + { + botConfigs: [], + botGateway: botGateway({ + authType: "qr", + platform: "weixin" + }) + }, + { ...profile, botConfigId: undefined }, + "app" + ); + + assert.equal(env.CCR_BOT_GATEWAY_PLATFORM, "weixin-ilink"); + assert.equal(env.CCR_BOT_GATEWAY_AUTH_TYPE, "qr_login"); + assert.equal(env.CCR_BOT_GATEWAY_CREATE_INTEGRATION, "false"); + assert.equal(JSON.parse(env.CCR_BOT_GATEWAY_CONFIG_JSON).transport, "websocket"); +}); diff --git a/tests/deep-link.test.mjs b/tests/deep-link.test.mjs new file mode 100644 index 0000000..2a9c63b --- /dev/null +++ b/tests/deep-link.test.mjs @@ -0,0 +1,126 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { + createProviderDeepLinkRequest, + isAppDeepLinkUrl, + parseProviderDeepLinkPayload, + parseProviderManifestDeepLinkPayload, + parseProviderManifestPayload +} from "../src/shared/deep-link.ts"; + +function base64UrlJson(value) { + return Buffer.from(JSON.stringify(value), "utf8") + .toString("base64") + .replace(/\+/g, "-") + .replace(/\//g, "_") + .replace(/=+$/, ""); +} + +test("parseProviderDeepLinkPayload reads payload JSON, models, display names, and usage account mapping", () => { + const payload = { + account: { + connectors: { + auth: "provider-api-key", + endpoint: "https://usage.example.com/balance", + mapping: { meters: [{ id: "balance", kind: "balance", remaining: "$.balance" }] }, + type: "http-json" + }, + enabled: true, + refreshIntervalMs: 60000 + }, + api_key: "sk-test", + base_url: "https://api.example.com/v1", + fetch_usage: true, + model_display_names: { + "model-a": "Model A" + }, + models: [ + { displayName: "Model B", id: "model-b" }, + "model-a,model-c" + ], + name: "Example AI", + protocol: "openai_chat_completions", + source: "https://example.com/install" + }; + + const parsed = parseProviderDeepLinkPayload(`ccr://provider?payload=${base64UrlJson(payload)}`); + + assert.equal(parsed.name, "Example AI"); + assert.equal(parsed.baseUrl, "https://api.example.com/v1"); + assert.equal(parsed.apiKey, "sk-test"); + assert.equal(parsed.protocol, "openai_chat_completions"); + assert.deepEqual(parsed.models, ["model-b", "model-a", "model-c"]); + assert.deepEqual(parsed.modelDisplayNames, { + "model-a": "Model A", + "model-b": "Model B" + }); + assert.equal(parsed.account?.enabled, true); + assert.equal(parsed.account?.refreshIntervalMs, 60000); + assert.equal(parsed.account?.connectors?.[0]?.type, "http-json"); +}); + +test("parseProviderDeepLinkPayload builds usage account config from query params", () => { + const usageHeaders = encodeURIComponent(JSON.stringify({ "x-usage": "yes", ignored: 123 })); + const parsed = parseProviderDeepLinkPayload( + [ + "ccr://provider?name=Query%20AI", + "base_url=https%3A%2F%2Fapi.example.com%2Fv1", + "models=model-a%2Cmodel-b", + "models=model-b%0Amodel-c", + "fetch_usage=true", + "usage_url=https%3A%2F%2Fusage.example.com%2Fme", + "usage_method=post", + `usage_headers=${usageHeaders}`, + "balance=%24.balance.remaining", + "balance_unit=CNY", + "subscription=%24.quota.remaining", + "subscription_limit=%24.quota.limit" + ].join("&") + ); + + assert.deepEqual(parsed.models, ["model-a", "model-b", "model-c"]); + const connector = parsed.account?.connectors?.[0]; + assert.equal(connector?.type, "http-json"); + assert.equal(connector?.method, "POST"); + assert.deepEqual(connector?.headers, { "x-usage": "yes" }); + assert.equal(connector?.mapping.meters.length, 2); + assert.equal(connector?.mapping.meters[0].unit, "CNY"); +}); + +test("provider deeplink manifest parsing accepts only HTTPS manifest URLs", () => { + assert.equal(isAppDeepLinkUrl(" ccr://provider?base_url=https://api.example.com "), true); + assert.deepEqual( + parseProviderManifestDeepLinkPayload("ccr://provider?manifest=https%3A%2F%2Fexample.com%2Fccr.json"), + { url: "https://example.com/ccr.json" } + ); + assert.throws( + () => parseProviderManifestDeepLinkPayload("ccr://provider?manifest=http%3A%2F%2Fexample.com%2Fccr.json"), + /must use https/ + ); +}); + +test("createProviderDeepLinkRequest captures parsing errors without throwing", () => { + const request = createProviderDeepLinkRequest("https://example.com/not-ccr", new Date("2026-06-30T00:00:00.000Z")); + + assert.equal(request.rawUrl, "https://example.com/not-ccr"); + assert.equal(request.receivedAt, "2026-06-30T00:00:00.000Z"); + assert.match(request.error ?? "", /Unsupported link protocol/); +}); + +test("parseProviderManifestPayload accepts provider wrappers and source fallback", () => { + const parsed = parseProviderManifestPayload( + { + provider: { + base_url: "https://api.example.com/v1", + models: [{ display_name: "Display Model", id: "display-model" }], + name: "Manifest AI" + } + }, + "https://example.com/manifest.json" + ); + + assert.equal(parsed.name, "Manifest AI"); + assert.equal(parsed.source, "https://example.com/manifest.json"); + assert.deepEqual(parsed.models, ["display-model"]); + assert.deepEqual(parsed.modelDisplayNames, { "display-model": "Display Model" }); +}); diff --git a/tests/model-catalog-file.test.mjs b/tests/model-catalog-file.test.mjs new file mode 100644 index 0000000..8a93289 --- /dev/null +++ b/tests/model-catalog-file.test.mjs @@ -0,0 +1,61 @@ +import assert from "node:assert/strict"; +import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import path from "node:path"; +import test from "node:test"; +import { loadModelCatalogPayload, modelCatalogPathCandidates } from "../src/main/model-catalog-file.ts"; + +test("modelCatalogPathCandidates prefers env paths and removes duplicates", () => { + const previousCatalogPath = process.env.CCR_MODEL_CATALOG_PATH; + const previousModelsPath = process.env.CCR_MODELS_JSON_PATH; + try { + process.env.CCR_MODEL_CATALOG_PATH = "/tmp/ccr-models.json"; + process.env.CCR_MODELS_JSON_PATH = "/tmp/ccr-models.json"; + + const candidates = modelCatalogPathCandidates(); + + assert.equal(candidates[0], "/tmp/ccr-models.json"); + assert.equal(candidates.filter((candidate) => candidate === "/tmp/ccr-models.json").length, 1); + assert.ok(candidates.some((candidate) => candidate.endsWith("models.json"))); + } finally { + if (previousCatalogPath === undefined) { + delete process.env.CCR_MODEL_CATALOG_PATH; + } else { + process.env.CCR_MODEL_CATALOG_PATH = previousCatalogPath; + } + if (previousModelsPath === undefined) { + delete process.env.CCR_MODELS_JSON_PATH; + } else { + process.env.CCR_MODELS_JSON_PATH = previousModelsPath; + } + } +}); + +test("loadModelCatalogPayload reads the first configured existing catalog", () => { + const previousCatalogPath = process.env.CCR_MODEL_CATALOG_PATH; + const previousModelsPath = process.env.CCR_MODELS_JSON_PATH; + const dir = mkdtempSync(path.join(tmpdir(), "ccr-model-catalog-test-")); + try { + const catalogFile = path.join(dir, "models.json"); + writeFileSync(catalogFile, JSON.stringify({ models: [{ id: "test-model" }] }), "utf8"); + process.env.CCR_MODEL_CATALOG_PATH = path.join(dir, "missing.json"); + process.env.CCR_MODELS_JSON_PATH = catalogFile; + + const loaded = loadModelCatalogPayload(); + + assert.equal(loaded?.loadedFrom, catalogFile); + assert.deepEqual(loaded?.payload, { models: [{ id: "test-model" }] }); + } finally { + rmSync(dir, { force: true, recursive: true }); + if (previousCatalogPath === undefined) { + delete process.env.CCR_MODEL_CATALOG_PATH; + } else { + process.env.CCR_MODEL_CATALOG_PATH = previousCatalogPath; + } + if (previousModelsPath === undefined) { + delete process.env.CCR_MODELS_JSON_PATH; + } else { + process.env.CCR_MODELS_JSON_PATH = previousModelsPath; + } + } +}); diff --git a/tests/profile-launch-core.test.mjs b/tests/profile-launch-core.test.mjs new file mode 100644 index 0000000..2659aa4 --- /dev/null +++ b/tests/profile-launch-core.test.mjs @@ -0,0 +1,104 @@ +import assert from "node:assert/strict"; +import path from "node:path"; +import test from "node:test"; +import { + buildProfileLaunchPlan, + ccrManagedProfileDir, + findProfileForOpen, + profileOpenCommand, + profileOpenSurfaces, + resolveClaudeCodeSettingsFile, + resolveCodexConfigFile, + resolveProfileOpenSurface +} from "../src/main/profile-launch-core.ts"; + +const claudeProfile = { + agent: "claude-code", + enabled: true, + id: "claude-main", + model: "provider,model", + name: "Claude Main", + scope: "ccr", + surface: "auto" +}; + +const codexProfile = { + agent: "codex", + enabled: true, + id: "codex-main", + model: "provider,model", + name: "Codex Main", + providerId: "openai-codex", + scope: "ccr", + surface: "auto" +}; + +test("findProfileForOpen resolves enabled profiles and reports ambiguous names", () => { + const config = { + profile: { + profiles: [ + claudeProfile, + { ...claudeProfile, enabled: false, id: "disabled", name: "Disabled" }, + { ...codexProfile, id: "duplicate-a", name: "Duplicate Name" }, + { ...codexProfile, id: "duplicate-b", name: "duplicate name" } + ] + } + }; + + assert.equal(findProfileForOpen(config, "claude-main").id, "claude-main"); + assert.equal(findProfileForOpen(config, "claude main").id, "claude-main"); + assert.throws(() => findProfileForOpen(config, "duplicate name"), /ambiguous/); + assert.throws(() => findProfileForOpen(config, "Disabled"), /not found or is disabled/); +}); + +test("profile open surfaces enforce agent capabilities", () => { + assert.deepEqual(profileOpenSurfaces(claudeProfile), ["cli", "app"]); + assert.deepEqual(profileOpenSurfaces({ ...claudeProfile, surface: "cli" }), ["cli"]); + assert.deepEqual(profileOpenSurfaces({ ...codexProfile, agent: "zcode" }), ["app"]); + assert.equal(resolveProfileOpenSurface(codexProfile, "app"), "app"); + assert.throws(() => resolveProfileOpenSurface({ ...claudeProfile, surface: "cli" }, "app"), /does not support APP/); +}); + +test("buildProfileLaunchPlan creates CCR-managed launcher paths", () => { + const configDir = path.join(path.sep, "tmp", "ccr-config"); + const codexPlan = buildProfileLaunchPlan(configDir, codexProfile, "app"); + const claudePlan = buildProfileLaunchPlan(configDir, claudeProfile, "cli", ["--debug"]); + + assert.equal(codexPlan.surface, "app"); + assert.deepEqual(codexPlan.args, ["app"]); + assert.equal(path.basename(codexPlan.command), process.platform === "win32" ? "ccr-codex-cli-stdio-codex-main.cmd" : "ccr-codex-cli-stdio-codex-main"); + assert.equal(codexPlan.env.CCR_PROFILE_SURFACE, "app"); + + assert.equal(claudePlan.surface, "cli"); + assert.deepEqual(claudePlan.args, ["--debug"]); + assert.equal(path.basename(claudePlan.command), process.platform === "win32" ? "ccr-claude-code-wrapper-claude-main.cmd" : "ccr-claude-code-wrapper-claude-main"); + assert.equal(claudePlan.env.CCR_PROFILE_SURFACE, "cli"); + assert.match(claudePlan.env.CLAUDE_CONFIG_DIR, /claude$/); + + assert.throws(() => buildProfileLaunchPlan(configDir, claudeProfile, "app"), /Claude App opening/); +}); + +test("profile config paths honor CCR, custom, and global scopes", () => { + const configDir = path.join(path.sep, "tmp", "ccr-config"); + const customProfile = { ...codexProfile, id: "Custom Profile", scope: "custom" }; + const globalCodex = { ...codexProfile, codexHome: "~/codex-home", scope: "global" }; + + assert.equal( + ccrManagedProfileDir(configDir, customProfile), + path.join(configDir, "profiles", "custom-profile", "custom") + ); + assert.equal( + resolveClaudeCodeSettingsFile(configDir, claudeProfile), + path.join(configDir, "profiles", "claude-main", "claude", "settings.json") + ); + assert.equal( + resolveCodexConfigFile(configDir, customProfile), + path.join(configDir, "profiles", "custom-profile", "custom", "codex", "config.toml") + ); + assert.equal(resolveCodexConfigFile(configDir, globalCodex), path.join(process.env.HOME, "codex-home", "config.toml")); +}); + +test("profileOpenCommand quotes profile references for shell usage", () => { + assert.match(profileOpenCommand(claudeProfile, "cli", "ccr", "Claude Main"), /Claude/); + assert.match(profileOpenCommand(claudeProfile, "cli", "ccr", "Claude Main"), /Main/); +}); diff --git a/tests/provider-preset-utils.test.mjs b/tests/provider-preset-utils.test.mjs new file mode 100644 index 0000000..7ee4b50 --- /dev/null +++ b/tests/provider-preset-utils.test.mjs @@ -0,0 +1,83 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { + findProviderPresetByBaseUrlInList, + findProviderPresetByIdentityInList, + providerApiKeySafetyIssueInList, + providerEndpointCanReceiveProviderApiKeyInList, + providerIdentitySafetyIssueInList, + providerPresetMatchesBaseUrl +} from "../src/shared/provider-preset-utils.ts"; + +const openAiPreset = { + aliases: ["OpenAI", "ChatGPT"], + endpoints: [{ baseUrl: "https://api.openai.com/v1", protocols: ["openai_chat_completions"] }], + id: "openai", + name: "OpenAI", + officialApiKeyPatterns: [{ source: "^sk-openai-" }] +}; + +const anthropicPreset = { + aliases: ["Claude"], + endpoints: [{ baseUrl: "https://api.anthropic.com", protocols: ["anthropic_messages"] }], + id: "anthropic", + name: "Anthropic", + officialApiKeyPatterns: [{ source: "^sk-ant-" }] +}; + +const presets = [openAiPreset, anthropicPreset]; + +test("provider preset matching accepts endpoint subpaths but rejects different hosts", () => { + assert.equal(providerPresetMatchesBaseUrl(openAiPreset, "https://api.openai.com/v1/chat/completions"), true); + assert.equal(providerPresetMatchesBaseUrl(openAiPreset, "https://api.openai.com"), true); + assert.equal(providerPresetMatchesBaseUrl(openAiPreset, "https://proxy.example.com/v1"), false); + assert.equal(findProviderPresetByBaseUrlInList(presets, "api.anthropic.com/v1/messages")?.id, "anthropic"); +}); + +test("provider identity lookup normalizes aliases and punctuation", () => { + assert.equal(findProviderPresetByIdentityInList(presets, "my ChatGPT gateway")?.id, "openai"); + assert.equal(findProviderPresetByIdentityInList(presets, "Claude Provider")?.id, "anthropic"); +}); + +test("provider identity safety allows loopback but warns on branded third-party endpoints", () => { + assert.equal( + providerIdentitySafetyIssueInList(presets, { + baseUrl: "http://127.0.0.1:3456/v1", + name: "OpenAI local test" + }), + undefined + ); + assert.match( + providerIdentitySafetyIssueInList(presets, { + baseUrl: "https://proxy.example.com/v1", + name: "OpenAI proxy" + })?.message ?? "", + /Provider identity looks like OpenAI/ + ); +}); + +test("provider API key safety blocks official-looking keys on untrusted endpoints", () => { + assert.match( + providerApiKeySafetyIssueInList(presets, { + apiKey: "sk-openai-test", + baseUrl: "https://proxy.example.com/v1", + name: "neutral proxy" + })?.message ?? "", + /official OpenAI key/ + ); + assert.equal( + providerApiKeySafetyIssueInList(presets, { + apiKey: "sk-openai-test", + baseUrl: "https://api.openai.com/v1" + }), + undefined + ); + assert.match( + providerEndpointCanReceiveProviderApiKeyInList(presets, { + apiKey: "sk-ant-test", + endpoint: "https://proxy.example.com/anthropic", + providerName: "Anthropic" + })?.message ?? "", + /official Anthropic key/ + ); +}); diff --git a/tests/provider-url.test.mjs b/tests/provider-url.test.mjs new file mode 100644 index 0000000..b776876 --- /dev/null +++ b/tests/provider-url.test.mjs @@ -0,0 +1,40 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { + normalizeProviderBaseUrl, + parseProviderBaseUrl, + providerBaseUrlForProtocol, + providerUrlWithDefaultScheme +} from "../src/shared/provider-url.ts"; + +test("provider URL parsing strips endpoint paths and unsafe URL parts", () => { + const parsed = parseProviderBaseUrl("https://user:secret@api.example.com/v1/chat/completions?token=secret#section"); + + assert.equal(parsed.normalizedInputBaseUrl, "https://api.example.com/v1"); + assert.equal(parsed.rootBaseUrl, "https://api.example.com"); + assert.equal(providerBaseUrlForProtocol(parsed, "openai_chat_completions"), "https://api.example.com/v1"); + assert.equal(providerBaseUrlForProtocol(parsed, "openai_responses"), "https://api.example.com/v1"); + assert.equal(providerBaseUrlForProtocol(parsed, "anthropic_messages"), "https://api.example.com"); + assert.equal(providerBaseUrlForProtocol(parsed, "gemini_generate_content"), "https://api.example.com"); +}); + +test("provider URL parsing handles local and Gemini endpoint variants", () => { + const parsed = parseProviderBaseUrl("localhost:8787/v1beta/models/gemini-2.5-pro:generateContent"); + + assert.equal(parsed.normalizedInputBaseUrl, "http://localhost:8787/v1beta"); + assert.equal(parsed.rootBaseUrl, "http://localhost:8787"); + assert.equal(parsed.geminiBaseUrl, "http://localhost:8787"); +}); + +test("provider URL normalization chooses protocol-specific bases", () => { + assert.equal(providerUrlWithDefaultScheme("127.0.0.1:3456/v1"), "http://127.0.0.1:3456/v1"); + assert.equal(providerUrlWithDefaultScheme("api.example.com/v1"), "https://api.example.com/v1"); + assert.equal( + normalizeProviderBaseUrl("api.example.com/v1/messages", "anthropic_messages"), + "https://api.example.com" + ); + assert.equal( + normalizeProviderBaseUrl("api.example.com/v1/chat/completions", "openai_chat_completions"), + "https://api.example.com/v1" + ); +}); diff --git a/tests/request-log-store.test.mjs b/tests/request-log-store.test.mjs new file mode 100644 index 0000000..30a9cb1 --- /dev/null +++ b/tests/request-log-store.test.mjs @@ -0,0 +1,275 @@ +import assert from "node:assert/strict"; +import { mkdtempSync, rmSync } from "node:fs"; +import { tmpdir } from "node:os"; +import path from "node:path"; +import test from "node:test"; +import { RequestLogStore } from "../src/main/request-log-store.ts"; + +test("RequestLogStore keeps list rows lightweight and detail rows complete", async () => { + const dir = mkdtempSync(path.join(tmpdir(), "ccr-request-log-test-")); + try { + const store = new RequestLogStore(path.join(dir, "request-logs.sqlite")); + const body = JSON.stringify({ messages: [{ content: "hello", role: "user" }], model: "request-model" }); + const response = JSON.stringify({ + model: "response-model", + usage: { + input_tokens: 3, + output_tokens: 4, + total_tokens: 7 + } + }); + + await store.record({ + completedAt: new Date().toISOString(), + durationMs: 42, + method: "POST", + path: "/v1/messages", + providerName: "test-provider", + requestBody: Buffer.from(body, "utf8"), + requestHeaders: { "content-type": "application/json" }, + requestId: "request-log-test", + responseBodyText: response, + responseHeaders: { "content-type": "application/json" }, + startedAt: new Date().toISOString(), + statusCode: 200, + url: "http://127.0.0.1:3456/v1/messages" + }); + + const page = await store.list({ pageSize: 25 }); + assert.equal(page.items.length, 1); + assert.equal(page.items[0].requestBody.text, ""); + assert.equal(page.items[0].responseBody?.text, ""); + assert.equal(page.items[0].requestBody.sizeBytes, Buffer.byteLength(body)); + assert.equal(page.items[0].responseBody?.sizeBytes, Buffer.byteLength(response)); + + const detail = await store.getDetail({ id: page.items[0].id }); + assert.ok(detail); + assert.match(detail.requestBody.text, /request-model/); + assert.match(detail.responseBody?.text ?? "", /response-model/); + } finally { + rmSync(dir, { force: true, recursive: true }); + } +}); + +test("RequestLogStore redacts secrets and records CCR metadata", async () => { + const dir = mkdtempSync(path.join(tmpdir(), "ccr-request-log-metadata-test-")); + try { + const store = new RequestLogStore(path.join(dir, "request-logs.sqlite")); + const startedAt = new Date().toISOString(); + + await store.record({ + completedAt: startedAt, + durationMs: 75, + method: "POST", + path: "/v1/chat/completions", + providerName: "openai-like", + providerProtocol: "openai_chat_completions", + requestBody: Buffer.from(JSON.stringify({ model: "gpt-test", stream: true }), "utf8"), + requestHeaders: { + accept: "text/event-stream", + authorization: "Bearer request-secret", + cookie: "session=request-secret", + "content-type": "application/json", + "x-ccr-provider-credential-chain": "cred-a, cred-b", + "x-ccr-provider-credential-id": "cred-a" + }, + requestId: "request-log-metadata-test", + responseBodyText: "", + responseHeaders: { + "content-type": "text/event-stream", + "x-api-key": "response-secret", + "x-ccr-provider-credential-saturated": "true", + "x-gateway-billing-cache-read-tokens": "10", + "x-gateway-billing-input-tokens": "100", + "x-gateway-billing-output-tokens": "20", + "x-gateway-billing-total-tokens": "130" + }, + startedAt, + statusCode: 200, + url: "http://127.0.0.1:3456/v1/chat/completions" + }); + + const page = await store.list({ pageSize: 25 }); + const detail = await store.getDetail({ id: page.items[0].id }); + + assert.ok(detail); + assert.equal(detail.requestHeaders.authorization, "[redacted]"); + assert.equal(detail.requestHeaders.cookie, "[redacted]"); + assert.equal(detail.responseHeaders["x-api-key"], "[redacted]"); + assert.equal(detail.credentialId, "cred-a"); + assert.deepEqual(detail.credentialChain, ["cred-a", "cred-b"]); + assert.equal(detail.credentialSaturated, true); + assert.equal(detail.isStream, true); + assert.equal(detail.inputTokens, 90); + assert.equal(detail.cacheReadTokens, 10); + assert.equal(detail.outputTokens, 20); + assert.equal(detail.totalTokens, 130); + } finally { + rmSync(dir, { force: true, recursive: true }); + } +}); + +test("RequestLogStore applies raw trace updates to existing request logs", async () => { + const dir = mkdtempSync(path.join(tmpdir(), "ccr-request-log-raw-trace-test-")); + try { + const store = new RequestLogStore(path.join(dir, "request-logs.sqlite")); + const startedAt = new Date().toISOString(); + const errorStream = [ + "event: error", + 'data: {"error":{"type":"rate_limit_error","message":"quota exceeded"}}', + "" + ].join("\n"); + + await store.record({ + completedAt: startedAt, + durationMs: 20, + method: "POST", + path: "/v1/messages", + providerName: "before-provider", + requestBody: Buffer.from(JSON.stringify({ model: "before-model" }), "utf8"), + requestHeaders: { "content-type": "application/json" }, + requestId: "raw-trace-request", + responseBodyText: "{}", + responseHeaders: { "content-type": "application/json" }, + startedAt, + statusCode: 200, + url: "http://127.0.0.1:3456/v1/messages" + }); + + const applied = await store.updateFromRawTrace({ + isStream: true, + model: "trace-model", + provider: "trace-provider", + requestHeaders: { "x-client-name": "codex-cli" }, + requestId: "raw-trace-request", + responseBodyContentType: "text/event-stream", + responseBodyText: errorStream, + responseHeaders: { "content-type": "text/event-stream" }, + statusCode: 200 + }); + + assert.equal(applied, true); + const page = await store.list({ query: "quota exceeded" }); + assert.equal(page.items.length, 1); + const detail = await store.getDetail({ id: page.items[0].id }); + + assert.ok(detail); + assert.equal(detail.model, "trace-model"); + assert.equal(detail.provider, "trace-provider"); + assert.equal(detail.ok, false); + assert.equal(detail.isStream, true); + assert.match(detail.error, /rate_limit_error: quota exceeded/); + assert.match(detail.responseBody?.text ?? "", /quota exceeded/); + assert.equal(detail.requestHeaders["x-client-name"], "codex-cli"); + } finally { + rmSync(dir, { force: true, recursive: true }); + } +}); + +test("RequestLogStore analyzes agent sessions and exposes trace payloads", async () => { + const dir = mkdtempSync(path.join(tmpdir(), "ccr-request-log-agent-test-")); + try { + const store = new RequestLogStore(path.join(dir, "request-logs.sqlite")); + const startedAt = new Date(Date.now() - 1000).toISOString(); + const completedAt = new Date().toISOString(); + const requestBody = { + messages: [ + { content: "inspect repo", role: "user" }, + { + content: JSON.stringify({ ok: true, files: ["README.md"] }), + role: "tool", + tool_call_id: "call-read" + } + ], + model: "gpt-test", + session_id: "session-1" + }; + const responseBody = { + choices: [ + { + message: { + role: "assistant", + tool_calls: [ + { + function: { + arguments: JSON.stringify({ path: "README.md" }), + name: "read_file" + }, + id: "call-read", + type: "function" + } + ] + } + } + ], + model: "gpt-test", + usage: { + completion_tokens: 3, + prompt_tokens: 7, + total_tokens: 10 + } + }; + + await store.record({ + completedAt, + durationMs: 150, + method: "POST", + path: "/v1/chat/completions", + providerName: "test-provider", + providerProtocol: "openai_chat_completions", + requestBody: Buffer.from(JSON.stringify(requestBody), "utf8"), + requestHeaders: { + "content-type": "application/json", + "user-agent": "openai-codex test", + "x-ccr-route-reason": "default", + "x-codex-session-id": "session-1" + }, + requestId: "agent-request-1", + responseBodyText: JSON.stringify(responseBody), + responseHeaders: { "content-type": "application/json" }, + startedAt, + statusCode: 200, + url: "http://127.0.0.1:3456/v1/chat/completions" + }); + + const page = await store.list({ pageSize: 25 }); + const detail = await store.getDetail({ id: page.items[0].id }); + assert.ok(detail); + + const analysis = await store.analyze({ range: "30d" }); + assert.equal(analysis.scannedRequestCount, 1); + assert.equal(analysis.totals.requestCount, 1); + assert.equal(analysis.agents[0]?.agent, "codex"); + assert.equal(analysis.sessions[0]?.id, "session-1"); + assert.equal(analysis.tools[0]?.name, "read_file"); + + const selected = await store.analyze({ + range: "30d", + sessionAgent: "codex", + sessionId: "session-1" + }); + assert.equal(selected.selectedSession?.trace.toolRunCount, 1); + assert.equal(selected.selectedSession?.trace.llmRunCount, 1); + assert.equal(selected.selectedSession?.trace.runs.some((run) => run.toolName === "read_file"), true); + + const inputPayload = await store.getTracePayload({ + callId: "call-read", + part: "tool-input", + requestLogId: detail.id + }); + assert.equal(inputPayload.found, true); + assert.equal(inputPayload.kind, "json"); + assert.match(inputPayload.content, /README\.md/); + + const resultPayload = await store.getTracePayload({ + callId: "call-read", + part: "tool-result", + requestLogId: detail.id + }); + assert.equal(resultPayload.found, true); + assert.equal(resultPayload.kind, "json"); + assert.match(resultPayload.content, /files/); + } finally { + rmSync(dir, { force: true, recursive: true }); + } +}); diff --git a/tests/usage-activity.test.mjs b/tests/usage-activity.test.mjs new file mode 100644 index 0000000..a3c0e2a --- /dev/null +++ b/tests/usage-activity.test.mjs @@ -0,0 +1,34 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { buildTokenActivity, activityDateKey } from "../src/renderer/lib/usage-activity.ts"; + +test("buildTokenActivity summarizes observed token days and streaks", () => { + const summary = buildTokenActivity( + [ + { bucket: "2026-06-02", totalTokens: 10 }, + { bucket: "2026-06-03", totalTokens: 30 }, + { bucket: "2026-06-04", totalTokens: 60 }, + { bucket: "2026-06-05", totalTokens: -20 }, + { bucket: "not-a-date", totalTokens: 999 } + ], + { minWeeks: 2 } + ); + + assert.equal(summary.totalTokens, 100); + assert.equal(summary.activeDays, 3); + assert.equal(summary.dayCount, 4); + assert.equal(summary.longestStreak, 3); + assert.equal(summary.maxTokens, 60); + assert.equal(summary.weekCount, 2); + assert.equal(summary.cells.length, 14); + + const cellsByDate = new Map(summary.cells.map((cell) => [cell.dateKey, cell])); + assert.equal(cellsByDate.get("2026-06-02")?.intensity, 1); + assert.equal(cellsByDate.get("2026-06-03")?.intensity, 3); + assert.equal(cellsByDate.get("2026-06-04")?.intensity, 4); + assert.equal(cellsByDate.get("2026-06-05")?.intensity, 0); +}); + +test("activityDateKey formats local calendar dates", () => { + assert.equal(activityDateKey(new Date(2026, 0, 5, 14, 30)), "2026-01-05"); +}); diff --git a/tests/usage-normalization.test.mjs b/tests/usage-normalization.test.mjs new file mode 100644 index 0000000..65dfc03 --- /dev/null +++ b/tests/usage-normalization.test.mjs @@ -0,0 +1,56 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { normalizeUsageInputTokens } from "../src/main/usage-normalization.ts"; + +test("normalizeUsageInputTokens subtracts cache tokens for OpenAI-compatible protocols", () => { + const usage = normalizeUsageInputTokens( + { + cacheReadTokens: 20, + cacheWriteTokens: 5, + inputTokens: 100, + outputTokens: 12 + }, + { providerProtocol: "openai_chat_completions" } + ); + + assert.deepEqual(usage, { + cacheReadTokens: 20, + cacheWriteTokens: 5, + inputTokens: 75, + outputTokens: 12 + }); +}); + +test("normalizeUsageInputTokens keeps Anthropic input tokens unchanged", () => { + const usage = normalizeUsageInputTokens( + { + cacheReadTokens: 20, + cacheWriteTokens: 5, + inputTokens: 100 + }, + { providerProtocol: "anthropic_messages" } + ); + + assert.deepEqual(usage, { + cacheReadTokens: 20, + cacheWriteTokens: 5, + inputTokens: 100 + }); +}); + +test("normalizeUsageInputTokens falls back to path and usage hints", () => { + assert.equal( + normalizeUsageInputTokens( + { cacheReadTokens: 8, inputTokens: 50 }, + { path: "/v1/responses" } + )?.inputTokens, + 42 + ); + assert.equal( + normalizeUsageInputTokens( + { cacheReadTokens: 8, inputTokens: 50 }, + { usageHint: { inputIncludesCacheTokens: false } } + )?.inputTokens, + 50 + ); +}); diff --git a/tests/usage-store.test.mjs b/tests/usage-store.test.mjs new file mode 100644 index 0000000..0cae4ff --- /dev/null +++ b/tests/usage-store.test.mjs @@ -0,0 +1,105 @@ +import assert from "node:assert/strict"; +import { mkdtempSync, rmSync } from "node:fs"; +import { tmpdir } from "node:os"; +import path from "node:path"; +import test from "node:test"; +import { UsageStore } from "../src/main/usage-store.ts"; + +test("UsageStore aggregates stats in SQLite without loading all events", async () => { + const dir = mkdtempSync(path.join(tmpdir(), "ccr-usage-test-")); + try { + const store = new UsageStore(path.join(dir, "usage.sqlite")); + const now = new Date(); + const earlier = new Date(now.getTime() - 60_000); + + await store.record({ + createdAt: earlier.toISOString(), + durationMs: 120, + method: "POST", + model: "alpha-model", + path: "/v1/messages", + provider: "alpha", + requestId: "req-1", + statusCode: 200, + usage: { + cacheReadTokens: 2, + inputTokens: 10, + outputTokens: 5, + totalTokens: 17 + } + }); + await store.record({ + createdAt: now.toISOString(), + durationMs: 80, + method: "POST", + model: "beta-model", + path: "/v1/messages", + provider: "beta", + requestId: "req-2", + statusCode: 500, + usage: { + inputTokens: 4, + outputTokens: 6 + } + }); + + const stats = await store.getStats("30d", { includeProxy: true }); + assert.equal(stats.totals.requestCount, 2); + assert.equal(stats.totals.errorCount, 1); + assert.equal(stats.totals.totalTokens, 27); + assert.equal(stats.totals.inputTokens, 14); + assert.equal(stats.totals.outputTokens, 11); + assert.equal(stats.recentRequests.length, 2); + assert.equal(stats.models[0]?.requestCount, 1); + } finally { + rmSync(dir, { force: true, recursive: true }); + } +}); + +test("UsageStore excludes proxy rows by default and includes them on request", async () => { + const dir = mkdtempSync(path.join(tmpdir(), "ccr-usage-proxy-test-")); + try { + const store = new UsageStore(path.join(dir, "usage.sqlite")); + const createdAt = new Date().toISOString(); + + await store.record({ + createdAt, + durationMs: 10, + method: "POST", + model: "direct/model-a", + path: "/v1/messages", + requestId: "direct-1", + statusCode: 200, + usage: { + inputTokens: 5, + outputTokens: 7 + } + }); + await store.record({ + createdAt, + durationMs: 10, + method: "POST", + model: "proxy-model", + path: "/v1/messages", + provider: "proxy", + requestId: "proxy-1", + statusCode: 200, + usage: { + inputTokens: 100, + outputTokens: 200 + } + }); + + const defaultStats = await store.getStats("30d"); + assert.equal(defaultStats.totals.requestCount, 1); + assert.equal(defaultStats.totals.totalTokens, 12); + assert.equal(defaultStats.providerModels[0]?.provider, "direct"); + assert.equal(defaultStats.providerModels[0]?.model, "model-a"); + + const withProxy = await store.getStats("30d", { includeProxy: true }); + assert.equal(withProxy.totals.requestCount, 2); + assert.equal(withProxy.totals.totalTokens, 312); + } finally { + rmSync(dir, { force: true, recursive: true }); + } +}); From 20691a8215164a87c58e7fdb7b0e790da2974776 Mon Sep 17 00:00:00 2001 From: musi Date: Tue, 30 Jun 2026 09:55:07 +0800 Subject: [PATCH 12/94] fix tray radial metric arc alignment --- src/renderer/pages/tray/components/widgets.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/renderer/pages/tray/components/widgets.tsx b/src/renderer/pages/tray/components/widgets.tsx index a1c5dc0..c77f295 100644 --- a/src/renderer/pages/tray/components/widgets.tsx +++ b/src/renderer/pages/tray/components/widgets.tsx @@ -566,8 +566,10 @@ export function RadialMetric({ const circumference = 2 * Math.PI * radius; const clamped = Math.max(0, Math.min(1, value)); const span = variant === "ring" ? 1 : variant === "arc" ? 0.78 : 0.55; + const sweepDegrees = span * 360; const dash = circumference * span; - const rotation = variant === "ring" ? -90 : variant === "arc" ? 130 : 160; + const progressDash = dash * clamped; + const rotation = variant === "ring" ? -90 : (540 - sweepDegrees) / 2; return (
@@ -589,7 +591,7 @@ export function RadialMetric({ fill="none" r={radius} stroke={color} - strokeDasharray={`${dash * clamped} ${circumference - dash * clamped}`} + strokeDasharray={`${progressDash} ${circumference - progressDash}`} strokeLinecap="round" strokeWidth="4" transform={`rotate(${rotation} 20 20)`} From 1a8d7cb610381cda685543fb964774bac11ab979 Mon Sep 17 00:00:00 2001 From: musistudio Date: Tue, 30 Jun 2026 10:38:33 +0800 Subject: [PATCH 13/94] Refactor router config handling --- build/run-tests.mjs | 76 ++++-- build/test.mjs | 112 ++++++-- package.json | 2 + tests/{ => main}/bot-gateway-env.test.mjs | 2 +- tests/{ => main}/deep-link.test.mjs | 2 +- tests/{ => main}/model-catalog-file.test.mjs | 2 +- tests/{ => main}/profile-launch-core.test.mjs | 2 +- .../{ => main}/provider-preset-utils.test.mjs | 2 +- tests/{ => main}/provider-url.test.mjs | 2 +- tests/{ => main}/request-log-store.test.mjs | 2 +- tests/{ => main}/usage-normalization.test.mjs | 2 +- tests/{ => main}/usage-store.test.mjs | 2 +- tests/renderer/components.test.tsx | 79 ++++++ tests/renderer/fixtures.ts | 189 +++++++++++++ tests/renderer/overview-components.test.tsx | 71 +++++ tests/renderer/tray-components.test.tsx | 258 ++++++++++++++++++ tests/{ => renderer}/usage-activity.test.mjs | 2 +- 17 files changed, 754 insertions(+), 53 deletions(-) rename tests/{ => main}/bot-gateway-env.test.mjs (97%) rename tests/{ => main}/deep-link.test.mjs (99%) rename tests/{ => main}/model-catalog-file.test.mjs (98%) rename tests/{ => main}/profile-launch-core.test.mjs (98%) rename tests/{ => main}/provider-preset-utils.test.mjs (98%) rename tests/{ => main}/provider-url.test.mjs (97%) rename tests/{ => main}/request-log-store.test.mjs (99%) rename tests/{ => main}/usage-normalization.test.mjs (94%) rename tests/{ => main}/usage-store.test.mjs (98%) create mode 100644 tests/renderer/components.test.tsx create mode 100644 tests/renderer/fixtures.ts create mode 100644 tests/renderer/overview-components.test.tsx create mode 100644 tests/renderer/tray-components.test.tsx rename tests/{ => renderer}/usage-activity.test.mjs (92%) diff --git a/build/run-tests.mjs b/build/run-tests.mjs index 086d390..a870705 100644 --- a/build/run-tests.mjs +++ b/build/run-tests.mjs @@ -8,26 +8,60 @@ import { fileURLToPath } from "node:url"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const projectRoot = path.resolve(__dirname, ".."); const testHome = mkdtempSync(path.join(os.tmpdir(), "ccr-test-home-")); +const testSuites = ["main", "renderer"]; +const requestedSuites = process.argv.slice(2); +const suites = requestedSuites.length === 0 ? testSuites : requestedSuites; -const child = spawn(electron, ["--test", "dist/tests/*.js"], { - cwd: projectRoot, - env: { - ...process.env, - CCR_INTERNAL_APP_DATA_DIR: path.join(testHome, "app-data"), - CCR_INTERNAL_HOME_DIR: testHome, - CCR_INTERNAL_USER_DATA_DIR: path.join(testHome, "user-data"), - HOME: testHome, - ELECTRON_RUN_AS_NODE: "1" - }, - shell: process.platform === "win32", - stdio: "inherit" -}); - -child.on("exit", (code, signal) => { - rmSync(testHome, { force: true, recursive: true }); - if (signal) { - process.kill(process.pid, signal); - return; +for (const suite of suites) { + if (!testSuites.includes(suite)) { + cleanup(); + throw new Error(`Unknown test suite: ${suite}`); } - process.exit(code ?? 1); -}); +} + +try { + for (const suite of suites) { + await runSuite(suite); + } +} finally { + cleanup(); +} + +function runSuite(suite) { + console.log(`\nRunning ${suite} tests...`); + + return new Promise((resolve, reject) => { + const child = spawn(electron, ["--test", `dist/tests/${suite}/*.js`], { + cwd: projectRoot, + env: { + ...process.env, + CCR_INTERNAL_APP_DATA_DIR: path.join(testHome, "app-data"), + CCR_INTERNAL_HOME_DIR: testHome, + CCR_INTERNAL_USER_DATA_DIR: path.join(testHome, "user-data"), + HOME: testHome, + ELECTRON_RUN_AS_NODE: "1" + }, + shell: process.platform === "win32", + stdio: "inherit" + }); + + child.on("error", reject); + child.on("exit", (code, signal) => { + if (signal) { + process.kill(process.pid, signal); + return; + } + + if (code === 0) { + resolve(); + return; + } + + reject(new Error(`${suite} tests exited with code ${code ?? 1}`)); + }); + }); +} + +function cleanup() { + rmSync(testHome, { force: true, recursive: true }); +} diff --git a/build/test.mjs b/build/test.mjs index 09e17dd..8319a30 100644 --- a/build/test.mjs +++ b/build/test.mjs @@ -1,44 +1,112 @@ import esbuild from "esbuild"; -import { mkdirSync, readdirSync, rmSync, statSync } from "node:fs"; +import { existsSync, readdirSync, rmSync, statSync } from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const projectRoot = path.resolve(__dirname, ".."); -const outdir = path.join(projectRoot, "dist", "tests"); +const testsOutDir = path.join(projectRoot, "dist", "tests"); +const rendererRoot = path.join(projectRoot, "src", "renderer"); +const testSuites = [ + { name: "main", testDir: path.join(projectRoot, "tests", "main") }, + { name: "renderer", testDir: path.join(projectRoot, "tests", "renderer") } +]; +const requestedSuites = new Set(process.argv.slice(2)); +const suiteNames = new Set(testSuites.map((suite) => suite.name)); +const unknownSuites = [...requestedSuites].filter((suite) => !suiteNames.has(suite)); +const selectedSuites = requestedSuites.size === 0 + ? testSuites + : testSuites.filter((suite) => requestedSuites.has(suite.name)); -rmSync(outdir, { force: true, recursive: true }); -mkdirSync(outdir, { recursive: true }); +if (unknownSuites.length > 0) { + throw new Error(`Unknown test suite: ${unknownSuites.join(", ")}`); +} -const entryPoints = findTestFiles(path.join(projectRoot, "tests")); +rmSync(testsOutDir, { force: true, recursive: true }); -await esbuild.build({ - absWorkingDir: projectRoot, - bundle: true, - entryNames: "[name]", - entryPoints, - external: [ - "better-sqlite3", - "electron" - ], - format: "cjs", - legalComments: "none", - logLevel: "info", - outdir, - platform: "node", - target: "node22" -}); +for (const suite of selectedSuites) { + const entryPoints = findTestFiles(suite.testDir); + if (entryPoints.length === 0) { + continue; + } + + await esbuild.build({ + absWorkingDir: projectRoot, + bundle: true, + entryNames: "[name]", + entryPoints, + external: [ + "better-sqlite3", + "electron" + ], + format: "cjs", + legalComments: "none", + loader: { + ".gif": "file", + ".ico": "file", + ".jpg": "file", + ".jpeg": "file", + ".png": "file", + ".svg": "file", + ".webp": "file" + }, + logLevel: "info", + outdir: path.join(testsOutDir, suite.name), + platform: "node", + plugins: [rendererAliasPlugin()], + target: "node22" + }); +} function findTestFiles(dir) { + if (!existsSync(dir)) { + return []; + } + const files = []; for (const name of readdirSync(dir)) { const file = path.join(dir, name); const stat = statSync(file); if (stat.isDirectory()) { files.push(...findTestFiles(file)); - } else if (name.endsWith(".test.mjs")) { + } else if (/\.(test|spec)\.(mjs|ts|tsx)$/.test(name)) { files.push(file); } } return files.sort(); } + +function rendererAliasPlugin() { + return { + name: "renderer-test-alias", + setup(build) { + build.onResolve({ filter: /^@\// }, (args) => { + return { path: resolveRendererImport(args.path.slice(2)) }; + }); + } + }; +} + +function resolveRendererImport(importPath) { + const basePath = path.resolve(rendererRoot, importPath); + const candidates = [ + basePath, + `${basePath}.tsx`, + `${basePath}.ts`, + `${basePath}.jsx`, + `${basePath}.js`, + `${basePath}.json`, + path.join(basePath, "index.tsx"), + path.join(basePath, "index.ts"), + path.join(basePath, "index.jsx"), + path.join(basePath, "index.js") + ]; + + for (const candidate of candidates) { + if (existsSync(candidate) && statSync(candidate).isFile()) { + return candidate; + } + } + + return basePath; +} diff --git a/package.json b/package.json index 0f930d8..16e4cf8 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,8 @@ "prepublishOnly": "npm run typecheck", "preview": "npm run build:assets && electron .", "test": "node build/test.mjs && node build/run-tests.mjs", + "test:main": "node build/test.mjs main && node build/run-tests.mjs main", + "test:renderer": "node build/test.mjs renderer && node build/run-tests.mjs renderer", "typecheck": "tsc --noEmit", "rebuild:sqlite3": "electron-rebuild -f -w better-sqlite3" }, diff --git a/tests/bot-gateway-env.test.mjs b/tests/main/bot-gateway-env.test.mjs similarity index 97% rename from tests/bot-gateway-env.test.mjs rename to tests/main/bot-gateway-env.test.mjs index ac3e384..a4e8acb 100644 --- a/tests/bot-gateway-env.test.mjs +++ b/tests/main/bot-gateway-env.test.mjs @@ -1,6 +1,6 @@ import assert from "node:assert/strict"; import test from "node:test"; -import { botGatewayProfileEnv } from "../src/main/bot-gateway-env.ts"; +import { botGatewayProfileEnv } from "../../src/main/bot-gateway-env.ts"; function botGateway(overrides = {}) { return { diff --git a/tests/deep-link.test.mjs b/tests/main/deep-link.test.mjs similarity index 99% rename from tests/deep-link.test.mjs rename to tests/main/deep-link.test.mjs index 2a9c63b..ba1ab95 100644 --- a/tests/deep-link.test.mjs +++ b/tests/main/deep-link.test.mjs @@ -6,7 +6,7 @@ import { parseProviderDeepLinkPayload, parseProviderManifestDeepLinkPayload, parseProviderManifestPayload -} from "../src/shared/deep-link.ts"; +} from "../../src/shared/deep-link.ts"; function base64UrlJson(value) { return Buffer.from(JSON.stringify(value), "utf8") diff --git a/tests/model-catalog-file.test.mjs b/tests/main/model-catalog-file.test.mjs similarity index 98% rename from tests/model-catalog-file.test.mjs rename to tests/main/model-catalog-file.test.mjs index 8a93289..4a012e7 100644 --- a/tests/model-catalog-file.test.mjs +++ b/tests/main/model-catalog-file.test.mjs @@ -3,7 +3,7 @@ import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import path from "node:path"; import test from "node:test"; -import { loadModelCatalogPayload, modelCatalogPathCandidates } from "../src/main/model-catalog-file.ts"; +import { loadModelCatalogPayload, modelCatalogPathCandidates } from "../../src/main/model-catalog-file.ts"; test("modelCatalogPathCandidates prefers env paths and removes duplicates", () => { const previousCatalogPath = process.env.CCR_MODEL_CATALOG_PATH; diff --git a/tests/profile-launch-core.test.mjs b/tests/main/profile-launch-core.test.mjs similarity index 98% rename from tests/profile-launch-core.test.mjs rename to tests/main/profile-launch-core.test.mjs index 2659aa4..228b0bb 100644 --- a/tests/profile-launch-core.test.mjs +++ b/tests/main/profile-launch-core.test.mjs @@ -10,7 +10,7 @@ import { resolveClaudeCodeSettingsFile, resolveCodexConfigFile, resolveProfileOpenSurface -} from "../src/main/profile-launch-core.ts"; +} from "../../src/main/profile-launch-core.ts"; const claudeProfile = { agent: "claude-code", diff --git a/tests/provider-preset-utils.test.mjs b/tests/main/provider-preset-utils.test.mjs similarity index 98% rename from tests/provider-preset-utils.test.mjs rename to tests/main/provider-preset-utils.test.mjs index 7ee4b50..3aea090 100644 --- a/tests/provider-preset-utils.test.mjs +++ b/tests/main/provider-preset-utils.test.mjs @@ -7,7 +7,7 @@ import { providerEndpointCanReceiveProviderApiKeyInList, providerIdentitySafetyIssueInList, providerPresetMatchesBaseUrl -} from "../src/shared/provider-preset-utils.ts"; +} from "../../src/shared/provider-preset-utils.ts"; const openAiPreset = { aliases: ["OpenAI", "ChatGPT"], diff --git a/tests/provider-url.test.mjs b/tests/main/provider-url.test.mjs similarity index 97% rename from tests/provider-url.test.mjs rename to tests/main/provider-url.test.mjs index b776876..633a8c8 100644 --- a/tests/provider-url.test.mjs +++ b/tests/main/provider-url.test.mjs @@ -5,7 +5,7 @@ import { parseProviderBaseUrl, providerBaseUrlForProtocol, providerUrlWithDefaultScheme -} from "../src/shared/provider-url.ts"; +} from "../../src/shared/provider-url.ts"; test("provider URL parsing strips endpoint paths and unsafe URL parts", () => { const parsed = parseProviderBaseUrl("https://user:secret@api.example.com/v1/chat/completions?token=secret#section"); diff --git a/tests/request-log-store.test.mjs b/tests/main/request-log-store.test.mjs similarity index 99% rename from tests/request-log-store.test.mjs rename to tests/main/request-log-store.test.mjs index 30a9cb1..09ba8ad 100644 --- a/tests/request-log-store.test.mjs +++ b/tests/main/request-log-store.test.mjs @@ -3,7 +3,7 @@ import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import path from "node:path"; import test from "node:test"; -import { RequestLogStore } from "../src/main/request-log-store.ts"; +import { RequestLogStore } from "../../src/main/request-log-store.ts"; test("RequestLogStore keeps list rows lightweight and detail rows complete", async () => { const dir = mkdtempSync(path.join(tmpdir(), "ccr-request-log-test-")); diff --git a/tests/usage-normalization.test.mjs b/tests/main/usage-normalization.test.mjs similarity index 94% rename from tests/usage-normalization.test.mjs rename to tests/main/usage-normalization.test.mjs index 65dfc03..0876d04 100644 --- a/tests/usage-normalization.test.mjs +++ b/tests/main/usage-normalization.test.mjs @@ -1,6 +1,6 @@ import assert from "node:assert/strict"; import test from "node:test"; -import { normalizeUsageInputTokens } from "../src/main/usage-normalization.ts"; +import { normalizeUsageInputTokens } from "../../src/main/usage-normalization.ts"; test("normalizeUsageInputTokens subtracts cache tokens for OpenAI-compatible protocols", () => { const usage = normalizeUsageInputTokens( diff --git a/tests/usage-store.test.mjs b/tests/main/usage-store.test.mjs similarity index 98% rename from tests/usage-store.test.mjs rename to tests/main/usage-store.test.mjs index 0cae4ff..ce8d20c 100644 --- a/tests/usage-store.test.mjs +++ b/tests/main/usage-store.test.mjs @@ -3,7 +3,7 @@ import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import path from "node:path"; import test from "node:test"; -import { UsageStore } from "../src/main/usage-store.ts"; +import { UsageStore } from "../../src/main/usage-store.ts"; test("UsageStore aggregates stats in SQLite without loading all events", async () => { const dir = mkdtempSync(path.join(tmpdir(), "ccr-usage-test-")); diff --git a/tests/renderer/components.test.tsx b/tests/renderer/components.test.tsx new file mode 100644 index 0000000..e6de94c --- /dev/null +++ b/tests/renderer/components.test.tsx @@ -0,0 +1,79 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import * as React from "react"; +import { renderToStaticMarkup } from "react-dom/server"; +import { Badge } from "../../src/renderer/components/ui/badge.tsx"; +import { Button } from "../../src/renderer/components/ui/button.tsx"; +import { Card, CardContent, CardHeader, CardTitle } from "../../src/renderer/components/ui/card.tsx"; +import { Switch } from "../../src/renderer/components/ui/switch.tsx"; + +test("Button renders default button semantics and variant classes", () => { + const html = renderToStaticMarkup( + + ); + + assert.match(html, /^ + ); + + assert.match(html, /class="plain-button"/); + assert.match(html, /type="button"/); + assert.doesNotMatch(html, /inline-flex/); +}); + +test("Badge renders the selected visual variant", () => { + const html = renderToStaticMarkup( + + Delayed + + ); + + assert.match(html, /^Delayed<\/span>$/); +}); + +test("Card primitives compose the expected document structure", () => { + const html = renderToStaticMarkup( + + + Provider settings + + Ready + + ); + + assert.match(html, /^
Provider settings<\/h2>/); + assert.match(html, /
Ready<\/div>/); +}); + +test("Switch renders accessible checked and disabled state", () => { + const html = renderToStaticMarkup( + + ); + + assert.match(html, /provider-switch/); + assert.match(html, /role="switch"/); + assert.match(html, /aria-checked="true"/); + assert.match(html, /aria-label="Enable provider"/); + assert.match(html, /disabled=""/); + assert.match(html, /translate-x-\[24px\]/); +}); diff --git a/tests/renderer/fixtures.ts b/tests/renderer/fixtures.ts new file mode 100644 index 0000000..79cf1e6 --- /dev/null +++ b/tests/renderer/fixtures.ts @@ -0,0 +1,189 @@ +import type { + ProviderAccountSnapshot, + UsageComparisonRow, + UsageStatsRange, + UsageStatsSnapshot, + UsageTotals +} from "../../src/shared/app.ts"; + +export function installBrowserGlobals() { + const localStorage = { + getItem: () => null, + removeItem: () => undefined, + setItem: () => undefined + }; + const windowMock = { + addEventListener: () => undefined, + cancelAnimationFrame: () => undefined, + ccr: { + closeTray: () => undefined, + quitApp: () => undefined, + setTrayDetailOpen: () => undefined, + showMainWindow: () => undefined + }, + clearInterval: () => undefined, + localStorage, + matchMedia: () => ({ + addEventListener: () => undefined, + matches: false, + removeEventListener: () => undefined + }), + removeEventListener: () => undefined, + requestAnimationFrame: () => 0, + setInterval: () => 0 + }; + + Object.defineProperty(globalThis, "window", { + configurable: true, + value: windowMock + }); + Object.defineProperty(globalThis, "document", { + configurable: true, + value: { + body: { + classList: { + add: () => undefined, + remove: () => undefined + }, + style: {} + }, + documentElement: { + lang: "en" + } + } + }); + Object.defineProperty(globalThis, "navigator", { + configurable: true, + value: { + language: "en-US", + languages: ["en-US"] + } + }); +} + +export function usageTotals(patch: Partial = {}): UsageTotals { + return { + avgDurationMs: 842, + cacheRatio: 0.24, + cacheTokens: 2400, + costUsd: 1.23, + errorCount: 2, + inputTokens: 6200, + outputTokens: 3400, + requestCount: 128, + successRate: 0.984, + totalTokens: 12000, + ...patch + }; +} + +export function usageRow(key: string, label: string, patch: Partial = {}): UsageComparisonRow { + return { + ...usageTotals(), + caption: "Test usage row", + key, + label, + maxShare: 0.5, + ...patch + }; +} + +export function usageStats(range: UsageStatsRange = "30d", patch: Partial = {}): UsageStatsSnapshot { + const series = Array.from({ length: 8 }, (_, index) => { + const totalTokens = 1000 + index * 250; + const requestCount = 8 + index; + return { + ...usageTotals({ + cacheRatio: 0.1 + index * 0.02, + cacheTokens: 120 + index * 30, + errorCount: index % 4 === 0 ? 1 : 0, + inputTokens: 560 + index * 120, + outputTokens: 320 + index * 80, + requestCount, + successRate: index % 4 === 0 ? 0.92 : 1, + totalTokens + }), + bucket: `2026-06-${String(20 + index).padStart(2, "0")}T00:00:00.000Z`, + label: `6/${20 + index}` + }; + }); + const models = [ + usageRow("model:gpt-4.1", "gpt-4.1", { maxShare: 0.62, model: "gpt-4.1", provider: "openai", totalTokens: 7200 }), + usageRow("model:claude-sonnet", "claude-sonnet", { maxShare: 0.38, model: "claude-sonnet", provider: "anthropic", totalTokens: 4800 }) + ]; + + return { + clientModels: [ + usageRow("client:claude-code", "Claude Code", { client: "claude-code", model: "gpt-4.1", provider: "openai", totalTokens: 6800 }), + usageRow("client:codex", "Codex", { client: "codex", model: "claude-sonnet", provider: "anthropic", totalTokens: 5200 }) + ], + generatedAt: "2026-06-30T00:00:00.000Z", + models, + providerModels: [ + usageRow("provider:openai", "OpenAI", { credentialId: "primary", model: "gpt-4.1", provider: "openai", totalTokens: 7200 }), + usageRow("provider:anthropic", "Anthropic", { credentialId: "secondary", model: "claude-sonnet", provider: "anthropic", totalTokens: 4800 }) + ], + range, + recentRequests: [], + series, + totals: usageTotals(), + ...patch + }; +} + +export function accountSnapshots(): ProviderAccountSnapshot[] { + return [ + { + credentialId: "primary", + credentialLabel: "Primary Key", + meters: [ + { + id: "5h", + kind: "quota", + label: "5h quota", + limit: 100, + remaining: 42, + unit: "requests", + window: "5h" + }, + { + id: "weekly", + kind: "quota", + label: "Weekly quota", + limit: 500, + remaining: 310, + unit: "requests", + window: "weekly" + }, + { + id: "balance", + kind: "balance", + label: "Cash balance", + remaining: 12.34, + unit: "USD" + } + ], + provider: "openai", + source: "standard", + status: "warning", + updatedAt: "2026-06-30T00:00:00.000Z" + }, + { + credentialId: "secondary", + credentialLabel: "Secondary Key", + meters: [ + { + id: "balance", + kind: "balance", + label: "Credit balance", + remaining: 88, + unit: "USD" + } + ], + provider: "anthropic", + source: "standard", + status: "ok", + updatedAt: "2026-06-30T00:00:00.000Z" + } + ]; +} diff --git a/tests/renderer/overview-components.test.tsx b/tests/renderer/overview-components.test.tsx new file mode 100644 index 0000000..b8e5b84 --- /dev/null +++ b/tests/renderer/overview-components.test.tsx @@ -0,0 +1,71 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import * as React from "react"; +import { renderToStaticMarkup } from "react-dom/server"; +import { OverviewView } from "../../src/renderer/pages/home/components/dashboard.tsx"; +import type { OverviewWidgetConfig } from "../../src/shared/app.ts"; +import { accountSnapshots, installBrowserGlobals, usageStats } from "./fixtures.ts"; + +installBrowserGlobals(); + +test("OverviewView renders every overview widget type", () => { + const widgets: OverviewWidgetConfig[] = [ + { enabled: true, id: "status", size: "4:1", type: "system-status", variant: "timeline" }, + { enabled: true, id: "account", size: "4:2", type: "account-balance", variant: "nested-rings" }, + { enabled: true, id: "metric-requests", metric: "requests", size: "1:1", type: "metric", variant: "card" }, + { enabled: true, id: "metric-cache", metric: "cache-ratio", size: "1:1", type: "metric", variant: "ring" }, + { enabled: true, id: "metric-errors", metric: "errors", size: "1:1", type: "metric", variant: "bar" }, + { enabled: true, id: "trend", size: "3:2", type: "usage-trend", variant: "composed" }, + { enabled: true, id: "activity", size: "4:2", type: "token-activity", variant: "heatmap" }, + { enabled: true, id: "token-mix", size: "2:2", type: "token-mix", variant: "stacked" }, + { enabled: true, id: "models", size: "2:2", type: "model-distribution", variant: "donut" }, + { enabled: true, id: "clients", size: "4:2", type: "client-analysis", variant: "table" }, + { enabled: true, id: "providers", size: "4:2", type: "provider-analysis", variant: "table" } + ]; + + const html = renderToStaticMarkup( + undefined} + setUsageRange={() => undefined} + usageRange="30d" + usageStats={usageStats("30d")} + onWidgetsChange={() => undefined} + /> + ); + + assert.match(html, /

Overview<\/h2>/); + assert.match(html, /aria-label="Edit widgets"/); + assert.match(html, /System status/); + assert.match(html, /API Service/); + assert.match(html, /openai \/ Primary Key/); + assert.match(html, /Requests/); + assert.match(html, /Cache ratio/); + assert.match(html, /Errors/); + assert.match(html, /Usage Trend/); + assert.match(html, /Activity/); + assert.match(html, /Token Mix/); + assert.match(html, /Model Distribution/); + assert.match(html, /Client Analysis/); + assert.match(html, /Provider Analysis/); + assert.match(html, /claude-code/); + assert.match(html, /openai/); +}); + +test("OverviewView renders the empty widget layout state", () => { + const html = renderToStaticMarkup( + undefined} + usageRange="7d" + usageStats={usageStats("7d")} + onWidgetsChange={() => undefined} + /> + ); + + assert.match(html, /Overview/); + assert.match(html, /No widgets configured/); + assert.match(html, /aria-label="Edit widgets"/); +}); diff --git a/tests/renderer/tray-components.test.tsx b/tests/renderer/tray-components.test.tsx new file mode 100644 index 0000000..6e55776 --- /dev/null +++ b/tests/renderer/tray-components.test.tsx @@ -0,0 +1,258 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import * as React from "react"; +import { renderToStaticMarkup } from "react-dom/server"; +import { + AccountSummaryPanel, + AnimatedUsageChart, + ChartShell, + ModelShareChart, + RadialMetric, + RangeSwitch, + RingMetrics, + SourceGrid, + StatsGrid, + TokenActivityPanel, + TokenMixPanel, + TrayStatusStrip, + UsageDetailPanel, + UsageOverviewPanel +} from "../../src/renderer/pages/tray/components/index.ts"; +import { TrayApp } from "../../src/renderer/pages/tray/TrayApp.tsx"; +import { TrayDetailApp } from "../../src/renderer/pages/tray/TrayDetailApp.tsx"; +import { accountSnapshots, installBrowserGlobals, usageStats, usageTotals } from "./fixtures.ts"; + +installBrowserGlobals(); + +const componentVariants = { + account: "bar", + modelShare: "bars", + rings: "rings", + stats: "cards", + tokenFlow: "line", + tokenMix: "bars" +} as const; + +test("UsageOverviewPanel renders every enabled overview tray module", () => { + const activeStats = usageStats("30d"); + const html = renderToStaticMarkup( + + ); + + assert.match(html, /openai \/ Primary Key/); + assert.match(html, /30d Token Flow/); + assert.match(html, /Activity/); + assert.match(html, /Today tokens/); + assert.match(html, /Token Mix/); + assert.match(html, /Circular metrics/); + assert.match(html, /Model Share/); + assert.match(html, /Syncing usage\.\.\./); +}); + +test("UsageDetailPanel renders configured detail widgets and empty state", () => { + const activeStats = usageStats("7d"); + const html = renderToStaticMarkup( + undefined} + /> + ); + const emptyHtml = renderToStaticMarkup( + undefined} + /> + ); + + assert.match(html, /Usage Detail/); + assert.match(html, /7d - OpenAI/); + assert.match(html, /7d tokens/); + assert.match(html, /Token Flow/); + assert.match(html, /Token Mix/); + assert.match(html, /Circular metrics/); + assert.match(html, /Model Share/); + assert.match(emptyHtml, /No tray modules enabled/); +}); + +test("TrayStatusStrip renders open and quit actions", () => { + const html = renderToStaticMarkup(); + + assert.match(html, /aria-label="Open CCR"/); + assert.match(html, /title="Open CCR"/); + assert.match(html, /12\.5K tokens/); + assert.match(html, /CCR/); + assert.match(html, /aria-label="Quit"/); +}); + +test("SourceGrid renders provider tabs with the selected state", () => { + const html = renderToStaticMarkup( + undefined} + /> + ); + + assert.match(html, />All<\/button>/); + assert.match(html, /border-teal-300\/35 bg-teal-300\/16 text-teal-50/); + assert.match(html, />OpenAI<\/button>/); + assert.match(html, />Anthropic<\/button>/); +}); + +test("AccountSummaryPanel covers empty and metered account states", () => { + const emptyHtml = renderToStaticMarkup(); + const meteredHtml = renderToStaticMarkup( + undefined} /> + ); + + assert.match(emptyHtml, /No account data configured/); + assert.match(meteredHtml, /openai \/ Primary Key/); + assert.match(meteredHtml, /5h quota/); + assert.match(meteredHtml, /42 requests/); + assert.match(meteredHtml, /style="\s*width:42%"/); +}); + +test("RangeSwitch renders every usage range option", () => { + const html = renderToStaticMarkup( undefined} />); + + assert.match(html, />Today<\/button>/); + assert.match(html, />24h<\/button>/); + assert.match(html, /bg-white\/14 text-slate-50/); + assert.match(html, />7d<\/button>/); + assert.match(html, />30d<\/button>/); +}); + +test("ChartShell renders title, meta, and children", () => { + const html = renderToStaticMarkup( + + chart body + + ); + + assert.match(html, /Token Flow/); + assert.match(html, /gpt-4\.1/); + assert.match(html, /chart body/); +}); + +test("StatsGrid renders cards, compact, and pill variants", () => { + const items = [ + { label: "Tokens", value: "12K" }, + { label: "Requests", value: "128" } + ]; + const cardsHtml = renderToStaticMarkup(); + const compactHtml = renderToStaticMarkup(); + const pillsHtml = renderToStaticMarkup(); + + assert.match(cardsHtml, /grid-cols-2/); + assert.match(compactHtml, /py-0\.5/); + assert.match(pillsHtml, /rounded-full/); + assert.match(`${cardsHtml}${compactHtml}${pillsHtml}`, /Tokens/); + assert.match(`${cardsHtml}${compactHtml}${pillsHtml}`, /12K/); +}); + +test("AnimatedUsageChart renders line, area, bar, and sparkline output", () => { + const series = usageStats().series; + const lineHtml = renderToStaticMarkup(); + const areaHtml = renderToStaticMarkup(); + const barHtml = renderToStaticMarkup(); + const sparkHtml = renderToStaticMarkup(); + + assert.match(lineHtml, /aria-label="Usage chart"/); + assert.match(lineHtml, /line-chart-glow/); + assert.match(areaHtml, /fill="rgba\(45,212,191,.18\)"/); + assert.match(barHtml, / { + const html = renderToStaticMarkup(); + + assert.match(html, /Activity/); + assert.match(html, /Longest streak/); + assert.match(html, /aria-label="Activity Tokens"/); + assert.match(html, /Less/); + assert.match(html, /More/); +}); + +test("TokenMixPanel renders bars, stacked, and share chart variants", () => { + const totals = usageTotals(); + const barsHtml = renderToStaticMarkup(); + const stackedHtml = renderToStaticMarkup(); + const donutHtml = renderToStaticMarkup(); + + assert.match(barsHtml, /Token Mix/); + assert.match(barsHtml, /Input/); + assert.match(stackedHtml, /bg-blue-400/); + assert.match(donutHtml, /aria-label="Share chart"/); +}); + +test("RingMetrics and RadialMetric render accessible radial charts", () => { + const ringsHtml = renderToStaticMarkup(); + const radialHtml = renderToStaticMarkup( + + ); + + assert.match(ringsHtml, /Circular metrics/); + assert.match(ringsHtml, /Success/); + assert.match(ringsHtml, /Cache/); + assert.match(radialHtml, /aria-label="Cache 24%"/); + assert.match(radialHtml, /12K/); + assert.match(radialHtml, /tokens/); +}); + +test("ModelShareChart renders populated variants and empty state", () => { + const rows = usageStats().models; + const barsHtml = renderToStaticMarkup(); + const listHtml = renderToStaticMarkup(); + const donutHtml = renderToStaticMarkup(); + const emptyHtml = renderToStaticMarkup(); + + assert.match(barsHtml, /Model Share/); + assert.match(barsHtml, /gpt-4\.1/); + assert.match(listHtml, /1\. gpt-4\.1/); + assert.match(listHtml, /62%/); + assert.match(donutHtml, /aria-label="Share chart"/); + assert.match(emptyHtml, /No usage captured yet/); +}); + +test("TrayApp and TrayDetailApp render their shell components without browser runtime", () => { + const trayHtml = renderToStaticMarkup(); + const detailHtml = renderToStaticMarkup(); + + assert.match(trayHtml, /Usage Overview/); + assert.match(trayHtml, /Open CCR/); + assert.match(detailHtml, /Usage Detail/); + assert.match(detailHtml, /OpenAI/); +}); diff --git a/tests/usage-activity.test.mjs b/tests/renderer/usage-activity.test.mjs similarity index 92% rename from tests/usage-activity.test.mjs rename to tests/renderer/usage-activity.test.mjs index a3c0e2a..37dc1a2 100644 --- a/tests/usage-activity.test.mjs +++ b/tests/renderer/usage-activity.test.mjs @@ -1,6 +1,6 @@ import assert from "node:assert/strict"; import test from "node:test"; -import { buildTokenActivity, activityDateKey } from "../src/renderer/lib/usage-activity.ts"; +import { buildTokenActivity, activityDateKey } from "../../src/renderer/lib/usage-activity.ts"; test("buildTokenActivity summarizes observed token days and streaks", () => { const summary = buildTokenActivity( From 13517cc2a0aba2a7a2f561ea2783ad3f012a4148 Mon Sep 17 00:00:00 2001 From: musistudio Date: Tue, 30 Jun 2026 12:04:26 +0800 Subject: [PATCH 14/94] Make model catalog capability-aware --- src/main/codex-cli-middleware-runtime.ts | 23 +- src/main/codex-model-catalog.ts | 316 +++++++++++++++++++++-- src/server/gateway/service.ts | 57 +++- tests/main/codex-model-catalog.test.mjs | 169 ++++++++++++ 4 files changed, 526 insertions(+), 39 deletions(-) create mode 100644 tests/main/codex-model-catalog.test.mjs diff --git a/src/main/codex-cli-middleware-runtime.ts b/src/main/codex-cli-middleware-runtime.ts index 0813c76..339d775 100644 --- a/src/main/codex-cli-middleware-runtime.ts +++ b/src/main/codex-cli-middleware-runtime.ts @@ -2260,13 +2260,8 @@ function modelCatalogConfigItem(model, priority) { slug: model, display_name: model, description: "CCR gateway model " + model, - default_reasoning_level: "medium", - supported_reasoning_levels: [ - { effort: "low", description: "Low reasoning" }, - { effort: "medium", description: "Medium reasoning" }, - { effort: "high", description: "High reasoning" }, - { effort: "xhigh", description: "Extra high reasoning" } - ], + default_reasoning_level: null, + supported_reasoning_levels: [], shell_type: "shell_command", visibility: "list", supported_in_api: true, @@ -2276,21 +2271,21 @@ function modelCatalogConfigItem(model, priority) { availability_nux: null, upgrade: null, base_instructions: "You are Codex, a coding agent.", - supports_reasoning_summaries: true, + supports_reasoning_summaries: false, default_reasoning_summary: "none", support_verbosity: true, default_verbosity: "low", - apply_patch_tool_type: "freeform", - web_search_tool_type: "text_and_image", + apply_patch_tool_type: null, + web_search_tool_type: "text", truncation_policy: { mode: "tokens", limit: 10000 }, - supports_parallel_tool_calls: true, - supports_image_detail_original: true, + supports_parallel_tool_calls: false, + supports_image_detail_original: false, context_window: 128000, max_context_window: 128000, effective_context_window_percent: 95, experimental_supported_tools: [], - input_modalities: ["text", "image"], - supports_search_tool: true + input_modalities: ["text"], + supports_search_tool: false }; } diff --git a/src/main/codex-model-catalog.ts b/src/main/codex-model-catalog.ts index d901519..fc2185e 100644 --- a/src/main/codex-model-catalog.ts +++ b/src/main/codex-model-catalog.ts @@ -1,5 +1,11 @@ -import type { AppConfig, VirtualModelProfileConfig } from "../shared/app"; -import { findModelCatalogEntry, modelCatalogMaxInputTokens } from "../server/gateway/model-catalog"; +import { BUILTIN_FUSION_WEB_SEARCH_TOOL_NAME } from "../shared/app"; +import type { AppConfig, GatewayProviderConfig, GatewayProviderProtocol, VirtualModelProfileConfig } from "../shared/app"; +import { + findModelCatalogEntry, + modelCatalogMaxInputTokens, + readCatalogCapability, + type ModelCatalogEntry +} from "../server/gateway/model-catalog"; const fusionModelProviderName = "Fusion"; const codexDefaultContextWindow = 128_000; @@ -11,11 +17,11 @@ export type CodexModelCatalog = { export type CodexModelCatalogItem = { additional_speed_tiers: unknown[]; - apply_patch_tool_type: string; + apply_patch_tool_type: string | null; availability_nux: null; base_instructions: string; context_window: number; - default_reasoning_level: string; + default_reasoning_level: string | null; default_reasoning_summary: string; description: string; display_name: string; @@ -42,7 +48,7 @@ export type CodexModelCatalogItem = { export function buildCodexModelCatalog(config?: Partial>, selectedModel?: string): CodexModelCatalog { return { - models: buildCodexModelCatalogIds(config, selectedModel).map((model, index) => codexModelCatalogItem(model, index)) + models: buildCodexModelCatalogIds(config, selectedModel).map((model, index) => codexModelCatalogItem(model, index, config)) }; } @@ -101,21 +107,26 @@ export function codexModelCatalogBase64(config?: Partial> +): CodexModelCatalogItem { + const profile = codexModelCapabilityProfile(model, config); + const contextWindow = codexModelContextWindow(model, profile.catalogEntry); return { additional_speed_tiers: [], - apply_patch_tool_type: "freeform", + apply_patch_tool_type: profile.applyPatchToolType, availability_nux: null, base_instructions: "You are Codex, a coding agent.", context_window: contextWindow, - default_reasoning_level: "medium", + default_reasoning_level: profile.supportsReasoning ? "medium" : null, default_reasoning_summary: "none", description: `CCR gateway model ${model}`, display_name: model, effective_context_window_percent: codexEffectiveContextWindowPercent, experimental_supported_tools: [], - input_modalities: ["text", "image"], + input_modalities: profile.inputModalities, max_context_window: contextWindow, priority, service_tiers: [], @@ -123,28 +134,287 @@ function codexModelCatalogItem(model: string, priority: number): CodexModelCatal slug: model, support_verbosity: true, supported_in_api: true, - supported_reasoning_levels: [ - { effort: "low", description: "Low reasoning" }, - { effort: "medium", description: "Medium reasoning" }, - { effort: "high", description: "High reasoning" }, - { effort: "xhigh", description: "Extra high reasoning" } - ], - supports_image_detail_original: true, - supports_parallel_tool_calls: true, - supports_reasoning_summaries: true, - supports_search_tool: true, + supported_reasoning_levels: profile.supportedReasoningLevels, + supports_image_detail_original: profile.supportsImageInput, + supports_parallel_tool_calls: profile.supportsParallelToolCalls, + supports_reasoning_summaries: profile.supportsReasoning, + supports_search_tool: profile.supportsSearchTool, truncation_policy: { mode: "tokens", limit: 10_000 }, upgrade: null, visibility: "list", - web_search_tool_type: "text_and_image" + web_search_tool_type: profile.supportsSearchTool && profile.supportsImageInput ? "text_and_image" : "text" }; } -function codexModelContextWindow(model: string): number { - const entry = findModelCatalogEntry(model); +type CodexCapabilityProfile = { + applyPatchToolType: string | null; + catalogEntry?: ModelCatalogEntry; + inputModalities: string[]; + supportedReasoningLevels: Array<{ description: string; effort: string }>; + supportsImageInput: boolean; + supportsParallelToolCalls: boolean; + supportsReasoning: boolean; + supportsSearchTool: boolean; +}; + +function codexModelCapabilityProfile( + model: string, + config?: Partial> +): CodexCapabilityProfile { + const selector = parseModelSelector(model); + const provider = selector?.provider ? findConfiguredProvider(config, selector.provider) : undefined; + const catalogEntry = findModelCatalogEntry(model); + const capabilities = catalogEntry?.capabilities ?? {}; + const providerProtocol = provider ? codexProviderProtocol(provider) : undefined; + const providerSupportsResponses = provider ? codexProviderSupportsResponses(provider) : false; + const supportsFusionWebSearch = codexVirtualModelSupportsFusionWebSearch(model, config); + const supportsReasoning = readCatalogCapability(capabilities, "reasoning"); + const supportsImageInput = catalogEntrySupportsImageInput(catalogEntry); + const supportsParallelToolCalls = readCatalogCapability(capabilities, "parallelFunctionCalling"); + const applyPatchToolType = providerSupportsResponses || catalogModelLooksLikeGpt(model, catalogEntry) + ? "freeform" + : null; + const supportsSearchTool = + supportsFusionWebSearch || + ( + readCatalogCapability(capabilities, "webSearch") && + (providerProtocol === "openai_responses" || providerProtocol === "anthropic_messages") + ); + + return { + applyPatchToolType, + catalogEntry, + inputModalities: supportsImageInput ? ["text", "image"] : ["text"], + supportedReasoningLevels: supportsReasoning ? supportedReasoningLevels(capabilities) : [], + supportsImageInput, + supportsParallelToolCalls, + supportsReasoning, + supportsSearchTool + }; +} + +function codexModelContextWindow(model: string, entry = findModelCatalogEntry(model)): number { return modelCatalogMaxInputTokens(entry) || codexDefaultContextWindow; } +function catalogEntrySupportsImageInput(entry: ModelCatalogEntry | undefined): boolean { + const capabilities = entry?.capabilities ?? {}; + const modalities = new Set((entry?.modalities?.input ?? []).map((item) => item.toLowerCase())); + return modalities.has("image") || + readCatalogCapability(capabilities, "imageInput") || + readCatalogCapability(capabilities, "vision") || + readCatalogCapability(capabilities, "multimodal"); +} + +function supportedReasoningLevels(capabilities: Record): Array<{ description: string; effort: string }> { + const levels = [ + { effort: "low", description: "Low reasoning" }, + { effort: "medium", description: "Medium reasoning" }, + { effort: "high", description: "High reasoning" } + ]; + if (readCatalogCapability(capabilities, "xhighReasoningEffort") || readCatalogCapability(capabilities, "maxReasoningEffort")) { + levels.push({ effort: "xhigh", description: "Extra high reasoning" }); + } + return levels; +} + +function findConfiguredProvider( + config: Partial> | undefined, + providerName: string +): GatewayProviderConfig | undefined { + const normalized = providerName.trim().toLowerCase(); + if (!normalized) { + return undefined; + } + return (config?.Providers ?? []).find((provider) => provider.name.trim().toLowerCase() === normalized); +} + +function codexProviderProtocol(provider: GatewayProviderConfig): GatewayProviderProtocol | undefined { + const capabilityProtocols = uniqueProviderProtocols((provider.capabilities ?? []).map((capability) => normalizeProviderProtocol(capability.type))); + for (const protocol of ["openai_responses", "openai_chat_completions", "anthropic_messages", "gemini_generate_content"] as GatewayProviderProtocol[]) { + if (capabilityProtocols.includes(protocol)) { + return protocol; + } + } + + return normalizeProviderProtocol(provider.type) ?? normalizeProviderProtocol(provider.provider) ?? inferProviderProtocol(provider); +} + +function codexProviderSupportsResponses(provider: GatewayProviderConfig): boolean { + return uniqueProviderProtocols((provider.capabilities ?? []).map((capability) => normalizeProviderProtocol(capability.type))).includes("openai_responses") || + normalizeProviderProtocol(provider.type) === "openai_responses" || + normalizeProviderProtocol(provider.provider) === "openai_responses" || + providerEndpointLooksLikeResponses(provider); +} + +function inferProviderProtocol(provider: GatewayProviderConfig): GatewayProviderProtocol { + const url = (provider.baseUrl || provider.baseurl || provider.api_base_url || "").toLowerCase(); + const transformer = JSON.stringify(provider.transformer ?? "").toLowerCase(); + if (providerEndpointLooksLikeResponses(provider)) { + return "openai_responses"; + } + if (url.includes("generativelanguage.googleapis.com") || transformer.includes("gemini")) { + return "gemini_generate_content"; + } + if (url.includes("anthropic") || transformer.includes("anthropic")) { + return "anthropic_messages"; + } + return "openai_chat_completions"; +} + +function providerEndpointLooksLikeResponses(provider: GatewayProviderConfig): boolean { + const url = (provider.baseUrl || provider.baseurl || provider.api_base_url || "").toLowerCase(); + return url.endsWith("/responses") || url.includes("/responses?"); +} + +function catalogModelLooksLikeGpt(model: string, entry: ModelCatalogEntry | undefined): boolean { + return [ + model, + entry?.id, + entry?.model + ].some((value) => typeof value === "string" && value.toLowerCase().includes("gpt")); +} + +function normalizeProviderProtocol(value: unknown): GatewayProviderProtocol | undefined { + if (typeof value !== "string") { + return undefined; + } + const normalized = value.trim().toLowerCase(); + if (normalized === "openai" || normalized === "openai_responses") { + return "openai_responses"; + } + if (normalized === "openai_chat" || normalized === "openai_chat_completions") { + return "openai_chat_completions"; + } + if (normalized === "anthropic" || normalized === "anthropic_messages") { + return "anthropic_messages"; + } + if (normalized === "gemini" || normalized === "gemini_generate_content") { + return "gemini_generate_content"; + } + return undefined; +} + +function uniqueProviderProtocols(values: Array): GatewayProviderProtocol[] { + const seen = new Set(); + const output: GatewayProviderProtocol[] = []; + for (const value of values) { + if (!value || seen.has(value)) { + continue; + } + seen.add(value); + output.push(value); + } + return output; +} + +function parseModelSelector(model: string): { model: string; provider: string } | undefined { + const normalized = normalizeModelSelector(model); + const slashIndex = normalized.indexOf("/"); + if (slashIndex <= 0 || slashIndex >= normalized.length - 1) { + return undefined; + } + return { + provider: normalized.slice(0, slashIndex), + model: normalized.slice(slashIndex + 1) + }; +} + +function codexVirtualModelSupportsFusionWebSearch( + model: string, + config?: Partial> +): boolean { + return (config?.virtualModelProfiles ?? []).some((profile) => + virtualModelIsCatalogVisible(profile) && + virtualModelMatchesCatalogModel(profile, model, config) && + virtualModelProfileSupportsFusionWebSearch(profile) + ); +} + +function virtualModelMatchesCatalogModel( + profile: VirtualModelProfileConfig, + model: string, + config?: Partial> +): boolean { + const normalizedModel = normalizeModelSelector(model); + const normalizedModelLower = normalizedModel.toLowerCase(); + if (!normalizedModelLower) { + return false; + } + + for (const alias of virtualModelRawCatalogNames(profile)) { + const normalizedAlias = alias.trim().toLowerCase(); + if (normalizedAlias && (normalizedModelLower === normalizedAlias || normalizedModelLower === fusionModelSelector(alias).toLowerCase())) { + return true; + } + } + + const selector = parseModelSelector(normalizedModel); + if (!selector) { + return false; + } + const provider = findConfiguredProvider(config, selector.provider); + if (!provider) { + return false; + } + const configuredModels = new Set(provider.models.map((item) => item.trim().toLowerCase()).filter(Boolean)); + const selectedModel = selector.model.trim(); + const selectedModelLower = selectedModel.toLowerCase(); + + for (const prefix of profile.match?.prefixes ?? []) { + const normalizedPrefix = prefix.trim(); + if (!normalizedPrefix || !selectedModelLower.startsWith(normalizedPrefix.toLowerCase())) { + continue; + } + const baseModel = selectedModel.slice(normalizedPrefix.length).trim().toLowerCase(); + if (configuredModels.has(baseModel)) { + return true; + } + } + + for (const suffix of profile.match?.suffixes ?? []) { + const normalizedSuffix = suffix.trim(); + if (!normalizedSuffix || !selectedModelLower.endsWith(normalizedSuffix.toLowerCase())) { + continue; + } + const baseModel = selectedModel.slice(0, selectedModel.length - normalizedSuffix.length).trim().toLowerCase(); + if (configuredModels.has(baseModel)) { + return true; + } + } + + return false; +} + +function virtualModelProfileSupportsFusionWebSearch(profile: VirtualModelProfileConfig): boolean { + const metadata = recordValue(profile.metadata); + const fusionWebSearch = recordValue(metadata?.fusionWebSearch); + if (stringRecordValue(fusionWebSearch, "toolName")) { + return true; + } + + if (recordValue(profile.execution)?.matchWebSearch === true) { + return true; + } + + return (profile.tools ?? []).some((tool) => { + const name = tool.name.trim(); + return name === BUILTIN_FUSION_WEB_SEARCH_TOOL_NAME || + name.startsWith(`${BUILTIN_FUSION_WEB_SEARCH_TOOL_NAME}_`); + }); +} + +function recordValue(value: unknown): Record | undefined { + return typeof value === "object" && value !== null && !Array.isArray(value) + ? value as Record + : undefined; +} + +function stringRecordValue(record: Record | undefined, key: string): string { + const value = record?.[key]; + return typeof value === "string" ? value.trim() : ""; +} + function virtualModelIsCatalogVisible(profile: VirtualModelProfileConfig): boolean { return profile.enabled !== false && profile.materialization?.enabled !== false && diff --git a/src/server/gateway/service.ts b/src/server/gateway/service.ts index 7dac5f1..3726623 100644 --- a/src/server/gateway/service.ts +++ b/src/server/gateway/service.ts @@ -1787,7 +1787,9 @@ function rewriteModelSelectorForProtocol( return model; } const publicModel = resolveGatewayPublicModelId(normalized, config) ?? normalized; - const selector = resolveConfiguredProviderModelSelector(publicModel, config); + const selector = + resolveConfiguredProviderModelSelector(publicModel, config) ?? + resolveUniqueConfiguredProviderModelSelector(publicModel, config); const capability = selector ? providerCapabilityForClientProtocol(selector.provider, protocol) : undefined; return selector && capability ? `${providerCapabilityInternalName(selector.provider, capability.type)}/${selector.model}` @@ -1997,7 +1999,9 @@ function prepareUpstreamCredentialAttempt(input: { if (!target) { return { ...input.attempt, - body: normalizedBody?.body ?? input.attempt.body, + body: bodyHasConfiguredProviderModelSelector(input.attempt.body, input.config) + ? input.attempt.body + : normalizedBody?.body ?? input.attempt.body, headers: input.headers }; } @@ -2059,6 +2063,12 @@ function normalizeConfiguredProviderModelBody( }; } +function bodyHasConfiguredProviderModelSelector(body: Buffer | undefined, config: AppConfig): boolean { + const parsedBody = parseJsonObjectSafe(body); + const model = stringValue(parsedBody?.model); + return Boolean(resolveConfiguredProviderModelSelector(model, config)); +} + function resolveProviderCredentialRoutingTarget( config: AppConfig, headers: Record, @@ -2183,6 +2193,49 @@ function resolveConfiguredProviderModelSelector( return selectedProvider && current ? { model: current, provider: selectedProvider } : undefined; } +function resolveUniqueConfiguredProviderModelSelector( + value: string | undefined, + config: AppConfig +): { model: string; provider: GatewayProviderConfig } | undefined { + const model = normalizeRouteSelector(value); + if (!model) { + return undefined; + } + + const exactMatches = configuredProviderModelMatches(model, config, false); + if (exactMatches.length === 1) { + return exactMatches[0]; + } + if (exactMatches.length > 1) { + return undefined; + } + + const caseInsensitiveMatches = configuredProviderModelMatches(model, config, true); + return caseInsensitiveMatches.length === 1 ? caseInsensitiveMatches[0] : undefined; +} + +function configuredProviderModelMatches( + model: string, + config: AppConfig, + caseInsensitive: boolean +): Array<{ model: string; provider: GatewayProviderConfig }> { + const normalized = caseInsensitive ? model.toLowerCase() : model; + const matches: Array<{ model: string; provider: GatewayProviderConfig }> = []; + for (const provider of config.Providers) { + for (const candidate of provider.models) { + const configuredModel = candidate.trim(); + if (!configuredModel) { + continue; + } + const comparable = caseInsensitive ? configuredModel.toLowerCase() : configuredModel; + if (comparable === normalized) { + matches.push({ model: configuredModel, provider }); + } + } + } + return matches; +} + function firstTargetProviderHeader(headers: Record): string | undefined { const provider = headers["x-target-provider"] || headers["x-gateway-target-provider"]; if (provider?.trim()) { diff --git a/tests/main/codex-model-catalog.test.mjs b/tests/main/codex-model-catalog.test.mjs new file mode 100644 index 0000000..5585183 --- /dev/null +++ b/tests/main/codex-model-catalog.test.mjs @@ -0,0 +1,169 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { buildCodexModelCatalog } from "../../src/main/codex-model-catalog.ts"; + +function catalogModelFor(config, slug) { + const catalog = buildCodexModelCatalog(config, slug); + const model = catalog.models.find((item) => item.slug === slug); + assert.ok(model, `expected catalog model ${slug}`); + return model; +} + +test("codex catalog treats unknown models as text-only without advanced tools", () => { + const model = catalogModelFor({ + Providers: [ + { name: "Custom", type: "openai_chat_completions", models: ["unknown-model"] } + ] + }, "Custom/unknown-model"); + + assert.deepEqual(model.input_modalities, ["text"]); + assert.equal(model.supports_search_tool, false); + assert.equal(model.supports_parallel_tool_calls, false); + assert.equal(model.supports_reasoning_summaries, false); + assert.equal(model.supports_image_detail_original, false); + assert.deepEqual(model.supported_reasoning_levels, []); + assert.equal(model.default_reasoning_level, null); + assert.equal(model.apply_patch_tool_type, null); +}); + +test("codex catalog uses model catalog capabilities for known text models", () => { + const model = catalogModelFor({ + Providers: [ + { name: "deepseek", type: "openai_chat_completions", models: ["deepseek-chat"] } + ] + }, "deepseek/deepseek-chat"); + + assert.deepEqual(model.input_modalities, ["text"]); + assert.equal(model.supports_parallel_tool_calls, true); + assert.equal(model.supports_search_tool, false); + assert.equal(model.supports_reasoning_summaries, false); + assert.equal(model.supports_image_detail_original, false); + assert.deepEqual(model.supported_reasoning_levels, []); + assert.equal(model.default_reasoning_level, null); + assert.equal(model.apply_patch_tool_type, null); +}); + +test("codex catalog enables multimodal reasoning and search when provider protocol supports it", () => { + const model = catalogModelFor({ + Providers: [ + { name: "openrouter", type: "openai_responses", models: ["google/gemini-2.5-pro"] } + ] + }, "openrouter/google/gemini-2.5-pro"); + + assert.deepEqual(model.input_modalities, ["text", "image"]); + assert.equal(model.supports_image_detail_original, true); + assert.equal(model.supports_parallel_tool_calls, true); + assert.equal(model.supports_reasoning_summaries, true); + assert.equal(model.supports_search_tool, true); + assert.equal(model.web_search_tool_type, "text_and_image"); + assert.deepEqual(model.supported_reasoning_levels.map((level) => level.effort), ["low", "medium", "high"]); + assert.equal(model.default_reasoning_level, "medium"); + assert.equal(model.apply_patch_tool_type, "freeform"); +}); + +test("codex catalog does not expose native search through chat-completions-only providers", () => { + const model = catalogModelFor({ + Providers: [ + { name: "openrouter", type: "openai_chat_completions", models: ["google/gemini-2.5-pro"] } + ] + }, "openrouter/google/gemini-2.5-pro"); + + assert.deepEqual(model.input_modalities, ["text", "image"]); + assert.equal(model.supports_parallel_tool_calls, true); + assert.equal(model.supports_reasoning_summaries, true); + assert.equal(model.supports_search_tool, false); + assert.equal(model.web_search_tool_type, "text"); + assert.equal(model.apply_patch_tool_type, null); +}); + +test("codex catalog keeps freeform apply_patch for GPT-named chat-compatible models", () => { + const model = catalogModelFor({ + Providers: [ + { name: "gateway", type: "openai_chat_completions", models: ["gpt-compatible-coder"] } + ] + }, "gateway/gpt-compatible-coder"); + + assert.equal(model.apply_patch_tool_type, "freeform"); +}); + +test("codex catalog keeps freeform apply_patch when provider advertises Responses capability", () => { + const model = catalogModelFor({ + Providers: [ + { + capabilities: [ + { baseUrl: "https://openrouter.ai/api/v1", type: "openai_chat_completions" }, + { baseUrl: "https://openrouter.ai/api/v1", type: "openai_responses" } + ], + name: "openrouter", + type: "openai_chat_completions", + models: ["google/gemini-2.5-pro"] + } + ] + }, "openrouter/google/gemini-2.5-pro"); + + assert.equal(model.apply_patch_tool_type, "freeform"); +}); + +test("codex catalog marks Fusion aliases with builtin web search as searchable", () => { + const model = catalogModelFor({ + Providers: [], + virtualModelProfiles: [ + { + displayName: "Research Fusion", + enabled: true, + execution: { + clientToolsPolicy: "allow", + matchWebSearch: false, + maxToolCalls: 4, + maxTurns: 4, + mode: "tool_loop", + streamMode: "optimistic" + }, + id: "research-fusion", + key: "research", + match: { exactAliases: ["research"], prefixes: [], suffixes: [] }, + materialization: { enabled: true, includeInGatewayModels: true }, + metadata: { + fusionWebSearch: { provider: "brave", toolName: "web_search_research" } + }, + tools: [{ name: "web_search_research", visibility: "internal" }] + } + ] + }, "Fusion/research"); + + assert.deepEqual(model.input_modalities, ["text"]); + assert.equal(model.supports_search_tool, true); + assert.equal(model.web_search_tool_type, "text"); + assert.equal(model.apply_patch_tool_type, null); +}); + +test("codex catalog marks prefixed Fusion virtual models with legacy web search tools as searchable", () => { + const model = catalogModelFor({ + Providers: [ + { name: "deepseek", type: "openai_chat_completions", models: ["deepseek-chat"] } + ], + virtualModelProfiles: [ + { + displayName: "Web Prefix", + enabled: true, + execution: { + clientToolsPolicy: "allow", + maxToolCalls: 4, + maxTurns: 4, + mode: "tool_loop", + streamMode: "optimistic" + }, + id: "web-prefix", + key: "web-prefix", + match: { exactAliases: [], prefixes: ["web-"], suffixes: [] }, + materialization: { enabled: true, includeInGatewayModels: true }, + tools: [{ name: "web_search", visibility: "internal" }] + } + ] + }, "deepseek/web-deepseek-chat"); + + assert.deepEqual(model.input_modalities, ["text"]); + assert.equal(model.supports_search_tool, true); + assert.equal(model.web_search_tool_type, "text"); + assert.equal(model.apply_patch_tool_type, null); +}); From f6174752b7e4d47b2941196081052a2a403758b3 Mon Sep 17 00:00:00 2001 From: musi Date: Tue, 30 Jun 2026 13:57:06 +0800 Subject: [PATCH 15/94] Improve app packaging diagnostics and usage formatting --- build/verify-packaged-app.cjs | 212 ++++++++++++++++++ electron-builder.json | 1 + src/main/main-app.ts | 15 +- src/main/main.ts | 63 +++++- .../pages/home/components/network-logs.tsx | 18 +- src/renderer/pages/home/shared/i18n.tsx | 5 + src/renderer/pages/home/shared/logs.ts | 12 +- src/renderer/pages/home/shared/usage.ts | 4 +- .../pages/tray/components/account-panel.tsx | 15 +- tests/renderer/usage-format.test.ts | 45 ++++ 10 files changed, 365 insertions(+), 25 deletions(-) create mode 100644 build/verify-packaged-app.cjs create mode 100644 tests/renderer/usage-format.test.ts diff --git a/build/verify-packaged-app.cjs b/build/verify-packaged-app.cjs new file mode 100644 index 0000000..0d6371a --- /dev/null +++ b/build/verify-packaged-app.cjs @@ -0,0 +1,212 @@ +const fs = require("node:fs"); +const path = require("node:path"); + +const betterSqliteNativeRelativePath = path.join( + "app.asar.unpacked", + "node_modules", + "better-sqlite3", + "build", + "Release", + "better_sqlite3.node" +); + +module.exports = async function verifyPackagedApp(context) { + const platform = context?.electronPlatformName; + const arch = normalizeArch(context?.arch); + const appOutDir = context?.appOutDir; + + if (!platform || !appOutDir) { + throw new Error("Packaged app verification did not receive electron-builder platform context."); + } + + const resourcesDir = findResourcesDir(appOutDir, platform); + assertFile(path.join(resourcesDir, "app.asar"), "Packaged app archive"); + + const nativeModule = path.join(resourcesDir, betterSqliteNativeRelativePath); + assertFile(nativeModule, "better-sqlite3 native module"); + + const nativeInfo = inspectNativeModule(nativeModule); + if (nativeInfo.platform !== platform) { + throw new Error( + `Packaged better-sqlite3 native module targets ${formatNativeInfo(nativeInfo)}, ` + + `but electron-builder is packaging ${platform}/${arch}. Rebuild native dependencies for the target platform before packaging.` + ); + } + + if (arch && !nativeArchMatches(nativeInfo, arch)) { + throw new Error( + `Packaged better-sqlite3 native module targets ${formatNativeInfo(nativeInfo)}, ` + + `but electron-builder is packaging ${platform}/${arch}. Run npm run rebuild:sqlite3 on the target platform before packaging.` + ); + } +}; + +function findResourcesDir(appOutDir, platform) { + if (platform !== "darwin") { + return path.join(appOutDir, "resources"); + } + + const appBundle = fs.readdirSync(appOutDir) + .find((entry) => entry.endsWith(".app") && fs.statSync(path.join(appOutDir, entry)).isDirectory()); + if (!appBundle) { + throw new Error(`Could not find a .app bundle in ${appOutDir}`); + } + return path.join(appOutDir, appBundle, "Contents", "Resources"); +} + +function assertFile(file, label) { + let stat; + try { + stat = fs.statSync(file); + } catch { + throw new Error(`${label} is missing: ${file}`); + } + + if (!stat.isFile() || stat.size === 0) { + throw new Error(`${label} is empty or not a file: ${file}`); + } +} + +function inspectNativeModule(file) { + const buffer = fs.readFileSync(file); + if (buffer.length < 32) { + throw new Error(`Native module is too small to identify: ${file}`); + } + + if (buffer[0] === 0x4d && buffer[1] === 0x5a) { + return inspectPe(buffer, file); + } + if (buffer[0] === 0x7f && buffer[1] === 0x45 && buffer[2] === 0x4c && buffer[3] === 0x46) { + return inspectElf(buffer); + } + + const magicLe = buffer.readUInt32LE(0); + const magicBe = buffer.readUInt32BE(0); + if (magicLe === 0xfeedface || magicLe === 0xfeedfacf || magicLe === 0xcefaedfe || magicLe === 0xcffaedfe) { + return inspectMachO(buffer, magicLe); + } + if (magicBe === 0xcafebabe || magicBe === 0xcafebabf) { + return inspectFatMachO(buffer, magicBe); + } + + throw new Error(`Native module has an unknown binary format: ${file}`); +} + +function inspectPe(buffer, file) { + const peOffset = buffer.readUInt32LE(0x3c); + if (peOffset + 6 >= buffer.length || buffer.toString("ascii", peOffset, peOffset + 4) !== "PE\0\0") { + throw new Error(`Native module has an invalid PE header: ${file}`); + } + const machine = buffer.readUInt16LE(peOffset + 4); + return { + arch: peMachineArch(machine), + platform: "win32" + }; +} + +function inspectElf(buffer) { + const machine = buffer.readUInt16LE(18); + return { + arch: elfMachineArch(machine), + platform: "linux" + }; +} + +function inspectMachO(buffer, magicLe) { + const bigEndian = magicLe === 0xcefaedfe || magicLe === 0xcffaedfe; + const cpuType = bigEndian ? buffer.readUInt32BE(4) : buffer.readUInt32LE(4); + return { + arch: machCpuArch(cpuType), + platform: "darwin" + }; +} + +function inspectFatMachO(buffer, magicBe) { + const archs = []; + const count = buffer.readUInt32BE(4); + const entrySize = magicBe === 0xcafebabf ? 32 : 20; + for (let index = 0; index < count; index += 1) { + const offset = 8 + index * entrySize; + if (offset + 8 > buffer.length) { + break; + } + archs.push(machCpuArch(buffer.readUInt32BE(offset))); + } + return { + arch: "universal", + archs, + platform: "darwin" + }; +} + +function peMachineArch(machine) { + if (machine === 0x8664) { + return "x64"; + } + if (machine === 0xaa64) { + return "arm64"; + } + if (machine === 0x014c) { + return "ia32"; + } + return `unknown-pe-${machine.toString(16)}`; +} + +function elfMachineArch(machine) { + if (machine === 0x3e) { + return "x64"; + } + if (machine === 0xb7) { + return "arm64"; + } + if (machine === 0x03) { + return "ia32"; + } + if (machine === 0x28) { + return "armv7l"; + } + return `unknown-elf-${machine.toString(16)}`; +} + +function machCpuArch(cpuType) { + if (cpuType === 0x01000007) { + return "x64"; + } + if (cpuType === 0x0100000c) { + return "arm64"; + } + if (cpuType === 0x00000007) { + return "ia32"; + } + return `unknown-macho-${cpuType.toString(16)}`; +} + +function normalizeArch(arch) { + if (typeof arch === "string") { + return arch; + } + const electronBuilderArchNames = new Map([ + [0, "ia32"], + [1, "x64"], + [2, "armv7l"], + [3, "arm64"], + [4, "universal"] + ]); + return electronBuilderArchNames.get(arch); +} + +function nativeArchMatches(info, arch) { + if (info.arch === arch) { + return true; + } + if (info.arch === "universal") { + return arch === "universal" || Boolean(info.archs?.includes(arch)); + } + return false; +} + +function formatNativeInfo(info) { + return info.arch === "universal" && info.archs?.length + ? `${info.platform}/${info.arch} (${info.archs.join(", ")})` + : `${info.platform}/${info.arch}`; +} diff --git a/electron-builder.json b/electron-builder.json index 2918119..b0a4bf0 100644 --- a/electron-builder.json +++ b/electron-builder.json @@ -16,6 +16,7 @@ "directories": { "output": "release/${version}" }, + "afterPack": "build/verify-packaged-app.cjs", "afterAllArtifactBuild": "build/verify-update-metadata.cjs", "protocols": [ { diff --git a/src/main/main-app.ts b/src/main/main-app.ts index 3fedf93..9c1d9ca 100644 --- a/src/main/main-app.ts +++ b/src/main/main-app.ts @@ -36,7 +36,7 @@ function startPrimaryInstance(): void { queueEnsureConfiguredProxyModeActive("second-instance"); }); - app.whenReady().then(() => { + void app.whenReady().then(() => { configureProxyDesktopIntegration(); try { ensureCcrCliLauncher(); @@ -57,6 +57,15 @@ function startPrimaryInstance(): void { } queueEnsureConfiguredProxyModeActive("activate"); }); + }).catch((error) => { + const detail = formatErrorDetail(error); + console.error(`Failed to initialize ${app.name || "application"}: ${detail}`); + try { + dialog.showErrorBox("Claude Code Router failed to start", detail); + } catch { + // Keep the console log as the fallback diagnostic channel. + } + app.exit(1); }); app.on("before-quit", (event) => { @@ -247,3 +256,7 @@ function logProxySystemProxyIssue(reason: string, status: ReturnType { - console.error(error instanceof Error ? error.message : String(error)); - app.quit(); -}); +let fatalStartupErrorReported = false; + +process.once("uncaughtException", reportFatalStartupError); +process.once("unhandledRejection", reportFatalStartupError); + +void import("./main-app.js") + .then(() => { + process.off("uncaughtException", reportFatalStartupError); + process.off("unhandledRejection", reportFatalStartupError); + }) + .catch((error) => { + reportFatalStartupError(error); + }); + +function reportFatalStartupError(error: unknown): void { + if (fatalStartupErrorReported) { + return; + } + fatalStartupErrorReported = true; + + const detail = formatErrorDetail(error); + console.error(detail); + + try { + dialog.showErrorBox("Claude Code Router failed to start", startupErrorMessage(detail)); + } catch { + // If the platform dialog is unavailable, the console output above still + // preserves the actionable failure for command-line launches. + } + + app.exit(1); +} + +function startupErrorMessage(detail: string): string { + if (isBetterSqliteNativeError(detail)) { + return [ + "The bundled SQLite native module could not be loaded.", + "", + "This usually means the Windows package was built with a missing or incompatible better-sqlite3 binary. Rebuild the app with npm run rebuild:sqlite3 before packaging, or build the Windows artifact on a Windows x64 runner.", + "", + detail + ].join("\n"); + } + + return detail; +} + +function isBetterSqliteNativeError(detail: string): boolean { + return /better[-_]sqlite3|better_sqlite3\.node|NODE_MODULE_VERSION|ERR_DLOPEN_FAILED|Cannot find module ['"]better-sqlite3/i.test(detail); +} + +function formatErrorDetail(error: unknown): string { + if (error instanceof Error) { + return error.stack || error.message; + } + return String(error); +} diff --git a/src/renderer/pages/home/components/network-logs.tsx b/src/renderer/pages/home/components/network-logs.tsx index da6e060..48117dc 100644 --- a/src/renderer/pages/home/components/network-logs.tsx +++ b/src/renderer/pages/home/components/network-logs.tsx @@ -10,7 +10,7 @@ import { Pause, Play, ProxyNetworkBody, ProxyNetworkExchange, ProxyNetworkSnapshot, ProxyStatus, ReactNode, ReactPointerEvent, RefreshCw, RequestLogBody, RequestLogEntry, RequestLogListFilter, RequestLogPage, requestLogPageSizeOptions, RequestLogStatusFilter, requestLogStatusOptions, Search, Select, - translateOptions, Trash2, useAppText, useCallback, useEffect, useMemo, useRef, + translateOptions, Trash2, useAppNumberLocale, useAppText, useCallback, useEffect, useMemo, useRef, useState } from "../shared"; type NetworkRequestTab = "body" | "header" | "query" | "raw" | "summary"; @@ -489,8 +489,9 @@ const LogRow = memo(function LogRow({ onToggle: (id: number) => void; }) { const t = useAppText(); + const numberLocale = useAppNumberLocale(); const createdAt = useMemo(() => formatLogDateTime(item.createdAt), [item.createdAt]); - const tokenSummary = useMemo(() => formatLogTokenSummary(item, t), [item, t]); + const tokenSummary = useMemo(() => formatLogTokenSummary(item, t, numberLocale), [item, numberLocale, t]); return (
@@ -542,6 +543,7 @@ function LogExpandedDetails({ entry: RequestLogEntry; }) { const t = useAppText(); + const numberLocale = useAppNumberLocale(); const hasCredentialInfo = logHasCredentialInfo(entry); return ( @@ -564,12 +566,12 @@ function LogExpandedDetails({ {entry.credentialChain.length ? ")} /> : null} {hasCredentialInfo ? : null} {entry.retryAttempts.length > 0 ? : null} - - - - - - + + + + + +
{entry.retryAttempts.length > 0 ? : null} diff --git a/src/renderer/pages/home/shared/i18n.tsx b/src/renderer/pages/home/shared/i18n.tsx index bedf620..7de0745 100644 --- a/src/renderer/pages/home/shared/i18n.tsx +++ b/src/renderer/pages/home/shared/i18n.tsx @@ -1563,6 +1563,11 @@ export function useAppText() { return useMemo(() => (value: string) => translateText(copy, value), [copy]); } +export function useAppNumberLocale(): Intl.LocalesArgument { + const copy = useContext(AppI18nContext); + return copy === appCopy.zh ? "zh-CN" : "en-US"; +} + export function translateText(copy: AppCopy, value: string): string { return copy.text[value] ?? value; } diff --git a/src/renderer/pages/home/shared/logs.ts b/src/renderer/pages/home/shared/logs.ts index c9b603b..9066f1d 100644 --- a/src/renderer/pages/home/shared/logs.ts +++ b/src/renderer/pages/home/shared/logs.ts @@ -393,7 +393,7 @@ export function formatLogDateTime(value: string): string { return `${year}-${month}-${day} ${hours}:${minutes}:${seconds} ${offset}`; } -export function formatLogTokenSummary(entry: RequestLogEntry, t: (value: string) => string): string { +export function formatLogTokenSummary(entry: RequestLogEntry, t: (value: string) => string, locale?: Intl.LocalesArgument): string { if ( entry.totalTokens === 0 && entry.inputTokens === 0 && @@ -405,18 +405,18 @@ export function formatLogTokenSummary(entry: RequestLogEntry, t: (value: string) return "-"; } const values = [ - `${formatCompactNumber(entry.inputTokens)} ${t("入")}`, - `${formatCompactNumber(entry.outputTokens)} ${t("出")}` + `${formatCompactNumber(entry.inputTokens, locale)} ${t("入")}`, + `${formatCompactNumber(entry.outputTokens, locale)} ${t("出")}` ]; if (entry.cacheReadTokens > 0) { - values.push(`${formatCompactNumber(entry.cacheReadTokens)} ${t("Cache")}`); + values.push(`${formatCompactNumber(entry.cacheReadTokens, locale)} ${t("Cache")}`); } if (entry.cacheWriteTokens > 0) { - values.push(`${formatCompactNumber(entry.cacheWriteTokens)} ${t("Cache write")}`); + values.push(`${formatCompactNumber(entry.cacheWriteTokens, locale)} ${t("Cache write")}`); } if (entry.reasoningTokens > 0) { - values.push(`${formatCompactNumber(entry.reasoningTokens)} ${t("Thinking")}`); + values.push(`${formatCompactNumber(entry.reasoningTokens, locale)} ${t("Thinking")}`); } return values.join(" "); diff --git a/src/renderer/pages/home/shared/usage.ts b/src/renderer/pages/home/shared/usage.ts index dd01129..76cc6c6 100644 --- a/src/renderer/pages/home/shared/usage.ts +++ b/src/renderer/pages/home/shared/usage.ts @@ -137,8 +137,8 @@ export function emptyUsageTotals(): UsageTotals { }; } -export function formatCompactNumber(value: number): string { - return new Intl.NumberFormat(undefined, { +export function formatCompactNumber(value: number, locale?: Intl.LocalesArgument): string { + return new Intl.NumberFormat(locale, { maximumFractionDigits: value >= 1000 ? 1 : 0, notation: value >= 10000 ? "compact" : "standard" }).format(value); diff --git a/src/renderer/pages/tray/components/account-panel.tsx b/src/renderer/pages/tray/components/account-panel.tsx index 75b17f4..10ad8f6 100644 --- a/src/renderer/pages/tray/components/account-panel.tsx +++ b/src/renderer/pages/tray/components/account-panel.tsx @@ -1,5 +1,5 @@ import { - accountMetersForDisplay, accountProgressClass, accountProgressColor, accountSnapshotLabel, accountStatusClass, compareAccountSnapshots, formatAccountMeterTitle, formatAccountMeterValue, + accountMetersForDisplay, accountProgressClass, accountProgressColor, accountSnapshotLabel, compareAccountSnapshots, formatAccountMeterTitle, formatAccountMeterValue, LoaderCircle, meterProgress, meterRemainingRatio, ProviderAccountMeter, ProviderAccountSnapshot, RefreshCw, TrayComponentVariants, useTrayText } from "../shared"; @@ -38,7 +38,7 @@ export function AccountSummaryPanel({

{accountSnapshotLabel(snapshot)}

))} @@ -436,6 +450,7 @@ function OverviewWidgetProperties({ onChangeBreakdownData, onChangeCategory, onChangeMetric, + onChangeShareData, onChangeSize, onChangeVariant, onRemove @@ -447,6 +462,7 @@ function OverviewWidgetProperties({ onChangeBreakdownData: (type: "model-distribution" | "token-mix") => void; onChangeCategory: (category: OverviewWidgetCategory) => void; onChangeMetric: (metric: OverviewMetricKind) => void; + onChangeShareData: (type: ShareOverviewWidgetType) => void; onChangeSize: (size: OverviewWidgetSize) => void; onChangeVariant: (variant: OverviewWidgetVariant) => void; onRemove: () => void; @@ -480,6 +496,9 @@ function OverviewWidgetProperties({ if (category === "breakdown") { onChangeBreakdownData(value as "model-distribution" | "token-mix"); } + if (category === "share-card") { + onChangeShareData(value as ShareOverviewWidgetType); + } }; return ( @@ -847,6 +866,8 @@ function OverviewWidgetRenderer({ content = ; } else if (widget.type === "client-analysis") { content = ; + } else if (isShareOverviewWidgetType(widget.type)) { + content = ; } else { content = ; } @@ -1464,11 +1485,19 @@ function overviewWidgetTemplates(): OverviewWidgetConfig[] { { enabled: true, id: "usage-trend", size: "3:2", type: "usage-trend", variant: "composed" }, { enabled: true, id: "token-activity", size: "4:2", type: "token-activity", variant: "heatmap" }, { enabled: true, id: "token-mix", size: "1:2", type: "token-mix", variant: "bars" }, - { enabled: true, id: "client-analysis", size: "2:2", type: "client-analysis", variant: "table" } + { enabled: true, id: "client-analysis", size: "2:2", type: "client-analysis", variant: "table" }, + { enabled: true, id: "share-usage-wrapped", size: "1:4", type: "share-usage-wrapped", variant: "card" }, + { enabled: true, id: "share-route-map", size: "1:4", type: "share-route-map", variant: "card" }, + { enabled: true, id: "share-model-leaderboard", size: "1:4", type: "share-model-leaderboard", variant: "card" }, + { enabled: true, id: "share-fuel-cockpit", size: "1:4", type: "share-fuel-cockpit", variant: "card" }, + { enabled: true, id: "share-token-calendar", size: "1:4", type: "share-token-calendar", variant: "card" }, + { enabled: true, id: "share-spend-receipt", size: "1:4", type: "share-spend-receipt", variant: "card" } ]; } -type OverviewWidgetCategory = "account-balance" | "activity" | "analysis" | "breakdown" | "metric" | "system-status" | "usage-trend"; +type ShareOverviewWidgetType = Extract; + +type OverviewWidgetCategory = "account-balance" | "activity" | "analysis" | "breakdown" | "metric" | "share-card" | "system-status" | "usage-trend"; function overviewWidgetCategoryOptions(): Array<{ label: string; value: OverviewWidgetCategory }> { return [ @@ -1478,7 +1507,8 @@ function overviewWidgetCategoryOptions(): Array<{ label: string; value: Overview "usage-trend", "activity", "breakdown", - "analysis" + "analysis", + "share-card" ].map((category) => ({ label: overviewWidgetCategoryLabel(category as OverviewWidgetCategory), value: category as OverviewWidgetCategory @@ -1499,6 +1529,17 @@ function overviewBreakdownDataOptions(): Array<{ label: string; value: "model-di ]; } +function overviewShareCardDataOptions(): Array<{ label: string; value: ShareOverviewWidgetType }> { + return [ + { label: "AI Usage Wrapped", value: "share-usage-wrapped" }, + { label: "CCR Route Map", value: "share-route-map" }, + { label: "Model Leaderboard", value: "share-model-leaderboard" }, + { label: "AI Fuel Cockpit", value: "share-fuel-cockpit" }, + { label: "Token Calendar Poster", value: "share-token-calendar" }, + { label: "Spend Receipt", value: "share-spend-receipt" } + ]; +} + function overviewWidgetDataOptions(widget: OverviewWidgetConfig, providerAccounts: ProviderAccountSnapshot[]): Array<{ label: string; value: string }> { const category = overviewWidgetCategory(widget.type); if (category === "metric") { @@ -1526,6 +1567,9 @@ function overviewWidgetDataOptions(widget: OverviewWidgetConfig, providerAccount if (category === "breakdown") { return overviewBreakdownDataOptions(); } + if (category === "share-card") { + return overviewShareCardDataOptions(); + } return [{ label: "Usage over time", value: "usage-trend" }]; } @@ -1546,6 +1590,9 @@ function overviewWidgetDataValue(widget: OverviewWidgetConfig): string { if (category === "activity") { return "token-activity"; } + if (category === "share-card") { + return widget.type; + } return category; } @@ -1559,6 +1606,9 @@ function overviewWidgetCategory(type: OverviewWidgetType): OverviewWidgetCategor if (type === "token-activity") { return "activity"; } + if (isShareOverviewWidgetType(type)) { + return "share-card"; + } return type; } @@ -1572,10 +1622,16 @@ function overviewWidgetTypeForCategory(category: OverviewWidgetCategory, current if (category === "activity") { return "token-activity"; } + if (category === "share-card") { + return isShareOverviewWidgetType(currentType) ? currentType : "share-usage-wrapped"; + } return category; } function overviewWidgetTemplateKey(widget: OverviewWidgetConfig): string { + if (isShareOverviewWidgetType(widget.type)) { + return widget.type; + } return overviewWidgetCategory(widget.type); } @@ -1584,6 +1640,7 @@ function overviewWidgetCategoryLabel(category: OverviewWidgetCategory): string { if (category === "analysis") return "Analysis component"; if (category === "activity") return "Activity component"; if (category === "metric") return "Metric component"; + if (category === "share-card") return "Share card"; if (category === "system-status") return "Status component"; if (category === "breakdown") return "Breakdown component"; return "Trend component"; @@ -1594,11 +1651,24 @@ function overviewWidgetCategoryDescription(category: OverviewWidgetCategory): st if (category === "analysis") return "Client or provider"; if (category === "activity") return "Token activity heatmap"; if (category === "metric") return "Requests, tokens, cost"; + if (category === "share-card") return "Social media PNG cards"; if (category === "system-status") return "Status timeline"; if (category === "breakdown") return "Token or model distribution"; return "Usage over time"; } +function overviewWidgetPaletteTitle(widget: OverviewWidgetConfig): string { + return isShareOverviewWidgetType(widget.type) + ? overviewWidgetTypeLabel(widget.type) + : overviewWidgetCategoryLabel(overviewWidgetCategory(widget.type)); +} + +function overviewWidgetPaletteDescription(widget: OverviewWidgetConfig): string { + return isShareOverviewWidgetType(widget.type) + ? overviewWidgetCategoryLabel("share-card") + : overviewWidgetCategoryDescription(overviewWidgetCategory(widget.type)); +} + function overviewWidgetTitle(widget: OverviewWidgetConfig, translate: (value: string) => string): string { if (widget.type === "metric") { return translate(overviewMetricLabel(widget.metric ?? "requests")); @@ -1612,6 +1682,12 @@ function overviewWidgetTypeLabel(type: OverviewWidgetType): string { if (type === "metric") return "Metric"; if (type === "model-distribution") return "Model Distribution"; if (type === "provider-analysis") return "Provider Analysis"; + if (type === "share-fuel-cockpit") return "AI Fuel Cockpit"; + if (type === "share-model-leaderboard") return "Model Leaderboard"; + if (type === "share-route-map") return "CCR Route Map"; + if (type === "share-spend-receipt") return "Spend Receipt"; + if (type === "share-token-calendar") return "Token Calendar Poster"; + if (type === "share-usage-wrapped") return "AI Usage Wrapped"; if (type === "system-status") return "System status"; if (type === "token-activity") return "Activity"; if (type === "token-mix") return "Token Mix"; @@ -1665,12 +1741,26 @@ function overviewWidgetVariantOptions(type: OverviewWidgetType): Array<{ label: { label: "Compact", value: "compact" } ]; } + if (isShareOverviewWidgetType(type)) { + return [ + { label: "Card", value: "card" } + ]; + } return [ { label: "Table", value: "table" }, { label: "Compact", value: "compact" } ]; } +function isShareOverviewWidgetType(type: OverviewWidgetType): type is ShareOverviewWidgetType { + return type === "share-fuel-cockpit" || + type === "share-model-leaderboard" || + type === "share-route-map" || + type === "share-spend-receipt" || + type === "share-token-calendar" || + type === "share-usage-wrapped"; +} + function overviewWidgetSizeClass(size: OverviewWidgetSize): string { const { height, width } = overviewWidgetDimensions(size); return cn(overviewWidgetWidthClass(width), overviewWidgetHeightClass(height)); diff --git a/src/renderer/pages/home/components/providers.tsx b/src/renderer/pages/home/components/providers.tsx index 4cd8d89..fa2ee33 100644 --- a/src/renderer/pages/home/components/providers.tsx +++ b/src/renderer/pages/home/components/providers.tsx @@ -2,7 +2,7 @@ import { AddProviderDraft, AnimatedDisclosure, AnimatedIconSwap, AnimatedListItem, AnimatedPopover, AnimatePresence, AppConfig, Badge, Box, Braces, Button, Card, CardContent, CardHeader, CardTitle, Check, Checkbox, ChevronDown, ChevronRight, CircleAlert, cn, - compareProviderAccountSnapshots, Copy, copyTextToClipboard, createDefaultProviderAccountDraft, createModelCatalogItems, createProviderAccountDraftFromConfig, createProviderCredentialDraft, createProviderInstallLinkFromDraft, + compareProviderAccountSnapshots, copyTextToClipboard, createDefaultProviderAccountDraft, createModelCatalogItems, createProviderAccountDraftFromConfig, createProviderCredentialDraft, customProviderPresetId, defaultProviderAccountConfigForPreset, Dialog, DialogBody, DialogContent, DialogFooter, DialogHeader, DialogTitle, ExternalLink, Field, findProviderPreset, formatProviderAccountMeterValue, GatewayProviderConfig, GatewayProviderProbeResult, getProviderPresets, Globe, inferProviderNameFromBaseUrl, Input, KeyValueRowsControl, Label, @@ -624,7 +624,7 @@ function ProviderPresetCombobox({ const [query, setQuery] = useState(""); const rootRef = useRef(null); const inputRef = useRef(null); - const selected = options.find((option) => option.value === value) ?? options[0]; + const selected = options.find((option) => option.value === value) ?? options.find((option) => option.value === ""); const normalizedQuery = query.trim().toLowerCase(); const filteredOptions = normalizedQuery ? options.filter((option) => providerPresetOptionMatchesQuery(option, normalizedQuery)) @@ -1232,9 +1232,9 @@ export function AddProviderForm({ const safetyIssue = providerDraftSafetyIssue(draft, detectedBaseUrl); const localAgentImport = draft.providerPlugins.length > 0; const providerPresetOptions = [ + { iconUrl: draft.icon, label: t("Other / custom API endpoint"), value: customProviderPresetId }, { label: t("Select preset provider"), value: "" }, - ...getProviderPresets().map((preset) => ({ label: t(preset.name), preset, value: preset.id })), - { iconUrl: draft.icon, label: t("Other / custom API endpoint"), value: customProviderPresetId } + ...getProviderPresets().map((preset) => ({ label: t(preset.name), preset, value: preset.id })) ]; const selectableProtocols = providerSelectableProtocolsFromProbe(probe); const hasConnectivityCheckInputs = Boolean( @@ -1800,7 +1800,6 @@ function ProviderUsageSettings({ const [testLoading, setTestLoading] = useState(false); const [testResult, setTestResult] = useState(); const [testError, setTestError] = useState(""); - const [copiedLink, setCopiedLink] = useState(false); const modeOptions = translateOptions(providerAccountModeOptions, t); useEffect(() => { @@ -1808,14 +1807,6 @@ function ProviderUsageSettings({ setTestError(""); }, [draft.accountMode, draft.usageRequestUrl, draft.usageRequestMethod]); - useEffect(() => { - if (!copiedLink) { - return; - } - const timer = window.setTimeout(() => setCopiedLink(false), 1300); - return () => window.clearTimeout(timer); - }, [copiedLink]); - async function testUsageRequest() { if (!window.ccr?.testProviderAccountConnector) { setTestError(t("Request failed.")); @@ -1855,17 +1846,6 @@ function ProviderUsageSettings({ } } - async function copyProviderPluginLink() { - const link = createProviderInstallLinkFromDraft(draft, probe); - if (typeof link === "string" && link.startsWith("ccr://")) { - await copyTextToClipboard(link); - setCopiedLink(true); - setTestError(""); - return; - } - setTestError(formatError(new Error(link))); - } - function selectPath(target: ProviderUsageFieldTarget, path: string) { onChange(providerUsageFieldPatch(target, path)); } @@ -1880,12 +1860,6 @@ function ProviderUsageSettings({ /> {t("Fetch usage")} -

{draft.accountEnabled ? ( diff --git a/src/renderer/pages/home/components/share-cards.tsx b/src/renderer/pages/home/components/share-cards.tsx new file mode 100644 index 0000000..3a68aba --- /dev/null +++ b/src/renderer/pages/home/components/share-cards.tsx @@ -0,0 +1,775 @@ +import { + Button, cn, Download, formatCompactNumber, formatDuration, formatPercent, formatProviderAccountMeterTitle, + formatProviderAccountMeterValue, formatUsdCost, LoaderCircle, primaryProviderAccountMeter, providerAccountMeterRemainingRatio, + providerAccountSnapshotKey, ReactNode, UsageStatsRange, UsageStatsSnapshot, useRef, useState, + useAppText, type OverviewWidgetType, type ProviderAccountMeter, type ProviderAccountSnapshot +} from "../shared"; +import { buildTokenActivity, type TokenActivityCell } from "@/lib/usage-activity"; + +type ShareCardTone = "amber" | "blue" | "emerald" | "indigo" | "rose" | "slate" | "teal"; + +type ShareCardWidgetProps = { + providerAccounts: ProviderAccountSnapshot[]; + type: OverviewWidgetType; + usageRange: UsageStatsRange; + usageStats: UsageStatsSnapshot; +}; + +type ShareCardPngExportOptions = { + exportId?: string; + output?: { + height: number; + width: number; + }; +}; + +type ShareCardPreparedExportTarget = { + canceled: boolean; + exportId?: string; +}; + +const shareCardExportCssWidth = 540; +const shareCardExportCssHeight = 960; +const shareCardExportPixelWidth = 1080; +const shareCardExportPixelHeight = 1920; + +const shareCardTones: Record = { + amber: { + accent: "#f59e0b", + background: "linear-gradient(135deg, #fffbeb 0%, #fef3c7 42%, #fff7ed 100%)", + border: "border-amber-200", + glow: "bg-amber-300/28", + muted: "text-amber-900/62", + text: "text-amber-950" + }, + blue: { + accent: "#2563eb", + background: "linear-gradient(135deg, #eff6ff 0%, #dbeafe 44%, #ecfeff 100%)", + border: "border-blue-200", + glow: "bg-blue-300/30", + muted: "text-blue-950/62", + text: "text-blue-950" + }, + emerald: { + accent: "#059669", + background: "linear-gradient(135deg, #ecfdf5 0%, #d1fae5 42%, #f0fdfa 100%)", + border: "border-emerald-200", + glow: "bg-emerald-300/28", + muted: "text-emerald-950/62", + text: "text-emerald-950" + }, + indigo: { + accent: "#4f46e5", + background: "linear-gradient(135deg, #eef2ff 0%, #e0e7ff 46%, #f5f3ff 100%)", + border: "border-indigo-200", + glow: "bg-indigo-300/30", + muted: "text-indigo-950/62", + text: "text-indigo-950" + }, + rose: { + accent: "#e11d48", + background: "linear-gradient(135deg, #fff1f2 0%, #ffe4e6 45%, #fff7ed 100%)", + border: "border-rose-200", + glow: "bg-rose-300/28", + muted: "text-rose-950/62", + text: "text-rose-950" + }, + slate: { + accent: "#475569", + background: "linear-gradient(135deg, #f8fafc 0%, #e2e8f0 48%, #f1f5f9 100%)", + border: "border-slate-200", + glow: "bg-slate-300/32", + muted: "text-slate-700", + text: "text-slate-950" + }, + teal: { + accent: "#0f766e", + background: "linear-gradient(135deg, #f0fdfa 0%, #ccfbf1 42%, #ecfeff 100%)", + border: "border-teal-200", + glow: "bg-teal-300/30", + muted: "text-teal-950/62", + text: "text-teal-950" + } +}; + +export function ShareCardWidget({ + providerAccounts, + type, + usageRange, + usageStats +}: ShareCardWidgetProps) { + const t = useAppText(); + const definition = shareCardDefinition(type); + + if (!definition) { + return null; + } + + return ( + + {type === "share-usage-wrapped" ? : null} + {type === "share-route-map" ? : null} + {type === "share-model-leaderboard" ? : null} + {type === "share-fuel-cockpit" ? : null} + {type === "share-token-calendar" ? : null} + {type === "share-spend-receipt" ? : null} + + ); +} + +function shareCardDefinition(type: OverviewWidgetType): { fileName: string; title: string } | undefined { + if (type === "share-usage-wrapped") return { fileName: "ccr-ai-usage-wrapped.png", title: "AI Usage Wrapped" }; + if (type === "share-route-map") return { fileName: "ccr-route-map.png", title: "CCR Route Map" }; + if (type === "share-model-leaderboard") return { fileName: "ccr-model-leaderboard.png", title: "Model Leaderboard" }; + if (type === "share-fuel-cockpit") return { fileName: "ccr-ai-fuel-cockpit.png", title: "AI Fuel Cockpit" }; + if (type === "share-token-calendar") return { fileName: "ccr-token-calendar-poster.png", title: "Token Calendar Poster" }; + if (type === "share-spend-receipt") return { fileName: "ccr-spend-receipt.png", title: "Spend Receipt" }; + return undefined; +} + +function ShareCardShell({ + children, + fileName, + title +}: { + children: ReactNode; + fileName: string; + title: string; +}) { + const t = useAppText(); + const cardRef = useRef(null); + const exportCardRef = useRef(null); + const [exporting, setExporting] = useState(false); + const [status, setStatus] = useState<"error" | "idle" | "saving">("idle"); + + async function saveImage() { + if (status === "saving") { + return; + } + setStatus("saving"); + let exportTarget: ShareCardPreparedExportTarget | undefined; + try { + if (window.ccr?.prepareImageExportTarget) { + exportTarget = await window.ccr.prepareImageExportTarget({ fileName }); + if (exportTarget.canceled) { + setStatus("idle"); + return; + } + } + setExporting(true); + await nextFrame(); + await nextFrame(); + const target = exportCardRef.current ?? cardRef.current; + if (!target) { + throw new Error("Export card is unavailable."); + } + await saveElementAsPng(target, fileName, { + exportId: exportTarget?.exportId, + output: { + height: shareCardExportPixelHeight, + width: shareCardExportPixelWidth + } + }); + setStatus("idle"); + } catch (error) { + console.error(error); + setStatus("error"); + } finally { + setExporting(false); + } + } + + return ( +
+
+
{title}
+
+ {status === "error" ? {t("Export failed")} : null} + +
+
+
+ {children} +
+ {exporting ? ( + + ) : null} +
+ ); +} + +function UsageWrappedCard({ usageStats }: { usageStats: UsageStatsSnapshot }) { + const t = useAppText(); + const totals = usageStats.totals; + const topModel = usageStats.models[0]; + const topProvider = usageStats.providerModels[0]; + const bestDay = [...usageStats.series].sort((a, b) => b.totalTokens - a.totalTokens)[0]; + const activity = buildTokenActivity(usageStats.series, { maxWeeks: 12, minWeeks: 8 }); + + return ( + + +
+
{formatCompactNumber(totals.totalTokens)}
+
{t("tokens routed through CCR")}
+
+
+ + + + +
+
+ + + +
+ +
+ ); +} + +function RouteMapCard({ usageStats }: { usageStats: UsageStatsSnapshot }) { + const t = useAppText(); + const routes = buildRouteRows(usageStats, t); + const total = routes.reduce((sum, row) => sum + row.value, 0) || 1; + + return ( + + +
+
+ {t("Client")} + CCR + {t("Model")} +
+
+ {routes.length > 0 ? routes.slice(0, 5).map((route, index) => { + const share = Math.max(8, (route.value / total) * 100); + return ( +
+ +
+ + + {index + 1} +
+ +
+ ); + }) : ( +
+ {t("No route activity")} +
+ )} +
+
+
+ row.client)))} /> + row.provider)))} /> + row.model)))} /> +
+ +
+ ); +} + +function ModelLeaderboardCard({ usageStats }: { usageStats: UsageStatsSnapshot }) { + const t = useAppText(); + const rows = usageStats.models.filter((row) => row.totalTokens > 0).slice(0, 5); + const max = Math.max(...rows.map((row) => row.totalTokens), 1); + + return ( + + +
+ {rows.length > 0 ? rows.map((row, index) => ( +
+
+ {index + 1} +
+
{row.label}
+
{row.provider ?? t("All providers")}
+
+
+
{formatPercent(row.maxShare)}
+
{formatCompactNumber(row.totalTokens)}
+
+
+
+
+
+
+ )) : ( + + )} +
+ + + ); +} + +function FuelCockpitCard({ providerAccounts }: { providerAccounts: ProviderAccountSnapshot[] }) { + const t = useAppText(); + const accountRows = providerAccounts + .filter((account) => account.meters.length > 0) + .map((account) => ({ + account, + meter: fuelMeter(account) + })) + .filter((row): row is { account: ProviderAccountSnapshot; meter: ProviderAccountMeter } => Boolean(row.meter)) + .slice(0, 3); + + return ( + + +
+ {accountRows.length > 0 ? accountRows.map(({ account, meter }, index) => { + const ratio = providerAccountMeterRemainingRatio(meter) ?? (meter.kind === "balance" ? 1 : 0); + return ( +
+ +
+
{safeProviderName(account.provider, t)}
+
{formatProviderAccountMeterTitle(meter, t)}
+
{formatProviderAccountMeterValue(meter)}
+
+
+ ); + }) : ( + + )} +
+ +
+ ); +} + +function TokenCalendarPosterCard({ usageStats }: { usageStats: UsageStatsSnapshot }) { + const t = useAppText(); + const activity = buildTokenActivity(usageStats.series, { maxWeeks: 26, minWeeks: 18 }); + + return ( + + +
+
+ {activity.cells.map((cell) => ( + + ))} +
+
+
+ + + +
+
+ {t("Less")} + {[0, 1, 2, 3, 4].map((intensity) => ( + + ))} + {t("More")} +
+ +
+ ); +} + +function SpendReceiptCard({ + usageRange, + usageStats +}: { + usageRange: UsageStatsRange; + usageStats: UsageStatsSnapshot; +}) { + const t = useAppText(); + const totals = usageStats.totals; + const rows = [ + { label: t("Range"), value: rangeLabel(usageRange, t) }, + { label: t("Requests"), value: formatCompactNumber(totals.requestCount) }, + { label: t("Input tokens"), value: formatCompactNumber(totals.inputTokens) }, + { label: t("Output tokens"), value: formatCompactNumber(totals.outputTokens) }, + { label: t("Cache tokens"), value: formatCompactNumber(totals.cacheTokens) }, + { label: t("Average latency"), value: formatDuration(totals.avgDurationMs) }, + { label: t("Success rate"), value: formatPercent(totals.successRate) } + ]; + + return ( + + +
+
+
{formatUsdCost(totals.costUsd)}
+
{t("Estimated cost")}
+
+
+ {rows.map((row) => ( +
+ {row.label} + {row.value} +
+ ))} +
+
+
{t("Total tokens")}
+
{formatCompactNumber(totals.totalTokens)}
+
+
+ +
+ ); +} + +function SharePoster({ + children, + tone +}: { + children: ReactNode; + tone: ShareCardTone; +}) { + const theme = shareCardTones[tone]; + return ( +
+ + +
+ {children} +
+
+ ); +} + +function SharePosterHeader({ + title, + tone +}: { + title: string; + tone: ShareCardTone; +}) { + const theme = shareCardTones[tone]; + return ( +
+
+

{title}

+
+
+ CCR +
+
+ ); +} + +function SharePosterFooter({ tone }: { tone: ShareCardTone }) { + const t = useAppText(); + const theme = shareCardTones[tone]; + return ( +
+ {t("Generated by CCR")} + {new Date().getFullYear()} +
+ ); +} + +function PosterStat({ label, value }: { label: string; value: string }) { + return ( +
+
{label}
+
{value}
+
+ ); +} + +function PosterHighlight({ label, value }: { label: string; value: string }) { + return ( +
+ {label} + {value} +
+ ); +} + +function EmptyPosterState({ label, tone }: { label: string; tone: ShareCardTone }) { + const theme = shareCardTones[tone]; + return ( +
+ {label} +
+ ); +} + +function RoutePill({ align = "left", label }: { align?: "left" | "right"; label: string }) { + return ( +
+
{label}
+
+ ); +} + +function MiniRouteMetric({ label, value }: { label: string; value: string }) { + return ( +
+
{value}
+
{label}
+
+ ); +} + +function CircularGauge({ color, ratio, value }: { color: string; ratio: number; value: string }) { + const radius = 41; + const circumference = 2 * Math.PI * radius; + const clamped = Math.max(0, Math.min(1, ratio)); + + return ( + + + + + {value} + + ); +} + +function buildRouteRows(usageStats: UsageStatsSnapshot, translate: (value: string) => string): Array<{ client: string; model: string; provider: string; value: number }> { + const rows = usageStats.clientModels + .filter((row) => row.totalTokens > 0) + .map((row) => ({ + client: row.client || row.label || translate("Agent"), + model: row.model || modelNameFromLabel(row.label), + provider: row.provider || translate("Provider"), + value: row.totalTokens + })); + + if (rows.length > 0) { + return rows.sort((a, b) => b.value - a.value); + } + + return usageStats.models + .filter((row) => row.totalTokens > 0) + .map((row) => ({ + client: translate("Agent"), + model: row.model || row.label, + provider: row.provider || translate("Provider"), + value: row.totalTokens + })) + .sort((a, b) => b.value - a.value); +} + +function modelNameFromLabel(label: string): string { + const parts = label.split("/"); + return parts[parts.length - 1]?.trim() || label; +} + +function fuelMeter(account: ProviderAccountSnapshot): ProviderAccountMeter | undefined { + return [...account.meters] + .filter((meter) => providerAccountMeterRemainingRatio(meter) !== undefined) + .sort((a, b) => (providerAccountMeterRemainingRatio(a) ?? 1) - (providerAccountMeterRemainingRatio(b) ?? 1))[0] ?? primaryProviderAccountMeter(account); +} + +function safeProviderName(value: string, translate: (value: string) => string): string { + return value.trim() || translate("Provider"); +} + +function uniqueCount(values: string[]): number { + return new Set(values.filter(Boolean)).size; +} + +function rangeLabel(range: UsageStatsRange, translate: (value: string) => string): string { + if (range === "today") { + return translate("Today"); + } + return translate(range); +} + +function calendarColor(intensity: TokenActivityCell["intensity"], inRange: boolean): string { + if (!inRange) return "rgba(225,29,72,.08)"; + if (intensity === 0) return "rgba(225,29,72,.13)"; + if (intensity === 1) return "rgba(225,29,72,.30)"; + if (intensity === 2) return "rgba(225,29,72,.50)"; + if (intensity === 3) return "rgba(225,29,72,.72)"; + return "rgba(225,29,72,.94)"; +} + +async function saveElementAsPng(element: HTMLElement, fileName: string, options: ShareCardPngExportOptions = {}): Promise { + if (window.ccr?.captureElementPng) { + await nativeCaptureElementAsPng(element, fileName, options); + return; + } + await browserExportElementAsPng(element, fileName); +} + +async function nativeCaptureElementAsPng(element: HTMLElement, fileName: string, options: ShareCardPngExportOptions): Promise { + element.scrollIntoView({ block: "nearest", inline: "nearest" }); + await nextFrame(); + const rect = element.getBoundingClientRect(); + const result = await window.ccr?.captureElementPng?.({ + borderRadius: exportBorderRadius(element), + exportId: options.exportId, + fileName, + output: options.output, + rect: { + height: rect.height, + width: rect.width, + x: rect.x, + y: rect.y + } + }); + if (!result || result.canceled) { + return; + } +} + +function exportBorderRadius(element: HTMLElement): number | undefined { + const target = element.firstElementChild instanceof HTMLElement ? element.firstElementChild : element; + const styles = window.getComputedStyle(target); + const radii = [ + styles.borderTopLeftRadius, + styles.borderTopRightRadius, + styles.borderBottomRightRadius, + styles.borderBottomLeftRadius + ].map((value) => Number.parseFloat(value)).filter(Number.isFinite); + const radius = Math.max(0, ...radii); + return radius > 0 ? radius : undefined; +} + +function nextFrame(): Promise { + return new Promise((resolve) => { + window.requestAnimationFrame(() => resolve()); + }); +} + +async function browserExportElementAsPng(element: HTMLElement, fileName: string): Promise { + const rect = element.getBoundingClientRect(); + const width = Math.ceil(rect.width); + const height = Math.ceil(rect.height); + if (width <= 0 || height <= 0) { + throw new Error("Cannot export an empty element."); + } + + const clone = element.cloneNode(true) as HTMLElement; + clone.setAttribute("xmlns", "http://www.w3.org/1999/xhtml"); + clone.style.width = `${width}px`; + clone.style.height = `${height}px`; + inlineComputedStyles(element, clone); + + const serialized = new XMLSerializer().serializeToString(clone); + const svg = [ + ``, + `${serialized}`, + "" + ].join(""); + const svgUrl = URL.createObjectURL(new Blob([svg], { type: "image/svg+xml;charset=utf-8" })); + + try { + const image = await loadImage(svgUrl); + const scale = 3; + const canvas = document.createElement("canvas"); + canvas.width = width * scale; + canvas.height = height * scale; + const context = canvas.getContext("2d"); + if (!context) { + throw new Error("Canvas rendering is unavailable."); + } + context.scale(scale, scale); + context.drawImage(image, 0, 0, width, height); + const pngBlob = await canvasToBlob(canvas); + const pngUrl = URL.createObjectURL(pngBlob); + try { + const link = document.createElement("a"); + link.href = pngUrl; + link.download = fileName; + document.body.appendChild(link); + link.click(); + link.remove(); + } finally { + URL.revokeObjectURL(pngUrl); + } + } finally { + URL.revokeObjectURL(svgUrl); + } +} + +function inlineComputedStyles(source: Element, target: Element): void { + if (target instanceof HTMLElement || target instanceof SVGElement) { + const computed = window.getComputedStyle(source); + for (const property of Array.from(computed)) { + target.style.setProperty(property, computed.getPropertyValue(property), computed.getPropertyPriority(property)); + } + } + + const sourceChildren = Array.from(source.children); + const targetChildren = Array.from(target.children); + sourceChildren.forEach((child, index) => { + const targetChild = targetChildren[index]; + if (targetChild) { + inlineComputedStyles(child, targetChild); + } + }); +} + +function loadImage(url: string): Promise { + return new Promise((resolve, reject) => { + const image = new Image(); + image.onload = () => resolve(image); + image.onerror = () => reject(new Error("Failed to render export image.")); + image.src = url; + }); +} + +function canvasToBlob(canvas: HTMLCanvasElement): Promise { + return new Promise((resolve, reject) => { + canvas.toBlob((blob) => { + if (blob) { + resolve(blob); + } else { + reject(new Error("Failed to encode PNG image.")); + } + }, "image/png"); + }); +} diff --git a/src/renderer/pages/home/shared/common.ts b/src/renderer/pages/home/shared/common.ts index f287bba..246a6fa 100644 --- a/src/renderer/pages/home/shared/common.ts +++ b/src/renderer/pages/home/shared/common.ts @@ -674,7 +674,7 @@ export function normalizeOverviewWidget(value: unknown): OverviewWidgetConfig | } export function normalizeOverviewWidgetType(value: unknown): OverviewWidgetType | undefined { - return typeof value === "string" && ["account-balance", "client-analysis", "metric", "model-distribution", "provider-analysis", "system-status", "token-activity", "token-mix", "usage-trend"].includes(value) + return typeof value === "string" && ["account-balance", "client-analysis", "metric", "model-distribution", "provider-analysis", "share-fuel-cockpit", "share-model-leaderboard", "share-route-map", "share-spend-receipt", "share-token-calendar", "share-usage-wrapped", "system-status", "token-activity", "token-mix", "usage-trend"].includes(value) ? value as OverviewWidgetType : undefined; } @@ -754,6 +754,11 @@ export function overviewWidgetVariantOptions(type: OverviewWidgetType): Array<{ { label: "Compact", value: "compact" } ]; } + if (isShareOverviewWidgetType(type)) { + return [ + { label: "Card", value: "card" } + ]; + } return [ { label: "Table", value: "table" }, { label: "Compact", value: "compact" } @@ -775,6 +780,7 @@ export function defaultOverviewWidgetSize(type: OverviewWidgetType): OverviewWid if (type === "client-analysis" || type === "provider-analysis") return "2:2"; if (type === "usage-trend") return "3:2"; if (type === "system-status") return "4:1"; + if (isShareOverviewWidgetType(type)) return "1:4"; return "4:2"; } @@ -786,6 +792,7 @@ export function defaultOverviewWidgetVariant(type: OverviewWidgetType): Overview if (type === "token-activity") return "heatmap"; if (type === "usage-trend") return "composed"; if (type === "system-status") return "timeline"; + if (isShareOverviewWidgetType(type)) return "card"; return "table"; } @@ -795,12 +802,24 @@ export function constrainOverviewWidgetSize( variant: OverviewWidgetVariant, accountProvider?: string ): OverviewWidgetSize { + if (isShareOverviewWidgetType(type)) { + return overviewWidgetSizeAtLeast(size, 1, 4); + } if (type !== "account-balance" || variant !== "compact" || accountProvider?.trim()) { return size; } return overviewWidgetSizeAtLeast(size, 2, 2); } +function isShareOverviewWidgetType(type: OverviewWidgetType): boolean { + return type === "share-fuel-cockpit" || + type === "share-model-leaderboard" || + type === "share-route-map" || + type === "share-spend-receipt" || + type === "share-token-calendar" || + type === "share-usage-wrapped"; +} + function overviewWidgetSizeAtLeast(size: OverviewWidgetSize, minWidth: 1 | 2 | 3 | 4, minHeight: 1 | 2 | 3 | 4): OverviewWidgetSize { const [widthText, heightText] = size.split(":"); const width = overviewWidgetDimensionAtLeast(widthText, minWidth); diff --git a/src/renderer/pages/home/shared/i18n.tsx b/src/renderer/pages/home/shared/i18n.tsx index 7de0745..a0ba92e 100644 --- a/src/renderer/pages/home/shared/i18n.tsx +++ b/src/renderer/pages/home/shared/i18n.tsx @@ -279,6 +279,7 @@ export const appCopy: Record = { "Bar": "Bar", "Bars": "Bars", "Breakdown component": "Breakdown component", + "Card": "Card", "Cards": "Cards", "Change widget type": "Change widget type", "Client Analysis": "Client Analysis", @@ -340,6 +341,27 @@ export const appCopy: Record = { "Widget": "Widget", "Widget size": "Widget size", "Wide": "Wide", + "Share cards": "Share cards", + "Share card": "Share card", + "Save social-ready cards as PNG images": "Save social-ready cards as PNG images", + "Social media PNG cards": "Social media PNG cards", + "PNG export": "PNG export", + "Save image": "Save image", + "Saving": "Saving", + "Export failed": "Export failed", + "AI Usage Wrapped": "AI Usage Wrapped", + "CCR Route Map": "CCR Route Map", + "Model Leaderboard": "Model Leaderboard", + "AI Fuel Cockpit": "AI Fuel Cockpit", + "Token Calendar Poster": "Token Calendar Poster", + "Spend Receipt": "Spend Receipt", + "tokens routed through CCR": "tokens routed through CCR", + "Top model": "Top model", + "Top provider": "Top provider", + "No provider activity": "No provider activity", + "Peak day": "Peak day", + "Generated by CCR": "Generated by CCR", + "Range": "Range", "Virtual model": "Fusion", "Virtual Models": "Fusion", "Add Virtual Model": "Add Fusion", @@ -967,6 +989,7 @@ export const appCopy: Record = { "Bar": "柱状图", "Bars": "横条", "Breakdown component": "构成组件", + "Card": "卡片", "Cards": "卡片", "Change widget type": "切换组件类型", "Client or provider": "客户端或供应商", @@ -1034,6 +1057,27 @@ export const appCopy: Record = { "Widget": "组件", "Widget size": "组件大小", "Wide": "宽", + "Share cards": "分享卡片", + "Share card": "分享卡片", + "Save social-ready cards as PNG images": "将适合社交媒体发布的卡片保存为 PNG 图片", + "Social media PNG cards": "适合社交媒体发布的 PNG 卡片", + "PNG export": "PNG 导出", + "Save image": "保存图片", + "Saving": "正在保存", + "Export failed": "导出失败", + "AI Usage Wrapped": "AI 用量年报", + "CCR Route Map": "CCR 路由图", + "Model Leaderboard": "模型排行榜", + "AI Fuel Cockpit": "AI 燃料仪表", + "Token Calendar Poster": "Token 日历海报", + "Spend Receipt": "消费小票", + "tokens routed through CCR": "通过 CCR 路由的令牌", + "Top model": "最高频模型", + "Top provider": "最高频供应商", + "No provider activity": "暂无供应商活动", + "Peak day": "峰值日期", + "Generated by CCR": "由 CCR 生成", + "Range": "范围", "W": "三", "Yes": "是", "Set as default provider": "设为默认供应商", @@ -1123,6 +1167,7 @@ export const appCopy: Record = { "Base model mode": "基础模型模式", "Client tools": "客户端工具", "Client-visible": "客户端可见", + "Clients": "客户端", "Command": "命令", "Command is required.": "命令不能为空。", "Decorate only": "仅装饰", diff --git a/src/renderer/types/electron.d.ts b/src/renderer/types/electron.d.ts index 3ac4e52..b9be193 100644 --- a/src/renderer/types/electron.d.ts +++ b/src/renderer/types/electron.d.ts @@ -6,8 +6,12 @@ import type { AgentAnalysisTracePayloadFullResult, AgentAnalysisTracePayloadRequest, AppConfig, + AppCaptureElementPngRequest, + AppCaptureElementPngResult, AppDataExportResult, AppInfo, + AppImageExportTargetRequest, + AppImageExportTargetResult, AppSaveConfigOptions, AppUpdateStatus, ApiKeyConfig, @@ -73,6 +77,7 @@ declare global { applyClaudeAppGateway: (config?: AppConfig) => Promise; applyProfile: () => Promise; cancelBotGatewayQrLogin: (request: BotGatewayQrLoginCancelRequest) => Promise; + captureElementPng?: (request: AppCaptureElementPngRequest) => Promise; checkProviderConnectivity: (request: GatewayProviderConnectivityCheckRequest) => Promise; closeBotGatewayQrWindow: (request: BotGatewayQrWindowCloseRequest) => Promise; clearProxyNetworkCaptures: () => Promise; @@ -108,6 +113,7 @@ declare global { openBotGatewayQrWindow: (request: BotGatewayQrWindowOpenRequest) => Promise; openExternal: (url: string) => Promise; openProfile: (request: ProfileOpenRequest) => Promise; + prepareImageExportTarget?: (request: AppImageExportTargetRequest) => Promise; probeProviderCandidates: (request: GatewayProviderProbeCandidatesRequest) => Promise; probeProvider: (request: GatewayProviderProbeRequest) => Promise; quitApp: () => Promise; diff --git a/src/shared/app.ts b/src/shared/app.ts index 2e83828..94ac0b5 100644 --- a/src/shared/app.ts +++ b/src/shared/app.ts @@ -18,6 +18,37 @@ export type AppDataExportResult = { file?: string; }; +export type AppCaptureElementPngRequest = { + borderRadius?: number; + exportId?: string; + fileName: string; + output?: { + height: number; + width: number; + }; + rect: { + height: number; + width: number; + x: number; + y: number; + }; +}; + +export type AppCaptureElementPngResult = { + canceled: boolean; + file?: string; +}; + +export type AppImageExportTargetRequest = { + fileName: string; +}; + +export type AppImageExportTargetResult = { + canceled: boolean; + exportId?: string; + file?: string; +}; + export type AppUpdateState = | "idle" | "checking" @@ -851,6 +882,12 @@ export type OverviewWidgetType = | "metric" | "model-distribution" | "provider-analysis" + | "share-fuel-cockpit" + | "share-model-leaderboard" + | "share-route-map" + | "share-spend-receipt" + | "share-token-calendar" + | "share-usage-wrapped" | "system-status" | "token-activity" | "token-mix" diff --git a/src/shared/ipc-channels.ts b/src/shared/ipc-channels.ts index 1bfe296..872fdaa 100644 --- a/src/shared/ipc-channels.ts +++ b/src/shared/ipc-channels.ts @@ -1,5 +1,6 @@ export const IPC_CHANNELS = { appBeforeQuit: "ccr:app:before-quit", + appCaptureElementPng: "ccr:app:capture-element-png", appCloseTray: "ccr:app:close-tray", appDetectProviderIcon: "ccr:app:detect-provider-icon", appExportData: "ccr:app:export-data", @@ -46,6 +47,7 @@ export const IPC_CHANNELS = { appProbeProviderCandidates: "ccr:app:probe-provider-candidates", appProviderDeepLink: "ccr:app:provider-deep-link", appGetPluginMarketplace: "ccr:app:get-plugin-marketplace", + appPrepareImageExportTarget: "ccr:app:prepare-image-export-target", appQuit: "ccr:app:quit", appRevealProxyCertificate: "ccr:app:reveal-proxy-certificate", appRestartProxy: "ccr:app:restart-proxy", diff --git a/tests/renderer/overview-components.test.tsx b/tests/renderer/overview-components.test.tsx index b8e5b84..4d8ee12 100644 --- a/tests/renderer/overview-components.test.tsx +++ b/tests/renderer/overview-components.test.tsx @@ -20,7 +20,13 @@ test("OverviewView renders every overview widget type", () => { { enabled: true, id: "token-mix", size: "2:2", type: "token-mix", variant: "stacked" }, { enabled: true, id: "models", size: "2:2", type: "model-distribution", variant: "donut" }, { enabled: true, id: "clients", size: "4:2", type: "client-analysis", variant: "table" }, - { enabled: true, id: "providers", size: "4:2", type: "provider-analysis", variant: "table" } + { enabled: true, id: "providers", size: "4:2", type: "provider-analysis", variant: "table" }, + { enabled: true, id: "share-usage", size: "1:4", type: "share-usage-wrapped", variant: "card" }, + { enabled: true, id: "share-routes", size: "1:4", type: "share-route-map", variant: "card" }, + { enabled: true, id: "share-models", size: "1:4", type: "share-model-leaderboard", variant: "card" }, + { enabled: true, id: "share-fuel", size: "1:4", type: "share-fuel-cockpit", variant: "card" }, + { enabled: true, id: "share-calendar", size: "1:4", type: "share-token-calendar", variant: "card" }, + { enabled: true, id: "share-receipt", size: "1:4", type: "share-spend-receipt", variant: "card" } ]; const html = renderToStaticMarkup( @@ -51,6 +57,13 @@ test("OverviewView renders every overview widget type", () => { assert.match(html, /Provider Analysis/); assert.match(html, /claude-code/); assert.match(html, /openai/); + assert.match(html, /Save image/); + assert.match(html, /AI Usage Wrapped/); + assert.match(html, /CCR Route Map/); + assert.match(html, /Model Leaderboard/); + assert.match(html, /AI Fuel Cockpit/); + assert.match(html, /Token Calendar Poster/); + assert.match(html, /Spend Receipt/); }); test("OverviewView renders the empty widget layout state", () => { From 98375b7f4e939b3a55f92273efe9e74ddab74c8e Mon Sep 17 00:00:00 2001 From: musistudio Date: Tue, 30 Jun 2026 17:45:19 +0800 Subject: [PATCH 18/94] Add Claude App profile opening and HTML PNG export --- src/main/cli.ts | 58 ++++++-- src/main/ipc.ts | 132 ++++++++++++++++-- src/main/preload.ts | 3 + src/main/profile-launch-core.ts | 12 +- .../pages/home/components/share-cards.tsx | 98 ++++++++++--- src/renderer/pages/home/shared/profiles.ts | 2 +- src/renderer/types/electron.d.ts | 3 + src/shared/app.ts | 20 +++ src/shared/ipc-channels.ts | 1 + tests/main/profile-launch-core.test.mjs | 17 ++- 10 files changed, 302 insertions(+), 44 deletions(-) diff --git a/src/main/cli.ts b/src/main/cli.ts index 0053a34..8964d4f 100644 --- a/src/main/cli.ts +++ b/src/main/cli.ts @@ -2,14 +2,16 @@ import { spawn, spawnSync } from "node:child_process"; import { accessSync, constants as fsConstants, existsSync, mkdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from "node:fs"; import path from "node:path"; -import { assertAvailableGatewayModels, type ProfileOpenSurface } from "../shared/app"; +import { assertAvailableGatewayModels, type ProfileConfig, type ProfileOpenSurface } from "../shared/app"; import { botGatewayProfileEnv } from "./bot-gateway-env"; +import { applyClaudeAppGatewayConfig } from "./claude-app-gateway-service"; +import { launchClaudeAppProfile, resolveClaudeAppProfileUserDataDir } from "./claude-app-launch"; import { launchCodexAppProfile, launchZcodeAppProfile } from "./codex-app-launch"; import { loadAppConfig } from "./config"; import { CONFIGDIR } from "./constants"; import { applyProfileConfig, applyProfileRuntimeConfig } from "./profile-service"; import { ensureProfileGateway } from "./profile-launch-service"; -import { buildProfileLaunchPlan, findProfileForOpen, profileLaunchSpawnCommand, resolveProfileOpenSurface } from "./profile-launch-core"; +import { buildProfileLaunchPlan, defaultProfileOpenSurface, findProfileForOpen, profileLaunchSpawnCommand, resolveProfileOpenSurface } from "./profile-launch-core"; import { startWebManagementServer } from "./web-management-server"; type ProfileCliOptions = { @@ -93,30 +95,50 @@ async function main(): Promise { assertAvailableGatewayModels(config); await applyProfileConfig(config); const profile = findProfileForOpen(config, profileOptions.profileRef); - const surface = profileOptions.surface ?? (profile.agent === "zcode" || profile.surface === "app" ? "app" : "cli"); + const surface = profileOptions.surface ?? defaultProfileOpenSurface(profile); const resolvedSurface = resolveProfileOpenSurface(profile, surface); - const launchConfig = resolvedSurface === "cli" - ? await ensureProfileGateway(config, profile, profile.name || profile.id || "profile", { reuseExisting: true, startIfMissing: false }) - : config; + if (profile.agent === "zcode" && profileOptions.agentArgs.length > 0) { + throw new Error("ZCode profiles can only open the app; agent arguments are not supported."); + } + if (profile.agent === "claude-code" && resolvedSurface === "app" && profileOptions.agentArgs.length > 0) { + throw new Error("Claude App profiles do not support agent arguments."); + } + + const launchConfig = await ensureProfileGateway(config, profile, resolvedSurface === "app" ? profileAppName(profile) : profile.name || profile.id || "profile", { + reuseExisting: true, + startIfMissing: false + }); if (resolvedSurface === "cli") { const runtimeResult = applyProfileRuntimeConfig(launchConfig, profile, launchConfig.APIKEY); if (!runtimeResult.ok) { throw new Error(runtimeResult.message); } } - if (profile.agent === "zcode" && profileOptions.agentArgs.length > 0) { - throw new Error("ZCode profiles can only open the app; agent arguments are not supported."); + if (profile.agent === "claude-code" && resolvedSurface === "app") { + applyClaudeAppGatewayConfig(launchConfig); + applyClaudeAppGatewayConfig(launchConfig, { + backup: false, + dataDir: resolveClaudeAppProfileUserDataDir(configDir, profile), + refreshModelDiscoveryCache: true + }); + const launch = await launchClaudeAppProfile(configDir, profile, launchConfig); + const spawnError = await waitForImmediateSpawnError(launch.child, 500); + if (spawnError) { + throw new Error(`Failed to open Claude App: ${spawnError}`); + } + process.stdout.write(`Opened Claude App with ${profile.name || profile.id}.\n`); + return; } if ((profile.agent === "codex" || profile.agent === "zcode") && resolvedSurface === "app" && profileOptions.agentArgs.length === 0) { if (profile.agent === "zcode") { - const launch = launchZcodeAppProfile(configDir, profile, config); + const launch = launchZcodeAppProfile(configDir, profile, launchConfig); const spawnError = await waitForImmediateSpawnError(launch.child, 500); if (spawnError) { throw new Error(`Failed to open ZCode App: ${spawnError}`); } process.stdout.write(`Opened ZCode App with ${profile.name || profile.id}.\n`); } else { - const launch = launchCodexAppProfile(configDir, profile, config); + const launch = launchCodexAppProfile(configDir, profile, launchConfig); const spawnError = await waitForImmediateSpawnError(launch.child, 500); if (spawnError) { throw new Error(`Failed to open Codex App: ${spawnError}`); @@ -197,6 +219,16 @@ function parseArgs(args: string[]): CliOptions { return options; } +function profileAppName(profile: Pick): string { + if (profile.agent === "claude-code") { + return "Claude App"; + } + if (profile.agent === "zcode") { + return "ZCode App"; + } + return "Codex App"; +} + function parseStopArgs(args: string[]): StopCliOptions { const options: StopCliOptions = { command: "stop", @@ -376,13 +408,13 @@ function printHelp(exitCode: number): void { "Usage:", " ccr start [--host ] [--port ] [--open] [--no-gateway]", " ccr stop", - " ccr [-- ]", + " ccr [cli|app] [-- ]", "", "Examples:", " ccr start", " ccr stop", - " ccr Codex cli", - " ccr default-codex cli -- --model gpt-5-codex", + " ccr Codex", + " ccr default-codex -- --model gpt-5-codex", " ccr default-codex app" ].join("\n"); const stream = exitCode === 0 ? process.stdout : process.stderr; diff --git a/src/main/ipc.ts b/src/main/ipc.ts index 4d55e3f..bdf8cd2 100644 --- a/src/main/ipc.ts +++ b/src/main/ipc.ts @@ -1,6 +1,6 @@ -import { app, BrowserWindow, dialog, ipcMain, shell, type OpenDialogOptions, type Rectangle, type SaveDialogOptions } from "electron"; +import { app, BrowserWindow, dialog, ipcMain, nativeImage, shell, type OpenDialogOptions, type Rectangle, type SaveDialogOptions } from "electron"; import { randomUUID } from "node:crypto"; -import { existsSync, readFileSync, statSync, writeFileSync } from "node:fs"; +import { existsSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs"; import net from "node:net"; import path from "node:path"; import { deflateSync, inflateSync } from "node:zlib"; @@ -31,7 +31,7 @@ import trayController from "./tray-controller"; import { appUpdateService } from "./update-service"; import { getUsageStats } from "./usage-store"; import windowsManager from "./windows"; -import type { AgentAnalysisFilter, AgentAnalysisTracePayloadRequest, ApiKeyConfig, AppCaptureElementPngRequest, AppCaptureElementPngResult, AppConfig, AppDataExportResult, AppImageExportTargetRequest, AppImageExportTargetResult, AppInfo, AppSaveConfigOptions, BotGatewayQrLoginCancelRequest, BotGatewayQrLoginStartRequest, BotGatewayQrLoginWaitRequest, BotGatewayQrWindowCloseRequest, BotGatewayQrWindowOpenRequest, GatewayPluginAppConfig, GatewayProviderConnectivityCheckRequest, GatewayProviderProbeCandidatesRequest, GatewayProviderProbeRequest, GatewayStatus, LocalAgentProviderImportRequest, PluginDependency, PluginDirectorySelection, PluginMarketplaceEntry, ProfileApplyResult, ProfileOpenRequest, ProviderAccountSnapshotRequestOptions, ProviderAccountTestRequest, ProviderCatalogModelsRequest, ProviderIconDetectionRequest, ProviderManifestFetchRequest, RequestLogListFilter, UsageStatsFilter, UsageStatsRange } from "../shared/app"; +import type { AgentAnalysisFilter, AgentAnalysisTracePayloadRequest, ApiKeyConfig, AppCaptureElementPngRequest, AppCaptureElementPngResult, AppConfig, AppDataExportResult, AppImageExportTargetRequest, AppImageExportTargetResult, AppInfo, AppRenderHtmlPngRequest, AppRenderHtmlPngResult, AppSaveConfigOptions, BotGatewayQrLoginCancelRequest, BotGatewayQrLoginStartRequest, BotGatewayQrLoginWaitRequest, BotGatewayQrWindowCloseRequest, BotGatewayQrWindowOpenRequest, GatewayPluginAppConfig, GatewayProviderConnectivityCheckRequest, GatewayProviderProbeCandidatesRequest, GatewayProviderProbeRequest, GatewayStatus, LocalAgentProviderImportRequest, PluginDependency, PluginDirectorySelection, PluginMarketplaceEntry, ProfileApplyResult, ProfileOpenRequest, ProviderAccountSnapshotRequestOptions, ProviderAccountTestRequest, ProviderCatalogModelsRequest, ProviderIconDetectionRequest, ProviderManifestFetchRequest, RequestLogListFilter, UsageStatsFilter, UsageStatsRange } from "../shared/app"; const pluginMarketplace: PluginMarketplaceEntry[] = [ { @@ -79,6 +79,9 @@ ipcMain.handle(IPC_CHANNELS.appCaptureElementPng, async (event, request: AppCapt ipcMain.handle(IPC_CHANNELS.appPrepareImageExportTarget, async (event, request: AppImageExportTargetRequest): Promise => { return prepareImageExportTarget(BrowserWindow.fromWebContents(event.sender), request); }); +ipcMain.handle(IPC_CHANNELS.appRenderHtmlPng, async (event, request: AppRenderHtmlPngRequest): Promise => { + return renderHtmlPng(BrowserWindow.fromWebContents(event.sender), request); +}); ipcMain.handle(IPC_CHANNELS.appGetConfig, () => loadAppConfig()); ipcMain.handle(IPC_CHANNELS.appGetOnboardingFinished, async () => { @@ -554,10 +557,7 @@ async function captureElementPng(window: BrowserWindow | null, request: AppCaptu } const rect = sanitizeCaptureRect(request.rect); - const targetFile = request.exportId ? consumeImageExportTarget(request.exportId) : undefined; - const result = targetFile - ? { canceled: false, filePath: targetFile } - : await dialog.showSaveDialog(window, shareCardSaveDialogOptions(request.fileName)); + const result = await imageExportFile(window, request.fileName, request.exportId); if (result.canceled || !result.filePath) { return { canceled: true }; } @@ -568,6 +568,62 @@ async function captureElementPng(window: BrowserWindow | null, request: AppCaptu return { canceled: false, file: result.filePath }; } +async function renderHtmlPng(window: BrowserWindow | null, request: AppRenderHtmlPngRequest): Promise { + const html = typeof request.html === "string" ? request.html : ""; + if (!html.trim()) { + throw new Error("Export HTML is empty."); + } + + const size = sanitizeRenderSize(request.size); + const result = await imageExportFile(window, request.fileName, request.exportId); + if (result.canceled || !result.filePath) { + return { canceled: true }; + } + + const renderWindow = new BrowserWindow({ + backgroundColor: "#00000000", + frame: false, + height: size.height, + paintWhenInitiallyHidden: true, + resizable: false, + show: false, + skipTaskbar: true, + transparent: true, + useContentSize: true, + webPreferences: { + backgroundThrottling: false, + contextIsolation: true, + nodeIntegration: false, + offscreen: true, + sandbox: true + }, + width: size.width + }); + + const tempDir = mkdtempSync(path.join(app.getPath("temp"), "ccr-export-")); + const tempHtmlFile = path.join(tempDir, "export.html"); + writeFileSync(tempHtmlFile, html, { encoding: "utf8", mode: 0o600 }); + + try { + await renderWindow.loadFile(tempHtmlFile); + await waitForExportWindowPaint(renderWindow); + const image = await renderWindow.webContents.capturePage({ + height: size.height, + width: size.width, + x: 0, + y: 0 + }); + const png = image.toPNG(); + writeFileSync(result.filePath, pngWithExportProcessing(png, request, size.width), { mode: 0o600 }); + return { canceled: false, file: result.filePath }; + } finally { + if (!renderWindow.isDestroyed()) { + renderWindow.destroy(); + } + rmSync(tempDir, { force: true, recursive: true }); + } +} + async function prepareImageExportTarget(window: BrowserWindow | null, request: AppImageExportTargetRequest): Promise { const result = window ? await dialog.showSaveDialog(window, shareCardSaveDialogOptions(request.fileName)) @@ -585,6 +641,15 @@ async function prepareImageExportTarget(window: BrowserWindow | null, request: A }; } +async function imageExportFile(window: BrowserWindow | null, fileName: string, exportId?: string): Promise<{ canceled: boolean; filePath?: string }> { + const targetFile = exportId ? consumeImageExportTarget(exportId) : undefined; + return targetFile + ? { canceled: false, filePath: targetFile } + : window + ? await dialog.showSaveDialog(window, shareCardSaveDialogOptions(fileName)) + : await dialog.showSaveDialog(shareCardSaveDialogOptions(fileName)); +} + function dataExportSaveDialogOptions(exportedAt: string): SaveDialogOptions { return { buttonLabel: "Export", @@ -623,6 +688,29 @@ function sanitizeCaptureRect(rect: AppCaptureElementPngRequest["rect"]): Rectang }; } +function sanitizeRenderSize(size: AppRenderHtmlPngRequest["size"]): { height: number; width: number } { + const width = finiteNumber(size?.width, "render width"); + const height = finiteNumber(size?.height, "render height"); + if (width <= 0 || height <= 0 || width > 4096 || height > 4096) { + throw new Error("Render size is out of range."); + } + return { + height: Math.ceil(height), + width: Math.ceil(width) + }; +} + +async function waitForExportWindowPaint(window: BrowserWindow): Promise { + await window.webContents.executeJavaScript(` + new Promise((resolve) => { + const fontsReady = document.fonts && document.fonts.ready ? document.fonts.ready.catch(() => undefined) : Promise.resolve(); + fontsReady.then(() => { + requestAnimationFrame(() => requestAnimationFrame(() => resolve(true))); + }); + }); + `); +} + function finiteNumber(value: unknown, label: string): number { if (typeof value !== "number" || !Number.isFinite(value)) { throw new Error(`Invalid ${label}.`); @@ -647,6 +735,14 @@ function consumeImageExportTarget(exportId: string): string { const pngSignature = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]); +type PngExportProcessingRequest = { + borderRadius?: number; + output?: { + height: number; + width: number; + }; +}; + type DecodedPngPixels = { bitDepth: number; colorType: 2 | 6; @@ -655,7 +751,7 @@ type DecodedPngPixels = { width: number; }; -function pngWithExportProcessing(png: Buffer, request: AppCaptureElementPngRequest, cssWidth: number): Buffer { +function pngWithExportProcessing(png: Buffer, request: PngExportProcessingRequest, cssWidth: number): Buffer { const radius = typeof request.borderRadius === "number" && Number.isFinite(request.borderRadius) ? Math.max(0, request.borderRadius) : 0; const outputWidth = sanitizePngOutputDimension(request.output?.width); const outputHeight = sanitizePngOutputDimension(request.output?.height); @@ -680,10 +776,30 @@ function pngWithExportProcessing(png: Buffer, request: AppCaptureElementPngReque return encodeRgbaPng(width, height, rgba); } catch (error) { console.warn(`[export] Failed to process exported PNG: ${formatError(error)}`); + const resized = resizePngWithNativeImage(png, outputWidth, outputHeight); + if (resized) { + return resized; + } return png; } } +function resizePngWithNativeImage(png: Buffer, width?: number, height?: number): Buffer | undefined { + if (!width || !height) { + return undefined; + } + try { + const image = nativeImage.createFromBuffer(png); + if (image.isEmpty()) { + return undefined; + } + return image.resize({ height, width }).toPNG(); + } catch (error) { + console.warn(`[export] Failed to resize exported PNG fallback: ${formatError(error)}`); + return undefined; + } +} + function sanitizePngOutputDimension(value: unknown): number | undefined { if (typeof value !== "number" || !Number.isFinite(value)) { return undefined; diff --git a/src/main/preload.ts b/src/main/preload.ts index f5f40d9..812d823 100644 --- a/src/main/preload.ts +++ b/src/main/preload.ts @@ -13,6 +13,8 @@ import type { AppInfo, AppImageExportTargetRequest, AppImageExportTargetResult, + AppRenderHtmlPngRequest, + AppRenderHtmlPngResult, AppSaveConfigOptions, AppUpdateStatus, ApiKeyConfig, @@ -131,6 +133,7 @@ contextBridge.exposeInMainWorld("ccr", { probeProvider: (request: GatewayProviderProbeRequest) => invoke(IPC_CHANNELS.appProbeProvider, request) as Promise, quitApp: () => invoke(IPC_CHANNELS.appQuit) as Promise, revealProxyCertificate: () => invoke(IPC_CHANNELS.appRevealProxyCertificate) as Promise, + renderHtmlPng: (request: AppRenderHtmlPngRequest) => invoke(IPC_CHANNELS.appRenderHtmlPng, request) as Promise, restartGateway: () => invoke(IPC_CHANNELS.appRestartGateway) as Promise, restartProxy: () => invoke(IPC_CHANNELS.appRestartProxy) as Promise, saveApiKeys: (apiKeys: ApiKeyConfig[]) => invoke(IPC_CHANNELS.appSaveApiKeys, apiKeys) as Promise, diff --git a/src/main/profile-launch-core.ts b/src/main/profile-launch-core.ts index 5f50af2..e737784 100644 --- a/src/main/profile-launch-core.ts +++ b/src/main/profile-launch-core.ts @@ -69,14 +69,22 @@ export function resolveProfileOpenSurface(profile: ProfileConfig, surface?: Prof return surfaces[0]; } +export function defaultProfileOpenSurface(profile: Pick): ProfileOpenSurface { + return profile.agent === "zcode" ? "app" : "cli"; +} + export function profileOpenCommand( profile: ProfileConfig, - surface: ProfileOpenSurface = profile.agent === "zcode" ? "app" : "cli", + surface: ProfileOpenSurface = defaultProfileOpenSurface(profile), command = "ccr", profileRef = profile.name?.trim() || profile.id ): string { const quote = process.platform === "win32" ? windowsCommandQuote : shellQuote; - return [quote(command), quote(profileRef), surface].join(" "); + const parts = [quote(command), quote(profileRef)]; + if (surface === "app") { + parts.push(surface); + } + return parts.join(" "); } export function buildProfileLaunchPlan( diff --git a/src/renderer/pages/home/components/share-cards.tsx b/src/renderer/pages/home/components/share-cards.tsx index 3a68aba..b356f20 100644 --- a/src/renderer/pages/home/components/share-cards.tsx +++ b/src/renderer/pages/home/components/share-cards.tsx @@ -29,9 +29,9 @@ type ShareCardPreparedExportTarget = { }; const shareCardExportCssWidth = 540; -const shareCardExportCssHeight = 960; +const shareCardExportCssHeight = 675; const shareCardExportPixelWidth = 1080; -const shareCardExportPixelHeight = 1920; +const shareCardExportPixelHeight = 1350; const shareCardTones: Record = { amber: { @@ -159,7 +159,7 @@ function ShareCardShell({ setExporting(true); await nextFrame(); await nextFrame(); - const target = exportCardRef.current ?? cardRef.current; + const target = exportCardRef.current; if (!target) { throw new Error("Export card is unavailable."); } @@ -185,9 +185,16 @@ function ShareCardShell({
{title}
{status === "error" ? {t("Export failed")} : null} -
@@ -197,10 +204,11 @@ function ShareCardShell({ {exporting ? (
@@ -350,11 +343,6 @@ export function ConfigureClaudeDesignDialog({ onChangeRule(index, { pattern: event.target.value })} /> ) : null} - {rule.type === "long-context" ? ( - - onChangeRule(index, { threshold: event.target.value })} /> - - ) : null} {isClaudeDesignStaticRuleType(rule.type) ? (
{t(claudeDesignRouteRuleTypeLabel(rule.type))}
) : null} diff --git a/src/renderer/pages/home/components/network-logs.tsx b/src/renderer/pages/home/components/network-logs.tsx index 48117dc..c92aa02 100644 --- a/src/renderer/pages/home/components/network-logs.tsx +++ b/src/renderer/pages/home/components/network-logs.tsx @@ -1,4 +1,5 @@ import { memo } from "react"; +import { Maximize2, X } from "lucide-react"; import { AnimatedIconSwap, Check, ChevronDown, ChevronLeft, ChevronRight, clampNumber, clientInitial, cn, Copy, copyTextToClipboard, @@ -21,6 +22,26 @@ const logJsonAutoExpandEntryLimit = 60; const logJsonContainerPreviewLimit = 80; const logJsonAutoExpandTextLimit = 160 * 1024; const logBodyViewCache = new Map>(); +type LogTableColumnId = "time" | "status" | "stream" | "model" | "credential" | "tokens" | "duration"; +type LogTableColumn = { + id: LogTableColumnId; + minWidth: number; +}; +type LogTableColumnWidths = Partial>; +type LogTableGridStyle = { + gridTemplateColumns: string; + minWidth: string; +}; + +const baseLogTableColumns: LogTableColumn[] = [ + { id: "time", minWidth: 150 }, + { id: "status", minWidth: 116 }, + { id: "stream", minWidth: 108 }, + { id: "model", minWidth: 180 }, + { id: "tokens", minWidth: 140 }, + { id: "duration", minWidth: 92 } +]; +const credentialLogTableColumn: LogTableColumn = { id: "credential", minWidth: 128 }; export function NetworkingView({ clearCaptures, @@ -287,14 +308,21 @@ export function LogsView({ const [detailById, setDetailById] = useState>({}); const [detailErrorById, setDetailErrorById] = useState>({}); const [detailLoadingId, setDetailLoadingId] = useState(); + const [logColumnWidths, setLogColumnWidths] = useState({}); + const logTableHeaderRef = useRef(null); const firstItem = page.total === 0 ? 0 : (page.page - 1) * page.pageSize + 1; const lastItem = Math.min(page.total, page.page * page.pageSize); const hasAnyCredentialInfo = Boolean(filter.credential) || page.options.credentials.length > 0 || page.items.some(logHasCredentialInfo); + const visibleLogColumns = useMemo(() => getLogTableColumns(hasAnyCredentialInfo), [hasAnyCredentialInfo]); const logTableGridClass = hasAnyCredentialInfo ? "grid-cols-[minmax(0,0.8fr)_minmax(92px,0.38fr)_minmax(98px,0.4fr)_minmax(0,0.78fr)_minmax(120px,0.42fr)_minmax(0,0.68fr)_82px]" : "grid-cols-[minmax(0,0.8fr)_minmax(92px,0.38fr)_minmax(98px,0.4fr)_minmax(0,0.9fr)_minmax(0,0.74fr)_82px]"; + const logTableGridStyle = useMemo( + () => createLogTableGridStyle(visibleLogColumns, logColumnWidths), + [logColumnWidths, visibleLogColumns] + ); const loadLogDetail = useCallback((id: number) => { if (detailById[id] || detailLoadingId === id || !window.ccr?.getRequestLogDetail) { return; @@ -333,6 +361,55 @@ export function LogsView({ setExpandedId(undefined); }, [expandedId, page.items]); + function startLogColumnResize(columnIndex: number, event: ReactPointerEvent) { + const header = logTableHeaderRef.current; + const leftColumn = visibleLogColumns[columnIndex]; + const rightColumn = visibleLogColumns[columnIndex + 1]; + if (!header || !leftColumn || !rightColumn) { + return; + } + + event.preventDefault(); + event.stopPropagation(); + + const measuredWidths: LogTableColumnWidths = {}; + visibleLogColumns.forEach((column, index) => { + const width = header.children[index]?.getBoundingClientRect().width ?? column.minWidth; + measuredWidths[column.id] = Math.round(clampNumber(width, column.minWidth, Number.MAX_SAFE_INTEGER)); + }); + + const startX = event.clientX; + const startLeftWidth = measuredWidths[leftColumn.id] ?? leftColumn.minWidth; + const startRightWidth = measuredWidths[rightColumn.id] ?? rightColumn.minWidth; + const minDelta = leftColumn.minWidth - startLeftWidth; + const maxDelta = startRightWidth - rightColumn.minWidth; + const previousCursor = document.body.style.cursor; + const previousUserSelect = document.body.style.userSelect; + document.body.style.cursor = "col-resize"; + document.body.style.userSelect = "none"; + + const update = (pointerEvent: PointerEvent) => { + const delta = clampNumber(pointerEvent.clientX - startX, minDelta, maxDelta); + setLogColumnWidths((current) => ({ + ...current, + ...measuredWidths, + [leftColumn.id]: Math.round(startLeftWidth + delta), + [rightColumn.id]: Math.round(startRightWidth - delta) + })); + }; + const stop = () => { + document.body.style.cursor = previousCursor; + document.body.style.userSelect = previousUserSelect; + window.removeEventListener("pointermove", update); + window.removeEventListener("pointerup", stop); + window.removeEventListener("pointercancel", stop); + }; + + window.addEventListener("pointermove", update); + window.addEventListener("pointerup", stop); + window.addEventListener("pointercancel", stop); + } + return (
-
- - - - - {hasAnyCredentialInfo ? : null} - - +
+ {visibleLogColumns.map((column, index) => ( + startLogColumnResize(index, event) : undefined} + resizeLabel={t("Resize column width")} + /> + ))}
{page.items.length === 0 ? ( @@ -458,6 +540,7 @@ export function LogsView({ item={expandedId === item.id ? detailById[item.id] ?? item : item} key={item.id} logTableGridClass={logTableGridClass} + logTableGridStyle={logTableGridStyle} onToggle={toggleExpandedLog} /> ))} @@ -469,6 +552,51 @@ export function LogsView({ ); } +function getLogTableColumns(hasCredentialColumn: boolean): LogTableColumn[] { + if (!hasCredentialColumn) { + return baseLogTableColumns; + } + return [ + ...baseLogTableColumns.slice(0, 4), + credentialLogTableColumn, + ...baseLogTableColumns.slice(4) + ]; +} + +function createLogTableGridStyle(columns: LogTableColumn[], widths: LogTableColumnWidths): LogTableGridStyle | undefined { + const columnWidths = columns.map((column) => widths[column.id]); + if (columnWidths.some((width) => typeof width !== "number")) { + return undefined; + } + + return { + gridTemplateColumns: columns.map((column, index) => { + const width = Math.max(column.minWidth, Math.round(columnWidths[index] ?? column.minWidth)); + return `minmax(${column.minWidth}px, ${width}fr)`; + }).join(" "), + minWidth: `${columns.reduce((total, column) => total + column.minWidth, 0)}px` + }; +} + +function logTableColumnLabel(columnId: LogTableColumnId, t: (value: string) => string): string { + switch (columnId) { + case "time": + return t("时间"); + case "status": + return t("状态"); + case "stream": + return t("Stream"); + case "model": + return t("模型"); + case "credential": + return t("Credential"); + case "tokens": + return t("令牌"); + case "duration": + return t("持续时间"); + } +} + const LogRow = memo(function LogRow({ detailError, detailLoading, @@ -477,6 +605,7 @@ const LogRow = memo(function LogRow({ index, item, logTableGridClass, + logTableGridStyle, onToggle }: { detailError?: string; @@ -486,6 +615,7 @@ const LogRow = memo(function LogRow({ index: number; item: RequestLogEntry; logTableGridClass: string; + logTableGridStyle?: LogTableGridStyle; onToggle: (id: number) => void; }) { const t = useAppText(); @@ -504,6 +634,7 @@ const LogRow = memo(function LogRow({ expanded && "network-row-selected" )} onClick={() => onToggle(item.id)} + style={logTableGridStyle} type="button" >
@@ -707,6 +838,7 @@ function LogJsonPanel({ const t = useAppText(); const [selectedTab, setSelectedTab] = useState("body"); const [preferTextBody, setPreferTextBody] = useState(false); + const [fullscreenOpen, setFullscreenOpen] = useState(false); const [query, setQuery] = useState(""); const bodyKey = logBodyCacheKey(body); const bodyView = useMemo(() => cachedFormatLogBodyView(bodyKey, body), [bodyKey]); @@ -721,6 +853,19 @@ function LogJsonPanel({ setPreferTextBody(false); }, [bodyKey]); + useEffect(() => { + if (!fullscreenOpen) { + return; + } + const closeOnEscape = (event: KeyboardEvent) => { + if (event.key === "Escape") { + setFullscreenOpen(false); + } + }; + window.addEventListener("keydown", closeOnEscape); + return () => window.removeEventListener("keydown", closeOnEscape); + }, [fullscreenOpen]); + function toggleJsonPath(path: string) { setExpandedJsonPaths((current) => { const next = new Set(current); @@ -757,36 +902,49 @@ function LogJsonPanel({
{selectedTab === "body" ? ( <> -
-
- - setQuery(event.target.value)} - placeholder={t("筛选 JSON...")} - value={query} - /> -
- {bodyView.json !== undefined && query.trim() === "" ? ( - - ) : null} - {body?.contentType ? {body.contentType} : null} - {body?.truncated ? {t("truncated")} : null} -
- - {showJsonTree ? ( - - ) : ( -
{visible}
- )} + setPreferTextBody((current) => !current)} + preferTextBody={preferTextBody} + query={query} + title={title} + /> + setFullscreenOpen(true)} + > + + {fullscreenOpen ? ( + setFullscreenOpen(false)} + onQueryChange={setQuery} + onToggleJsonPath={toggleJsonPath} + onToggleTextBody={() => setPreferTextBody((current) => !current)} + preferTextBody={preferTextBody} + query={query} + showJsonTree={showJsonTree} + subtitle={subtitle} + title={title} + visible={visible} + value={bodyView.json} + /> + ) : null} ) : (
@@ -798,6 +956,153 @@ function LogJsonPanel({ ); } +function LogJsonBodyToolbar({ + body, + bodyView, + onQueryChange, + onToggleTextBody, + preferTextBody, + query, + title +}: { + body?: RequestLogBody; + bodyView: ReturnType; + onQueryChange: (value: string) => void; + onToggleTextBody: () => void; + preferTextBody: boolean; + query: string; + title: string; +}) { + const t = useAppText(); + + return ( +
+
+ + onQueryChange(event.target.value)} + placeholder={t("筛选 JSON...")} + value={query} + /> +
+ {bodyView.json !== undefined && query.trim() === "" ? ( + + ) : null} + {body?.contentType ? {body.contentType} : null} + {body?.truncated ? {t("truncated")} : null} +
+ ); +} + +function LogJsonBodyContent({ + expandedJsonPaths, + onToggleJsonPath, + showJsonTree, + value, + visible +}: { + expandedJsonPaths: Set; + onToggleJsonPath: (path: string) => void; + showJsonTree: boolean; + value: unknown; + visible: string; +}) { + return showJsonTree ? ( + + ) : ( +
{visible}
+ ); +} + +function LogJsonFullscreenViewer({ + body, + bodyView, + copyLabel, + copyText, + expandedJsonPaths, + onClose, + onQueryChange, + onToggleJsonPath, + onToggleTextBody, + preferTextBody, + query, + showJsonTree, + subtitle, + title, + value, + visible +}: { + body?: RequestLogBody; + bodyView: ReturnType; + copyLabel: string; + copyText: string; + expandedJsonPaths: Set; + onClose: () => void; + onQueryChange: (value: string) => void; + onToggleJsonPath: (path: string) => void; + onToggleTextBody: () => void; + preferTextBody: boolean; + query: string; + showJsonTree: boolean; + subtitle?: string; + title: string; + value: unknown; + visible: string; +}) { + const t = useAppText(); + + return ( +
+
+ {title} + {subtitle ? {subtitle} : null} + +
+ +
+ + + +
+
+ ); +} + function logBodyCacheKey(body: RequestLogBody | undefined): string { if (!body) { return "missing"; @@ -901,11 +1206,15 @@ function jsonContainerHiddenSummary(value: Record | unknown[], function LogBodyViewer({ children, copyLabel, - copyText + copyText, + fullscreenLabel, + onFullscreen }: { children: ReactNode; copyLabel: string; copyText: string; + fullscreenLabel?: string; + onFullscreen?: () => void; }) { const t = useAppText(); const [copied, setCopied] = useState(false); @@ -925,20 +1234,33 @@ function LogBodyViewer({ return (
- +
+ {onFullscreen ? ( + + ) : null} + +
{children}
); @@ -954,7 +1276,7 @@ function LogJsonTree({ value: unknown; }) { return ( -
+
); @@ -1091,10 +1413,27 @@ function JsonPrimitiveValue({ value }: { value: unknown }) { return {String(value)}; } -function NetworkHeaderCell({ label }: { label: string }) { +function NetworkHeaderCell({ + label, + onResizeStart, + resizeLabel +}: { + label: string; + onResizeStart?: (event: ReactPointerEvent) => void; + resizeLabel?: string; +}) { return ( -
- {label} +
+ {label} + {onResizeStart ? ( +
); } diff --git a/src/renderer/pages/home/shared/extensions.ts b/src/renderer/pages/home/shared/extensions.ts index 865da24..0c51be2 100644 --- a/src/renderer/pages/home/shared/extensions.ts +++ b/src/renderer/pages/home/shared/extensions.ts @@ -697,12 +697,12 @@ export function wrapperPluginCapability(item: Record): string { if (isClaudeDesignPluginConfig(item)) { const routing = readClaudeDesignRoutingConfig(item.config); - const routeCount = routing.rules.length + (routing.defaultTarget ? 1 : 0); + const routeCount = routing.rules.length; capabilities.push(routeCount > 0 ? `${routeCount} model ${routeCount === 1 ? "route" : "routes"}` : "Configurable routing"); } if (isCursorProxyPluginConfig(item)) { const routing = readClaudeDesignRoutingConfig(item.config); - const routeCount = routing.rules.length + (routing.defaultTarget ? 1 : 0); + const routeCount = routing.rules.length; capabilities.push(routeCount > 0 ? `${routeCount} model ${routeCount === 1 ? "route" : "routes"}` : "Configurable routing"); } diff --git a/src/renderer/pages/home/shared/i18n.tsx b/src/renderer/pages/home/shared/i18n.tsx index a0ba92e..6637543 100644 --- a/src/renderer/pages/home/shared/i18n.tsx +++ b/src/renderer/pages/home/shared/i18n.tsx @@ -320,6 +320,10 @@ export const appCopy: Record = { "Requests, tokens, cost": "Requests, tokens, cost", "Remove widget": "Remove widget", "Reset layout": "Reset layout", + "Close fullscreen JSON viewer": "Close fullscreen JSON viewer", + "Fullscreen JSON viewer": "Fullscreen JSON viewer", + "Open fullscreen JSON viewer": "Open fullscreen JSON viewer", + "Resize column width": "Resize column width", "Resize widget height": "Resize widget height", "Resize widget size": "Resize widget size", "Resize widget width": "Resize widget width", @@ -1033,6 +1037,10 @@ export const appCopy: Record = { "Requests, tokens, cost": "请求、Token、成本", "Result": "结果", "Reset layout": "重置布局", + "Close fullscreen JSON viewer": "关闭全屏 JSON 查看器", + "Fullscreen JSON viewer": "全屏 JSON 查看器", + "Open fullscreen JSON viewer": "全屏查看 JSON", + "Resize column width": "调整列宽", "Resize widget height": "调整组件高度", "Resize widget size": "调整组件大小", "Resize widget width": "调整组件宽度", diff --git a/src/renderer/pages/home/shared/options.ts b/src/renderer/pages/home/shared/options.ts index e0578e2..e10caa3 100644 --- a/src/renderer/pages/home/shared/options.ts +++ b/src/renderer/pages/home/shared/options.ts @@ -65,7 +65,7 @@ type ProviderAccountDraftMode = "standard" | "http-json" | "raw"; type ApiKeyLimitMetric = "images" | "requests" | "tokens"; type ApiKeyExpirationPreset = "7d" | "30d" | "90d" | "custom" | "never"; type LimitWindowPreset = "day" | "hour" | "minute"; -type ClaudeDesignRouteRuleType = "always" | "image" | "long-context" | "model" | "model-prefix" | "thinking" | "web-search"; +type ClaudeDesignRouteRuleType = "always" | "model" | "model-prefix"; type VirtualModelClientToolsPolicy = "allow" | "deny"; type VirtualModelMatchMode = "alias" | "prefix" | "suffix"; export type AgentFilterValue = AgentKind | "all"; @@ -211,12 +211,7 @@ export const routerRewriteOperationOptions: Array<{ label: string; value: Router ]; export const legacyRouterRuleTypes: RouterRuleType[] = [ - "image", - "long-context", - "model-prefix", - "subagent", - "thinking", - "web-search" + "model-prefix" ]; export const routerFallbackModeOptions: Array<{ label: string; value: RouterFallbackMode }> = [ @@ -236,10 +231,6 @@ export const removedLegacyRouterRuleIds = new Set([ export const claudeDesignRouteRuleTypeOptions: Array<{ label: string; value: ClaudeDesignRouteRuleType }> = [ { label: "Exact model", value: "model" }, { label: "Model prefix", value: "model-prefix" }, - { label: "Long context", value: "long-context" }, - { label: "Thinking", value: "thinking" }, - { label: "Web search", value: "web-search" }, - { label: "Image content", value: "image" }, { label: "Always", value: "always" } ]; diff --git a/src/renderer/pages/home/shared/providers.ts b/src/renderer/pages/home/shared/providers.ts index 71864da..2e434f7 100644 --- a/src/renderer/pages/home/shared/providers.ts +++ b/src/renderer/pages/home/shared/providers.ts @@ -506,13 +506,6 @@ export function routerRuleConditionFromRule(rule: RouterRule, config?: AppConfig if (rule.type === "condition") { return undefined; } - if (rule.type === "long-context") { - return { - left: "request.tokenCount", - operator: ">", - right: String(rule.threshold ?? config?.Router.longContextThreshold ?? "200000") - }; - } if (rule.type === "model-prefix") { return { left: "request.body.model", @@ -520,34 +513,6 @@ export function routerRuleConditionFromRule(rule: RouterRule, config?: AppConfig right: rule.pattern ?? "" }; } - if (rule.type === "thinking") { - return { - left: "request.body.thinking", - operator: "==", - right: "true" - }; - } - if (rule.type === "web-search") { - return { - left: "request.body.tools", - operator: "contains-deep", - right: "web_search" - }; - } - if (rule.type === "image") { - return { - left: "request.body.messages", - operator: "contains-deep", - right: "image" - }; - } - if (rule.type === "subagent") { - return { - left: "request.body.system.1.text", - operator: "==", - right: "" - }; - } return undefined; } diff --git a/src/renderer/pages/home/shared/routing.ts b/src/renderer/pages/home/shared/routing.ts index c985363..ce8c909 100644 --- a/src/renderer/pages/home/shared/routing.ts +++ b/src/renderer/pages/home/shared/routing.ts @@ -442,8 +442,7 @@ export function normalizeRouterRules(value: unknown): RouterRule[] | undefined { const target = stringValue(item.target); const threshold = Number(item.threshold); const condition = normalizeRouterRuleCondition(item.condition ?? item) ?? routerRuleConditionFromLegacy(type, { - pattern, - threshold: Number.isFinite(threshold) && threshold > 0 ? Math.trunc(threshold) : undefined + pattern }); const rewrites = normalizeRouterRuleRewrites(item); const rawFallback = item.fallback ?? item.failureFallback ?? item.fallbackStrategy; @@ -459,7 +458,7 @@ export function normalizeRouterRules(value: unknown): RouterRule[] | undefined { ...(rewrites.length > 0 ? { rewrites } : {}), ...(target ? { target } : {}), ...(Number.isFinite(threshold) && threshold > 0 ? { threshold: Math.trunc(threshold) } : {}), - type: condition && type !== "subagent" ? "condition" : type + type: condition ? "condition" : type }; }) .filter((item): item is RouterRule => Boolean(item)); @@ -503,15 +502,8 @@ export function parseRouterRuleOperator(value: unknown): RouterRuleOperator | un function routerRuleConditionFromLegacy( type: RouterRuleType, - input: { pattern?: string; threshold?: number } + input: { pattern?: string } ): RouterRuleCondition | undefined { - if (type === "long-context") { - return { - left: "request.tokenCount", - operator: ">", - right: String(input.threshold ?? "200000") - }; - } if (type === "model-prefix" && input.pattern) { return { left: "request.body.model", @@ -519,27 +511,6 @@ function routerRuleConditionFromLegacy( right: input.pattern }; } - if (type === "thinking") { - return { - left: "request.body.thinking", - operator: "==", - right: "true" - }; - } - if (type === "web-search") { - return { - left: "request.body.tools", - operator: "contains-deep", - right: "web_search" - }; - } - if (type === "image") { - return { - left: "request.body.messages", - operator: "contains-deep", - right: "image" - }; - } return undefined; } @@ -719,7 +690,12 @@ export function normalizeClaudeDesignRoutingRuleDraft(value: unknown, index: num if (!isPlainRecord(value)) { return undefined; } - const type = parseClaudeDesignRouteRuleType(value.type) ?? "model"; + const rawType = stringValue(value.type); + const parsedType = parseClaudeDesignRouteRuleType(value.type); + if (rawType && !parsedType) { + return undefined; + } + const type = parsedType ?? "model"; const target = stringValue(value.target) || composeRouteTargetValue(value.targetProvider, value.targetModel) || @@ -783,9 +759,6 @@ export function normalizeClaudeDesignRuleTypeChange( if (type === "model-prefix" && !rule.pattern.trim()) { patch.pattern = defaults.pattern; } - if (type === "long-context" && !rule.threshold.trim()) { - patch.threshold = "200000"; - } return patch; } @@ -806,16 +779,12 @@ export function isClaudeDesignRoutingDraftValid(draft: ClaudeDesignRoutingDraft) if (rule.type === "model-prefix") { return Boolean(rule.pattern.trim()); } - if (rule.type === "long-context") { - return numberValue(rule.threshold) > 0; - } return true; }); } export function claudeDesignRoutingConfigFromDraft(draft: ClaudeDesignRoutingDraft): Record { return { - ...(draft.defaultTarget.trim() ? { default: draft.defaultTarget.trim() } : {}), enabled: draft.enabled, rules: draft.rules.map((rule) => { const output: Record = { @@ -831,9 +800,6 @@ export function claudeDesignRoutingConfigFromDraft(draft: ClaudeDesignRoutingDra if (rule.type === "model-prefix") { output.pattern = rule.pattern.trim(); } - if (rule.type === "long-context") { - output.threshold = numberValue(rule.threshold); - } return output; }) }; @@ -863,21 +829,6 @@ export function buildPluginRoutingRows(plugin: AppConfig["plugins"][number], plu const routing = readClaudeDesignRoutingConfig(plugin.config); const baseEnabled = plugin.enabled !== false && routing.enabled; const rows: RoutingRuleRow[] = []; - if (routing.defaultTarget) { - rows.push({ - condition: "always", - enabled: baseEnabled, - key: `plugin-${pluginIndex}-${pluginName}-default`, - name: "Default", - pluginIndex, - readonly: true, - ruleCount: 0, - ruleId: "default", - sourceLabel: `Plugin: ${pluginName}`, - target: routing.defaultTarget, - typeLabel: "Always" - }); - } routing.rules.forEach((rule, ruleIndex) => { rows.push({ condition: formatClaudeDesignRoutingRuleCondition(rule), @@ -915,18 +866,6 @@ export function formatClaudeDesignRoutingRuleCondition(rule: ClaudeDesignRouting if (rule.type === "model-prefix") { return rule.pattern ? `starts with ${rule.pattern}` : "prefix unset"; } - if (rule.type === "long-context") { - return `>${rule.threshold || "threshold"} tokens`; - } - if (rule.type === "thinking") { - return "thinking enabled"; - } - if (rule.type === "web-search") { - return "web_search tool"; - } - if (rule.type === "image") { - return "image content"; - } return "always"; } @@ -940,7 +879,7 @@ export function isClaudeDesignRouteRuleType(value: string): value is ClaudeDesig } export function isClaudeDesignStaticRuleType(type: ClaudeDesignRouteRuleType): boolean { - return type === "always" || type === "image" || type === "thinking" || type === "web-search"; + return type === "always"; } export function claudeDesignRouteRuleTypeLabel(type: ClaudeDesignRouteRuleType): string { diff --git a/src/renderer/pages/home/shared/types.ts b/src/renderer/pages/home/shared/types.ts index 8e1d68e..11fc81c 100644 --- a/src/renderer/pages/home/shared/types.ts +++ b/src/renderer/pages/home/shared/types.ts @@ -564,7 +564,7 @@ export type RoutingRewriteDraftRow = { value: string; }; -export type ClaudeDesignRouteRuleType = "always" | "image" | "long-context" | "model" | "model-prefix" | "thinking" | "web-search"; +export type ClaudeDesignRouteRuleType = "always" | "model" | "model-prefix"; export type ClaudeDesignRoutingRuleDraft = { enabled: boolean; diff --git a/src/renderer/styles/globals.css b/src/renderer/styles/globals.css index b6b4bcf..c3792ee 100644 --- a/src/renderer/styles/globals.css +++ b/src/renderer/styles/globals.css @@ -556,11 +556,23 @@ .network-table-scroll, .network-detail, + .network-json-fullscreen, + .network-json-fullscreen-body, .network-pane-body { background: var(--network-panel); border-color: var(--network-border); } + .network-json-fullscreen { + color: var(--network-text); + } + + .network-json-fullscreen-header { + background: var(--network-panel-alt); + border-color: var(--network-border); + box-shadow: var(--network-shadow); + } + .network-table-header, .network-kv-header, .network-body-meta { @@ -573,6 +585,41 @@ border-color: var(--network-border-strong); } + .network-column-resize-handle { + appearance: none; + background: transparent; + border: 0; + bottom: 0; + cursor: col-resize; + margin: 0; + outline: none; + padding: 0; + position: absolute; + right: -4px; + top: 0; + width: 8px; + z-index: 1; + } + + .network-column-resize-handle::after { + background: var(--network-border-strong); + bottom: 6px; + content: ""; + left: 50%; + opacity: .62; + position: absolute; + top: 6px; + transform: translateX(-50%); + width: 1px; + } + + .network-column-resize-handle:hover::after, + .network-column-resize-handle:focus-visible::after { + background: var(--network-accent); + opacity: 1; + width: 2px; + } + .network-empty { background: var(--network-panel); color: var(--network-text-subtle); diff --git a/src/server/gateway/claude-code-router-plugin.ts b/src/server/gateway/claude-code-router-plugin.ts index ab47884..99888a4 100644 --- a/src/server/gateway/claude-code-router-plugin.ts +++ b/src/server/gateway/claude-code-router-plugin.ts @@ -60,7 +60,7 @@ export class ClaudeCodeRouterPlugin { }; const customModel = await this.resolveCustomRoute(request); - const configuredDecision = resolveConfiguredRouteDecision(request, this.config, tokenCount); + const configuredDecision = resolveConfiguredRouteDecision(request, this.config); if (customModel) { body.model = customModel; } else { @@ -177,8 +177,7 @@ function isPathInside(file: string, root: string): boolean { function resolveConfiguredRouteDecision( request: MutableRequestLike, - config: AppConfig, - tokenCount: number + config: AppConfig ): ConfiguredRouteDecision { const requestedModel = readString(request.body.model); const explicitModel = normalizeRouteSelector(requestedModel); @@ -189,7 +188,7 @@ function resolveConfiguredRouteDecision( const router = config.Router; const rules = router.rules ?? []; for (const rule of rules) { - const decision = resolveRouterRule(rule, request, tokenCount, router); + const decision = resolveRouterRule(rule, request, router); if (decision) { return decision; } @@ -201,7 +200,6 @@ function resolveConfiguredRouteDecision( function resolveRouterRule( rule: RouterRule, request: MutableRequestLike, - tokenCount: number, router: RouterConfig ): ConfiguredRouteDecision | undefined { if (!rule.enabled) { @@ -209,11 +207,6 @@ function resolveRouterRule( } const fallback = rule.fallback ?? router.fallback; - if (rule.type === "subagent") { - const subagentModel = extractSubagentModel(request.body.system); - return subagentModel ? { fallback, model: normalizeRouteSelector(subagentModel), reason: "subagent" } : undefined; - } - const rewrites = routerRuleRewritesFromRule(rule); if (rewrites.length === 0) { return undefined; @@ -225,11 +218,6 @@ function resolveRouterRule( : undefined; } - if (rule.type === "long-context") { - const threshold = rule.threshold || router.longContextThreshold || 200000; - return tokenCount > threshold ? routerRuleRewriteDecision(rule, rewrites, fallback) : undefined; - } - if (rule.type === "model-prefix") { const pattern = readString(rule.pattern); const requestedModel = readString(request.body.model); @@ -238,18 +226,6 @@ function resolveRouterRule( : undefined; } - if (rule.type === "thinking") { - return request.body.thinking ? routerRuleRewriteDecision(rule, rewrites, fallback) : undefined; - } - - if (rule.type === "web-search") { - return hasWebSearchTool(request.body.tools) ? routerRuleRewriteDecision(rule, rewrites, fallback) : undefined; - } - - if (rule.type === "image") { - return hasImageContent(request.body.messages) ? routerRuleRewriteDecision(rule, rewrites, fallback) : undefined; - } - return undefined; } @@ -731,36 +707,6 @@ function estimateTextTokens(text: string): number { return Math.max(1, Math.ceil((asciiWords + cjkChars) * 1.15)); } -function extractSubagentModel(system: unknown): string | undefined { - if (!Array.isArray(system) || system.length < 2) { - return undefined; - } - const second = system[1]; - if (!isRecord(second) || typeof second.text !== "string") { - return undefined; - } - - const match = second.text.match(/(.*?)<\/CCR-SUBAGENT-MODEL>/s); - if (!match?.[1]) { - return undefined; - } - - second.text = second.text.replace(match[0], ""); - return match[1].trim(); -} - -function hasWebSearchTool(tools: unknown): boolean { - return Array.isArray(tools) && tools.some((tool) => isRecord(tool) && readString(tool.type)?.startsWith("web_search")); -} - -function hasImageContent(messages: unknown): boolean { - if (!Array.isArray(messages)) { - return false; - } - - return messages.some((message) => JSON.stringify(message).includes("\"image\"")); -} - function resolveSessionId(body: Record, headers: Record): string | undefined { const fromHeader = readHeader(headers["x-claude-code-session-id"]) || readHeader(headers["x-claude-session-id"]); if (fromHeader) { diff --git a/src/server/gateway/service.ts b/src/server/gateway/service.ts index 7e0e16d..0e768be 100644 --- a/src/server/gateway/service.ts +++ b/src/server/gateway/service.ts @@ -882,10 +882,10 @@ function writeCoreGatewayConfig(config: AppConfig, rawTraceSyncToken: string): v ...pluginService.getCoreProviderPlugins() ]); const codexOauthProviderNames = codexOauthLocalProviderNames(providerPlugins); - const virtualModelProfiles = withOptimisticVirtualModelStreams(withCodexCompatibleVirtualModelProfiles(withFusionVirtualModelAliases([ + const virtualModelProfiles = normalizeCoreGatewayVirtualModelProfiles(withOptimisticVirtualModelStreams(withCodexCompatibleVirtualModelProfiles(withFusionVirtualModelAliases([ ...(config.virtualModelProfiles ?? []), ...pluginService.getVirtualModelProfiles() - ]))); + ]))), config); const coreEndpoint = endpoint(config.gateway.coreHost, config.gateway.corePort); const builtinToolArtifacts = fusionBuiltinToolArtifacts(virtualModelProfiles, coreEndpoint); const providers = [ @@ -957,6 +957,98 @@ function writePrivateTextFile(file: string, content: string): void { } } +export function normalizeCoreGatewayVirtualModelProfiles(profiles: unknown[], config: AppConfig): unknown[] { + return profiles.map((profile) => normalizeCoreGatewayVirtualModelProfile(profile, config)); +} + +function normalizeCoreGatewayVirtualModelProfile(profile: unknown, config: AppConfig): unknown { + if (!isRecord(profile)) { + return profile; + } + + let nextProfile: Record | undefined; + const baseModel = isRecord(profile.baseModel) ? profile.baseModel : undefined; + const fixedModel = stringValue(baseModel?.fixedModel); + const rewrittenFixedModel = fixedModel + ? rewriteModelSelectorForCoreGatewayProfile(fixedModel, config, "anthropic_messages") + : undefined; + if (baseModel && rewrittenFixedModel && rewrittenFixedModel !== fixedModel) { + nextProfile = { + ...profile, + baseModel: { + ...baseModel, + fixedModel: rewrittenFixedModel + } + }; + } + + const sourceProfile = nextProfile ?? profile; + const metadata = isRecord(sourceProfile.metadata) ? sourceProfile.metadata : undefined; + const fusionVision = isRecord(metadata?.fusionVision) ? metadata.fusionVision : undefined; + const visionBaseUrl = stringValue(fusionVision?.baseUrl); + const visionSelectorField = stringValue(fusionVision?.modelSelector) ? "modelSelector" : stringValue(fusionVision?.model) ? "model" : undefined; + const visionSelector = visionSelectorField ? stringValue(fusionVision?.[visionSelectorField]) : undefined; + const rewrittenVisionSelector = fusionVision && !visionBaseUrl && visionSelector + ? rewriteModelSelectorForCoreGatewayProfile(visionSelector, config, "openai_chat_completions") + : undefined; + + if (metadata && fusionVision && visionSelectorField && rewrittenVisionSelector && rewrittenVisionSelector !== visionSelector) { + nextProfile = { + ...sourceProfile, + metadata: { + ...metadata, + fusionVision: { + ...fusionVision, + [visionSelectorField]: rewrittenVisionSelector + } + } + }; + } + + return nextProfile ?? profile; +} + +function rewriteModelSelectorForCoreGatewayProfile( + model: string, + config: AppConfig, + clientProtocol: GatewayProviderProtocol +): string | undefined { + const normalized = normalizeRouteSelector(model); + if (!normalized) { + return undefined; + } + + const publicModel = resolveGatewayPublicModelId(normalized, config) ?? normalized; + const selector = + resolveConfiguredProviderModelSelector(publicModel, config) ?? + resolveUniqueConfiguredProviderModelSelector(publicModel, config); + if (!selector) { + return publicModel; + } + + const providerName = coreGatewayProviderSelectorName(selector.provider, clientProtocol); + return providerName ? `${providerName}/${selector.model}` : publicModel; +} + +function coreGatewayProviderSelectorName( + provider: GatewayProviderConfig, + clientProtocol: GatewayProviderProtocol +): string | undefined { + const capability = providerCapabilityForClientProtocol(provider, clientProtocol); + const explicitCapabilities = normalizedProviderCapabilities(provider); + const protocol = capability?.type ?? (explicitCapabilities.length === 0 ? providerProtocolForClientProtocol(provider, clientProtocol) : undefined); + if (!protocol) { + return undefined; + } + + const credentials = sortProviderCredentialsForConfig(activeProviderCredentials(provider)); + if (credentials.length > 0) { + return providerCredentialInternalName(provider, protocol, credentials[0]); + } + + return capability ? providerCapabilityInternalName(provider, protocol) : providerRuntimeId(provider); +} + function withCodexOauthRuntimeDefaults(providerPlugins: unknown[]): unknown[] { const codexAuth = readCodexAuth(); return providerPlugins.map((plugin) => { diff --git a/src/shared/app.ts b/src/shared/app.ts index 6788072..b3578c7 100644 --- a/src/shared/app.ts +++ b/src/shared/app.ts @@ -461,12 +461,7 @@ export type GatewayProviderConnectivityCheckReport = { export type RouterRuleType = | "condition" - | "image" - | "long-context" - | "model-prefix" - | "subagent" - | "thinking" - | "web-search"; + | "model-prefix"; export type RouterRuleOperator = | "==" diff --git a/tests/main/gateway-virtual-models.test.mjs b/tests/main/gateway-virtual-models.test.mjs new file mode 100644 index 0000000..4931a1f --- /dev/null +++ b/tests/main/gateway-virtual-models.test.mjs @@ -0,0 +1,61 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { normalizeCoreGatewayVirtualModelProfiles } from "../../src/server/gateway/service.ts"; + +test("gateway config rewrites Fusion fixed base and vision models to core provider selectors", () => { + const providerName = "Zhipu AI (China) - Coding Plan"; + const config = { + Providers: [ + { + baseUrl: "https://open.bigmodel.cn/api/coding/paas/v4", + capabilities: [ + { baseUrl: "https://open.bigmodel.cn/api/coding/paas/v4", type: "openai_chat_completions" }, + { baseUrl: "https://open.bigmodel.cn/api/coding/paas/v4", type: "anthropic_messages" } + ], + credentials: [{ apiKey: "test-key", id: "test-1" }], + models: ["glm-5.2", "glm-5v-turbo"], + name: providerName, + type: "openai_chat_completions" + } + ], + Router: { fallback: { mode: "off", models: [], retryCount: 0 } }, + gateway: {} + }; + const profiles = [ + { + baseModel: { fixedModel: `${providerName}/glm-5.2`, mode: "fixed" }, + displayName: "GLM Fusion", + enabled: true, + execution: { + clientToolsPolicy: "allow", + maxToolCalls: 8, + maxTurns: 6, + mode: "tool_loop", + streamMode: "optimistic" + }, + id: "glm-fusion", + key: "glm-fusion", + match: { exactAliases: ["glm-fusion"], prefixes: [], suffixes: [] }, + materialization: { enabled: true, includeInGatewayModels: true }, + metadata: { + fusionVision: { + modelSelector: `${providerName}/glm-5v-turbo`, + toolName: "vision_understand_glm_fusion" + } + }, + tools: [{ name: "vision_understand_glm_fusion", visibility: "internal" }] + } + ]; + + const [profile] = normalizeCoreGatewayVirtualModelProfiles(profiles, config); + + assert.match( + profile.baseModel.fixedModel, + /^provider-zhipu-ai-china---coding-plan-[a-f0-9]{10}::anthropic_messages::cred:test-1\/glm-5\.2$/ + ); + assert.match( + profile.metadata.fusionVision.modelSelector, + /^provider-zhipu-ai-china---coding-plan-[a-f0-9]{10}::openai_chat_completions::cred:test-1\/glm-5v-turbo$/ + ); + assert.equal(profiles[0].baseModel.fixedModel, `${providerName}/glm-5.2`); +}); From 451bdb572c68a6a93f0f0542c7ddb03c857d1ee5 Mon Sep 17 00:00:00 2001 From: musi Date: Tue, 30 Jun 2026 22:27:16 +0800 Subject: [PATCH 20/94] Expand configuration docs for dashboard, keys, and providers --- docs/src/content/docs/en/configuration.md | 8 +- .../content/docs/en/configuration/api-keys.md | 52 ++++ .../content/docs/en/configuration/overview.md | 59 ++++ .../docs/en/configuration/providers.md | 134 +++++++- .../content/docs/en/configuration/server.md | 32 ++ .../src/content/docs/en/configuration/tray.md | 45 +++ docs/src/content/docs/en/index.md | 4 +- docs/src/content/docs/zh/configuration.md | 8 +- .../content/docs/zh/configuration/api-keys.md | 52 ++++ .../content/docs/zh/configuration/overview.md | 59 ++++ .../content/docs/zh/configuration/provider.md | 134 +++++++- .../content/docs/zh/configuration/server.md | 32 ++ .../src/content/docs/zh/configuration/tray.md | 45 +++ docs/src/content/docs/zh/index.md | 4 +- docs/src/i18n/content.ts | 20 +- docs/src/pages/configuration.astro | 17 +- docs/src/pages/configuration/[slug].astro | 4 + docs/src/pages/en/configuration.astro | 17 +- docs/src/pages/en/configuration/[slug].astro | 4 + .../pages/home/components/providers.tsx | 2 +- .../pages/home/components/virtual-models.tsx | 285 +++++++++++++++--- src/renderer/pages/home/shared/i18n.tsx | 6 + .../pages/home/shared/virtual-models.ts | 137 ++++++--- tests/renderer/virtual-models.test.ts | 47 +++ 24 files changed, 1080 insertions(+), 127 deletions(-) create mode 100644 docs/src/content/docs/en/configuration/api-keys.md create mode 100644 docs/src/content/docs/en/configuration/overview.md create mode 100644 docs/src/content/docs/en/configuration/server.md create mode 100644 docs/src/content/docs/en/configuration/tray.md create mode 100644 docs/src/content/docs/zh/configuration/api-keys.md create mode 100644 docs/src/content/docs/zh/configuration/overview.md create mode 100644 docs/src/content/docs/zh/configuration/server.md create mode 100644 docs/src/content/docs/zh/configuration/tray.md create mode 100644 tests/renderer/virtual-models.test.ts diff --git a/docs/src/content/docs/en/configuration.md b/docs/src/content/docs/en/configuration.md index dd828f8..36f1c24 100644 --- a/docs/src/content/docs/en/configuration.md +++ b/docs/src/content/docs/en/configuration.md @@ -2,7 +2,7 @@ title: Claude Code Router Detailed Configuration pageTitle: Detailed Configuration eyebrow: Detailed Configuration -lead: Configure providers, routing, Agent Config, Fusion, Bots, and the config database location in detail. +lead: Configure the overview dashboard, API keys, server, providers, routing, Agent Config, Fusion, Bots, tray, and the config database location in detail. --- ## Page Structure @@ -11,6 +11,9 @@ Detailed configuration docs are split into standalone pages. Every left-sidebar | Page | Covers | | --- | --- | +| Overview Dashboard | System status, account balance, usage widgets, layout editing, and share cards | +| API Keys | Client access keys, expiration, and local limits | +| Server | Host, port, proxy mode, system proxy, network capture, and CA certificate | | Provider Config | Upstream services, protocol, Base URL, model list, and credentials | | One click import | Provider deeplink protocol, manifest import, one-click import buttons, and security boundaries | | Routing Config | Default routing, conditional rules, fallback, and request rewrites | @@ -19,8 +22,9 @@ Detailed configuration docs are split into standalone pages. Every left-sidebar | Agent Config | Agent launch method, model, scope, multi-instance launching, and Bot binding | | Extension Mechanism | Wrapper plugins, core gateway plugins, custom extension creation, and debugging | | Bots And IM Agent Relay | Bot forwarding, handoff mode, and platform pages | +| Tray Configuration | Tray icon, balance progress, and tray window widgets | | Config Database Location | SQLite config database location maintained by the desktop app | ## Content Relationships -Provider Config and One click import cover how upstream model services enter CCR. Routing determines where model requests go. Agent Config covers Claude Code, Codex, and ZCode launch, multi-instance usage, and model selection. Fusion covers vision, web search, and MCP tools. Extension Mechanism covers local plugin creation, installation, and debugging. Bots cover IM platform relay. +Overview Dashboard shows system status and usage. API Keys control client access to CCR. Server controls the local gateway listener and proxy features. Provider Config and One click import cover how upstream model services enter CCR. Routing determines where model requests go. Agent Config covers Claude Code, Codex, and ZCode launch, multi-instance usage, and model selection. Fusion covers vision, web search, and MCP tools. Extension Mechanism covers local plugin creation, installation, and debugging. Bots cover IM platform relay. Tray Configuration covers the menu bar icon and tray window. diff --git a/docs/src/content/docs/en/configuration/api-keys.md b/docs/src/content/docs/en/configuration/api-keys.md new file mode 100644 index 0000000..766e588 --- /dev/null +++ b/docs/src/content/docs/en/configuration/api-keys.md @@ -0,0 +1,52 @@ +--- +title: API Keys +pageTitle: API Keys +eyebrow: Detailed Configuration +lead: Manage API keys that clients use to access the CCR gateway, with expiration and local limits. +--- + +## Basic Concept + +The API Keys page manages client access keys for CCR. These keys are different from provider `API key` values and provider credential pools: API Keys control who can call CCR; provider keys control how CCR calls upstream providers. + +When API keys are configured, clients should send `Authorization: Bearer ` or `x-api-key: `. + +## List Fields + +| Field | Capability | +| --- | --- | +| Search API keys | Filters the list by key name or key value. | +| Add API key | Opens the create dialog and generates a new client access key. | +| Name | Display name for the key. Use it to identify a client, team, purpose, or automation. | +| Key | Masked access key. Use `Copy API key` to copy the full key. | +| Expires | Expiration time. After expiration, clients can no longer use the key to access CCR. | +| Limits | Local limit summary. Shows `No limits configured` when no limits are set. | +| Edit API key | Edits expiration and limits. The key value itself is not shown again. | +| Remove API key | Deletes the client access key. Deleted keys stop working immediately. | + +## Create And Edit + +| Field | Capability | +| --- | --- | +| Name | Display name for the new key. Examples: `Claude Code - laptop`, `CI`, or a team name. | +| Expiration | Selects the validity period: `Never`, `7 days`, `30 days`, `90 days`, or `Custom`. | +| Expires at | Appears for `Custom` expiration and sets the exact date and time. | +| API key created | Confirmation dialog after creation. It displays the full key. | +| Copy this key now. It may not be shown again. | Reminder to copy the key immediately because CCR will not show it again after the dialog closes. | + +## Advanced Settings + +`Advanced settings` adds local limits to a client key. When a limit is reached, requests using that key are rejected or limited; provider-side quota is not changed. + +| Field | Capability | +| --- | --- | +| Advanced settings | Expands or collapses limit editing. | +| No limits configured | The key has no local limits. | +| Requests | Limits by request count. | +| Tokens | Limits by token count. | +| Images | Limits by image count. | +| per minute | Uses a 1-minute limit window. | +| per hour | Uses a 1-hour limit window. | +| per day | Uses a 1-day limit window. | +| Add limit | Adds one limit rule. | +| Remove limit | Removes the current limit rule. | diff --git a/docs/src/content/docs/en/configuration/overview.md b/docs/src/content/docs/en/configuration/overview.md new file mode 100644 index 0000000..9f07d50 --- /dev/null +++ b/docs/src/content/docs/en/configuration/overview.md @@ -0,0 +1,59 @@ +--- +title: Overview Dashboard +pageTitle: Overview Dashboard +eyebrow: Detailed Configuration +lead: Customize the CCR home dashboard for system status, account balance, requests, tokens, cost, model distribution, and share cards. +--- + +## Basic Concept + +Overview is the CCR dashboard. It turns gateway status, provider accounts, request usage, and analysis results into draggable widgets. You can switch the time range, edit widgets, resize them, change styles, and generate shareable cards from selected data. + +Overview depends on request logs, usage stats, and provider account usage. Some widgets require Request logs or Agent observability to be enabled in Settings. Account balance widgets require `Fetch usage` on providers. + +## Top Controls + +| Field | Capability | +| --- | --- | +| Usage over time | Changes the statistics range used by the dashboard. | +| Today / 24h / 7d / 30d | Available time windows. Widgets recalculate requests, tokens, cost, and trends for the selected range. | +| Edit widgets | Enters layout editing mode. | +| Reset layout | Restores the default overview layout. | +| Done | Leaves editing mode and keeps the current widget configuration. | + +## Widget Editing + +In editing mode, the left `Components` panel adds widgets, the middle `Preview` panel shows the current layout, and the right `Component properties` panel edits the selected widget. + +| Field | Capability | +| --- | --- | +| Components | List of widgets that can be added. | +| Preview | Current dashboard layout. Widgets can be dragged to reorder. | +| Component properties | Configuration for the selected widget. | +| Component category | Changes the widget category, such as status, account, metric, trend, activity, breakdown, analysis, or share card. | +| Data | Selects the data shown by the widget, such as requests, tokens, cost, account, client analysis, or provider analysis. | +| Widget size | Controls the widget's grid width and height. | +| Style | Changes visual style, such as cards, compact, bar, line, ring, and more. | +| Remove widget | Removes the selected widget from the overview. | + +## Widget Types + +| Widget | Capability | +| --- | --- | +| Status component | Shows a system status timeline for recent gateway health. | +| Account component | Shows provider account balance, quota, or usage. Requires provider `Fetch usage`. | +| Metric component | Shows requests, total tokens, input tokens, output tokens, cache tokens, cache ratio, estimated cost, success rate, errors, or average latency. | +| Trend component | Shows usage trend over time. | +| Activity component | Shows token activity as a heatmap. | +| Breakdown component | Shows Token mix or Model distribution. | +| Analysis component | Shows Client Analysis or Provider Analysis. | +| Share card | Generates shareable PNG cards such as AI Usage Wrapped, CCR Route Map, Model Leaderboard, AI Fuel Cockpit, Token Calendar Poster, and Spend Receipt. | + +## Data Sources + +| Data | Source | +| --- | --- | +| Requests, tokens, cost, success rate, latency | Request logs and usage stats. | +| Account balance, quota, status messages | Provider `Fetch usage` configuration. | +| Client analysis, provider analysis, model distribution | Client, provider, model, and token data from request logs. | +| Agent analysis data | Agent observability settings and agent execution traces. | diff --git a/docs/src/content/docs/en/configuration/providers.md b/docs/src/content/docs/en/configuration/providers.md index 4a7830b..2a6716c 100644 --- a/docs/src/content/docs/en/configuration/providers.md +++ b/docs/src/content/docs/en/configuration/providers.md @@ -7,20 +7,130 @@ lead: Configure upstream model services, credentials, protocols, base URLs, and ## Basic Concept -A provider is an upstream model service. CCR needs at least one provider with a valid protocol, Base URL, model list, and credential. +A provider is an upstream model service. One provider config describes the upstream address, protocol capabilities, model list, authentication, optional multi-key rotation, and optional account usage fetching. + +Each provider needs at least a name, API endpoint, usable protocol, model list, and one valid credential. The add/edit dialog changes which fields are visible depending on preset providers, custom endpoints, local agent login import, and advanced settings. + +Preset providers fill common endpoints, protocols, models, and usage-fetching settings, so they are the recommended starting point. For custom providers, enter the endpoint manually. CCR uses the endpoint and API key to probe protocol compatibility and help identify whether the upstream works with OpenAI Chat, OpenAI Responses, Anthropic Messages, or Gemini Generate. + +## Main Fields + +| Field | Capability | +| --- | --- | +| Select preset provider | Applies a built-in provider template, including default endpoint, supported protocols, default models, icon, provider website, and sometimes account usage settings. Choose `Other / custom API endpoint` for any OpenAI, Anthropic, or Gemini compatible upstream. | +| Import local agent login | Appears while adding a provider if CCR finds usable Claude Code, Codex, or ZCode login state on this computer. Importing creates a provider and provider plugin that reuse the local login credential instead of a normal pasted API key. | +| Name | Internal CCR display name. It is also used by routing, model selectors, logs, and config references. Names must be unique. | +| API endpoint | Upstream API base URL. It controls where requests are sent, and is also used for protocol probing, model discovery, icon detection, and safety checks. Preset providers hide it by default while adding, but it can be overridden in Advanced settings. Custom providers must provide it. | +| API key | Default provider credential. When the credential pool is empty, model requests use this key. Protocol probing, model discovery, connection checks, and default usage fetching also use it. Only use a key issued for the selected endpoint. | +| Models | Model IDs exposed by CCR. Routing rules, profile model selectors, the model catalog, and client `/models` responses all use this list. | +| Search models / All / Clear | When CCR can discover models from the upstream or catalog, you can search, select all, clear, and choose models. Selected models are saved to the provider. | +| Custom models | Manually adds model IDs that discovery did not return. Use this when the provider lacks a `/models` endpoint or a new model is not in the catalog yet. | +| Check Connection | Sends real test requests with the current endpoint, API key, protocol, and selected models. It verifies key, model name, and protocol usability. | +| Models to check | Model selection inside the connection-check confirmation dialog. Use it to test only some models. | +| Check results | Shows whether each model is available, which protocol matched, and the upstream diagnostic message. Passing results do not automatically add models; the main model selection remains authoritative. | + +## Connectivity Checks + +`Check Connection` sends real model requests for the models you select. It verifies whether the endpoint, API key, protocol, and model IDs are usable. The check limits generated output, but it can still create extra token usage or count against provider-side request limits. + +If the provider bills by request, input tokens, or output tokens, select only the models you need to verify instead of checking every model at once. Check results are diagnostic only; they do not automatically change the model list or usage-fetching settings. ## Credentials -Credentials store API keys. Multiple credentials can rotate by priority and weight, and can switch when a key hits a limit or fails. +`API key` is the simplest single-key setup. For multiple upstream keys, expand `Credential pool` in Advanced settings. -Use recognizable labels so failed keys are easy to identify in Logs. - -## Provider Options - -| Field | Description | +| Field | Capability | | --- | --- | -| Name | Internal display name in CCR; keep it short and recognizable | -| Base URL | Upstream service address; custom providers should include the correct API path | -| Protocol | OpenAI, Anthropic, Gemini, or compatible protocol | -| Models | Model list exposed to CCR selectors | -| Request headers | Extra headers required by some compatible services | +| Show credential settings | Expands or collapses credential pool editing. Collapsing does not remove saved credentials. | +| Import JSON | Imports credentials from a JSON file. CCR accepts a top-level array, or an object with a `credentials`, `keys`, or `apiKeys` array. | +| Add key | Adds one upstream API key row. | +| Enable | Controls whether this credential participates in request forwarding and usage fetching. Disabled credentials are kept but not selected. | +| Name | Display name for the credential. It appears in account usage, logs, and diagnostics, so use a recognizable purpose or quota source. | +| API key | The actual key sent to the upstream for this credential. When a credential pool is configured, CCR expands enabled credentials into internal upstream targets and prefers the pool over the main form API key for model requests. | +| Remove | Deletes the credential row. | +| Advanced key options | Expands per-key scheduling and limit fields. | +| Priority | Credential priority. Lower numbers are tried first. If omitted, the row order is used. | +| Weight | Tie-break weight among credentials with the same priority and similar usage. Higher numbers are preferred. Defaults to `1`. | +| Limits JSON | Local limit rules for this key. CCR tracks request, token, or image usage windows and skips a key once it would exceed its limit, then tries another key on the same provider. | + +Common `Limits JSON` fields: + +| Field | Meaning | +| --- | --- | +| `rpm` / `rph` / `rpd` | Max requests per minute / hour / day | +| `tpm` / `tph` / `tpd` | Max tokens per minute / hour / day | +| `ipm` / `iph` / `ipd` | Max images per minute / hour / day | +| `maxRequests` + `windowMs` | Max requests in a custom time window | +| `maxTokens` + `quotaWindowMs` | Max tokens in a custom time window | + +Example: + +```json +{ + "rpm": 60, + "tpm": 100000 +} +``` + +The credential pool is an upstream provider key pool. It is separate from the client access keys configured on the API Keys page. + +## Usage Fetching + +`Fetch usage` lets CCR show balance, subscription quota, status, and messages in the provider list, tray, and account panels. It does not affect whether models can be requested. + +| Field | Capability | +| --- | --- | +| Fetch usage | Enables or disables account usage fetching for this provider. | +| Usage mode | Usage connector mode. `Standard usage endpoint` uses CCR standard account endpoints; `HTTP JSON request` maps a custom JSON endpoint; `Raw connector JSON` edits the connector array directly. | +| Refresh interval ms | Usage refresh interval in milliseconds. Empty uses the default interval. The minimum effective interval is 30000ms. | + +### Standard Usage Endpoint + +This mode tries provider-hosted CCR account endpoints such as `/.well-known/ccr/account` and `/v1/account/limits`. It is best for providers or presets that already implement CCR's standard account format. + +### HTTP JSON Request + +Use this mode when the provider has a balance or quota JSON endpoint that does not match CCR's standard account format. + +| Field | Capability | +| --- | --- | +| Method | Usage request method, `GET` or `POST`. | +| Usage request URL | Usage endpoint URL. It can be a full URL. The request includes the provider API key unless changed through raw connector JSON. | +| Headers | Extra headers for the usage endpoint. Avoid hard-coding sensitive auth headers here; prefer provider API key auth. | +| Body | `POST` request body. Must be valid JSON. | +| Balance remaining field | JSON path for remaining balance. | +| Balance total field | JSON path for total balance or total credits. | +| Balance used field | JSON path for used balance. | +| Balance unit | Balance unit, such as `USD`, `CNY`, or `%`. | +| Subscription remaining field | JSON path for remaining subscription, token, quota, or package amount. | +| Subscription limit field | JSON path for subscription, token, quota, or package limit. | +| Subscription reset field | JSON path for reset time. It may resolve to an ISO string, seconds timestamp, or milliseconds timestamp. | +| Subscription unit | Subscription unit, such as `tokens`, `requests`, or `hours`. | +| Status field | JSON path for account status. Supported values are `ok`, `warning`, `critical`, `error`, and `unsupported`. | +| Message field | JSON path for account message. Useful for provider errors, plan notes, or risk-control messages. | +| Test usage request | Requests and parses the usage endpoint before saving. | +| Response fields | Lists selectable paths from the response. Buttons such as `Balance rem`, `Balance total`, `Balance used`, `Sub rem`, `Sub limit`, and `Reset` fill the matching field. | + +Field paths use CCR's lightweight JSONPath syntax: + +| Syntax | Meaning | +| --- | --- | +| `$` | Whole response object | +| `$.balance.remaining` | Object field | +| `$.items[0].value` | Array index | +| `$["weird-key"]` | Field name with special characters | +| `$.limits[?(@.type=="TOKENS")].remaining` | First array item matching simple equality filters | +| `100 - $.data.percentage` | Numeric subtraction expression, often used to convert used percent into remaining percent | + +### Raw Connector JSON + +`Connectors JSON` edits the `account.connectors` array directly for more complex provider setups. + +| Connector type | Capability | +| --- | --- | +| `standard` | Uses CCR standard account endpoints. | +| `http-json` | Requests a JSON endpoint and maps balance, subscription, status, and message fields. | +| `plugin` | Calls an account usage connector registered by an installed plugin. | +| `local-estimate` | Shows estimated quota from local time-window config without a remote request. | + +`Insert example` fills an example connector array containing `standard`, `http-json`, `plugin`, and `local-estimate` connectors. diff --git a/docs/src/content/docs/en/configuration/server.md b/docs/src/content/docs/en/configuration/server.md new file mode 100644 index 0000000..8742de9 --- /dev/null +++ b/docs/src/content/docs/en/configuration/server.md @@ -0,0 +1,32 @@ +--- +title: Server +pageTitle: Server +eyebrow: Detailed Configuration +lead: Configure the CCR gateway host, port, and Proxy mode for MITM interception and proxying into CCR. +--- + +## Basic Concept + +The Server page controls how the local CCR gateway listens for requests. Normal model requests usually need only `Host` and `Port`. + +## Main Fields + +| Field | Capability | +| --- | --- | +| Host | Host address the CCR gateway listens on. Common values are `127.0.0.1` and `0.0.0.0`. | +| Port | Gateway listening port. Clients should point their API base URL to this port. | + +## Proxy Mode + +Proxy mode is the local proxy capability. When enabled, clients can send HTTP/HTTPS traffic to CCR. CCR uses MITM interception to identify and decrypt HTTPS requests, then proxies supported model requests into the CCR gateway path. + +| Field | Capability | +| --- | --- | +| Proxy mode | Enables Proxy mode. CCR can receive client traffic as an HTTP/HTTPS proxy and use MITM interception to proxy model requests into CCR. | +| System proxy | Points the system proxy at CCR so apps that honor system proxy settings can go through CCR automatically. | +| Capture network | Stores network requests that pass through proxy mode so the Networking page can show request and response details. | +| CA certificate | Trust status of the current proxy CA certificate. | +| Install CA | Installs the CCR proxy CA into the system or user trust store. Installation differs by OS. | +| Check Trust | Checks again whether the proxy CA is trusted by the system. | +| Proxy status | Shows whether the proxy service is running. | +| Restart Proxy | Restarts the proxy service when proxy mode is enabled. | diff --git a/docs/src/content/docs/en/configuration/tray.md b/docs/src/content/docs/en/configuration/tray.md new file mode 100644 index 0000000..c856666 --- /dev/null +++ b/docs/src/content/docs/en/configuration/tray.md @@ -0,0 +1,45 @@ +--- +title: Tray Configuration +pageTitle: Tray Configuration +eyebrow: Detailed Configuration +lead: Configure the CCR system tray icon, balance progress, and tray window widgets. +--- + +## Basic Concept + +Tray settings live under Settings. They control the system tray icon and the tray window. The tray window can show providers, account balance, token trends, activity, metrics, and model share so you can check CCR status without opening the main window. + +## Top Fields + +| Field | Capability | +| --- | --- | +| Tray mascot | Selects the tray icon style. Options include `Random`, `Auralis`, `Solara`, `Vesper`, and `Balance progress`. | +| Balance progress | Uses provider account usage as tray icon progress. Requires `Fetch usage` on a provider first. | +| Account | Selects the provider account used for balance progress. | +| Data | Selects the balance, subscription, or quota meter used as the progress source. | + +If no account data is available, the page shows `No account data is available. Enable account monitoring on a provider first.` This usually means no provider has `Fetch usage` enabled, or usage fetching has not succeeded yet. + +## Tray Window Layout + +| Area | Capability | +| --- | --- | +| Components | Left-side component palette for adding or enabling tray window widgets. | +| Preview | Middle preview area showing the current tray window layout. Widgets can be dragged to reorder. | +| Component properties | Right-side editor for the selected widget's `Style`, or for removing the widget. | + +## Component Types + +| Component | Capability | +| --- | --- | +| Provider component | Shows `Provider tabs` for switching provider data in the tray window. It is a singleton component and can be enabled only once. | +| Header component | Shows `Title and status`. It is a singleton component and can be enabled only once. | +| Account component | Shows `Account balance`. Multiple account components can be added with different styles. | +| Trend component | Shows `Token flow chart`. | +| Activity component | Shows `Token activity`. | +| Metric component | Shows `Token stats`. | +| Breakdown component | Shows `Token mix`, `Circular metrics`, or `Model share`. | + +## Styles + +Different components support different `Style` options. Common styles include `Cards`, `Compact`, `List`, `Pills`, `Line`, `Area`, `Bar`, `Ring`, `Donut`, `Gauges`, `Sparkline`, and `Stacked`. Style changes only affect the tray display; they do not change routing, providers, or usage stats. diff --git a/docs/src/content/docs/en/index.md b/docs/src/content/docs/en/index.md index a628432..97ac403 100644 --- a/docs/src/content/docs/en/index.md +++ b/docs/src/content/docs/en/index.md @@ -26,7 +26,7 @@ The top navigation is split into four standalone pages: | --- | --- | | [Documentation](./) | Product positioning, architecture overview, and reading path | | [Quick Start](guides/) | From installation and provider setup to connecting an agent | -| [Detailed Configuration](configuration/) | Providers, routing, Agent Config, Fusion, Bots, and config database location | +| [Detailed Configuration](configuration/providers/) | Overview dashboard, API keys, server, providers, routing, Agent Config, Fusion, Bots, tray, and config database location | | [Q&A](troubleshooting/) | Request logs, observability panel, and common questions | Bot platform guides are child pages under Detailed Configuration. Each platform has its own page so platform dashboard fields, callback URLs, signatures, and FAQs can be expanded independently. @@ -37,5 +37,5 @@ If this is your first time using CCR: 1. Start with [Quick Start](guides/) to connect a provider and Agent Config. 2. Use the app's request logs to confirm whether requests are passing through CCR. -3. Open [Detailed Configuration](configuration/) for vision, web search, MCP tools, and IM relay. +3. Open [Detailed Configuration](configuration/providers/) for the overview dashboard, API keys, server, providers, vision, web search, MCP tools, tray, and IM relay. 4. Use [Q&A](troubleshooting/) for 401, 404, timeout, wrong-routing, or Bot delivery questions. diff --git a/docs/src/content/docs/zh/configuration.md b/docs/src/content/docs/zh/configuration.md index 8cc3c8e..26df10a 100644 --- a/docs/src/content/docs/zh/configuration.md +++ b/docs/src/content/docs/zh/configuration.md @@ -2,7 +2,7 @@ title: Claude Code Router 详细配置 pageTitle: 详细配置 eyebrow: 详细配置 -lead: 深入配置供应商、路由、Agent配置、Fusion、Bot 和配置数据库位置。这里是按功能查字段和扩展能力的地方。 +lead: 深入配置概览仪表盘、API 密钥、服务、供应商、路由、Agent配置、Fusion、Bot、托盘和配置数据库位置。这里是按功能查字段和扩展能力的地方。 --- ## 页面结构 @@ -11,6 +11,9 @@ lead: 深入配置供应商、路由、Agent配置、Fusion、Bot 和配置数 | 页面 | 内容 | | --- | --- | +| 概览仪表盘 | 系统状态、账户余额、用量组件、布局编辑和分享卡片 | +| API 密钥 | 客户端访问 Key、过期时间和本地限额 | +| 服务配置 | Host、Port、代理模式、系统代理、网络捕获和 CA 证书 | | 供应商配置 | 上游服务、协议、基础 URL、模型列表和凭据 | | 一键导入供应商 | Provider deeplink 协议、Manifest 导入、一键导入按钮和安全边界 | | 路由配置 | 默认路由、条件规则、fallback 和请求改写 | @@ -19,8 +22,9 @@ lead: 深入配置供应商、路由、Agent配置、Fusion、Bot 和配置数 | Agent配置 | Agent 启动方式、模型、作用范围、多开和 Bot 绑定 | | 扩展机制 | Wrapper plugin、Core gateway plugin、自定义扩展创建和调试 | | Bot 与 IM 接力 Agent | Bot 转发、接力模式和平台页面 | +| 托盘配置 | 托盘图标、余额进度条和托盘窗口组件 | | 配置数据库位置 | 桌面 App 维护的 SQLite 配置数据库位置 | ## 内容关系 -供应商配置和一键导入供应商页面覆盖上游模型服务如何进入 CCR;路由决定模型请求的上游去向;Agent配置页面覆盖 Claude Code、Codex 和 ZCode 的启动、多开与模型选择;Fusion 页面覆盖图像、搜索和 MCP 工具;扩展机制页面覆盖本地插件的创建、安装和调试;Bot 页面覆盖 IM 平台接力。 +概览仪表盘用于查看系统状态和用量;API 密钥控制客户端访问 CCR;服务配置控制本地网关监听和代理能力;供应商配置和一键导入供应商页面覆盖上游模型服务如何进入 CCR;路由决定模型请求的上游去向;Agent配置页面覆盖 Claude Code、Codex 和 ZCode 的启动、多开与模型选择;Fusion 页面覆盖图像、搜索和 MCP 工具;扩展机制页面覆盖本地插件的创建、安装和调试;Bot 页面覆盖 IM 平台接力;托盘配置覆盖菜单栏图标和托盘窗口。 diff --git a/docs/src/content/docs/zh/configuration/api-keys.md b/docs/src/content/docs/zh/configuration/api-keys.md new file mode 100644 index 0000000..70e61ff --- /dev/null +++ b/docs/src/content/docs/zh/configuration/api-keys.md @@ -0,0 +1,52 @@ +--- +title: API 密钥 +pageTitle: API 密钥 +eyebrow: 详细配置 +lead: 管理客户端访问 CCR 网关时使用的 API Key,并为每个 Key 设置过期时间和本地限额。 +--- + +## 基本概念 + +“API 密钥”页面管理的是客户端访问 CCR 时使用的访问 Key。它不同于供应商配置里的 `API 密钥` 或 `凭据池`:这里的 Key 用来控制谁能调用 CCR;供应商 Key 用来控制 CCR 调用上游供应商。 + +如果配置了 API Key,客户端请求需要带 `Authorization: Bearer ` 或 `x-api-key: `。 + +## 列表字段 + +| 字段 | 代表的能力 | +| --- | --- | +| 搜索 API 密钥 | 按名称或 Key 内容过滤列表。 | +| 添加 API 密钥 | 打开创建弹窗,生成新的客户端访问 Key。 | +| 名称 | API Key 的显示名,用于区分客户端、团队、用途或自动化来源。 | +| Key | 脱敏后的访问 Key。可以点击 `复制 API 密钥` 复制完整 Key。 | +| 过期 | 当前 Key 的过期时间。过期后客户端不能继续使用这个 Key 访问 CCR。 | +| 限制 | 当前 Key 的本地限额摘要。没有配置限额时显示“未配置限制”。 | +| 编辑 API 密钥 | 修改过期时间和限额。出于安全原因,已创建的 Key 本身不会重新明文展示。 | +| 移除 API 密钥 | 删除当前客户端访问 Key。删除后立即不能再用于请求。 | + +## 创建和编辑 + +| 字段 | 代表的能力 | +| --- | --- | +| 名称 | 新 Key 的显示名。建议写成客户端或用途,例如 `Claude Code - laptop`、`CI` 或团队名。 | +| 过期时间 | 选择 Key 的有效期:`永不`、`7 天`、`30 天`、`90 天` 或 `自定义`。 | +| 过期于 | 选择 `自定义` 时出现,用于填写精确的过期日期和时间。 | +| API 密钥已创建 | 创建成功后的确认弹窗。这里会显示完整 Key。 | +| 请现在复制保存这个密钥,之后可能不会再次完整显示。 | 提醒你立即复制 Key。关闭弹窗后,CCR 不会再次展示完整 Key。 | + +## 高级设置 + +`高级设置` 用于给单个客户端 Key 添加本地限额。限额命中后,使用这个 Key 的客户端请求会被拒绝或限制;它不会修改供应商侧额度。 + +| 字段 | 代表的能力 | +| --- | --- | +| 高级设置 | 展开或收起限额编辑区。 | +| 未配置限制 | 当前 Key 没有任何本地限额。 | +| 请求 | 按请求次数限制。 | +| 令牌 | 按 token 数量限制。 | +| 图片 | 按图片数量限制。 | +| 每分钟 | 限额窗口为 1 分钟。 | +| 每小时 | 限额窗口为 1 小时。 | +| 每天 | 限额窗口为 1 天。 | +| 添加限制 | 新增一条限额规则。 | +| 移除限制 | 删除当前限额规则。 | diff --git a/docs/src/content/docs/zh/configuration/overview.md b/docs/src/content/docs/zh/configuration/overview.md new file mode 100644 index 0000000..24a188b --- /dev/null +++ b/docs/src/content/docs/zh/configuration/overview.md @@ -0,0 +1,59 @@ +--- +title: 概览仪表盘 +pageTitle: 概览仪表盘 +eyebrow: 详细配置 +lead: 自定义 CCR 首页组件,查看系统状态、账户余额、请求量、tokens、成本、模型分布和可分享卡片。 +--- + +## 基本概念 + +“概览”是 CCR 的仪表盘页面。它把网关运行状态、供应商账户、请求用量和分析结果做成可拖拽组件。你可以切换时间范围、编辑组件、调整大小、切换样式,也可以把部分数据生成分享卡片。 + +概览依赖请求日志、用量统计和供应商账户用量。某些组件需要先在“设置”里开启请求日志或 Agent 观测,账户余额组件需要在供应商配置中开启“获取用量”。 + +## 顶部控件 + +| 字段 | 代表的能力 | +| --- | --- | +| 按时间查看用量 | 切换仪表盘使用的统计时间范围。 | +| 今天 / 24 小时 / 7 天 / 30 天 | 可选统计窗口。不同组件会根据这个窗口重新计算请求、tokens、成本和趋势。 | +| 编辑组件 | 进入布局编辑模式。 | +| 重置布局 | 恢复默认概览布局。 | +| 完成 | 退出布局编辑模式并保留当前组件配置。 | + +## 组件区 + +编辑模式下,左侧“组件”用于添加组件,中间“预览”展示当前布局,右侧“组件属性”编辑选中组件。 + +| 字段 | 代表的能力 | +| --- | --- | +| 组件 | 可添加组件列表。 | +| 预览 | 当前仪表盘布局。组件可以拖拽排序。 | +| 组件属性 | 当前选中组件的配置区域。 | +| 组件类型 | 切换组件大类,例如状态、账户、指标、趋势、活跃度、构成、分析或分享卡片。 | +| 数据 | 选择组件展示的数据,例如请求数、tokens、成本、账户、客户端分析或供应商分析。 | +| 组件大小 | 控制组件占用的网格宽高。 | +| 样式 | 切换组件展示样式,例如卡片、紧凑、柱状图、折线图、圆环等。 | +| 移除组件 | 从概览中移除当前组件。 | + +## 组件类型 + +| 组件 | 代表的能力 | +| --- | --- | +| 状态组件 | 展示系统状态时间线,帮助判断网关近期是否正常。 | +| 账户组件 | 展示供应商账户余额、套餐额度或用量。需要供应商开启“获取用量”。 | +| 指标组件 | 展示请求数、总 tokens、输入 tokens、输出 tokens、缓存 tokens、缓存比例、估算成本、成功率、错误数或平均延迟。 | +| 趋势组件 | 展示按时间聚合的用量趋势。 | +| 活跃度组件 | 展示 token 活跃度热力图。 | +| 构成组件 | 展示 Token 构成或模型分布。 | +| 分析组件 | 展示客户端分析或供应商分析。 | +| 分享卡片 | 生成适合分享的 PNG 卡片,例如 AI Usage Wrapped、CCR Route Map、Model Leaderboard、AI Fuel Cockpit、Token Calendar Poster 和 Spend Receipt。 | + +## 数据来源 + +| 数据 | 来源 | +| --- | --- | +| 请求数、tokens、成本、成功率、延迟 | 请求日志和用量统计。 | +| 账户余额、套餐额度、状态消息 | 供应商“获取用量”配置。 | +| 客户端分析、供应商分析、模型分布 | 请求日志中的客户端、供应商、模型和 token 信息。 | +| Agent 分析相关数据 | 设置中的“Agent 观测”和 Agent 执行链路数据。 | diff --git a/docs/src/content/docs/zh/configuration/provider.md b/docs/src/content/docs/zh/configuration/provider.md index ded9a4b..af3cbdb 100644 --- a/docs/src/content/docs/zh/configuration/provider.md +++ b/docs/src/content/docs/zh/configuration/provider.md @@ -7,20 +7,130 @@ lead: 配置 CCR 的上游模型服务,包括协议、基础 URL、模型列 ## 基本概念 -供应商是 CCR 转发请求的上游模型服务。每个供应商至少需要协议、基础 URL、模型列表和一条可用凭据。 +供应商是 CCR 转发请求的上游模型服务。一个供应商配置同时描述了上游地址、协议能力、模型列表、认证方式、可选的多 Key 轮换,以及可选的账号用量读取能力。 + +每个供应商至少需要名称、API 地址、可用协议、模型列表和一条可用凭据。添加或编辑供应商时,弹窗中的字段会根据“预设供应商 / 自定义 API 地址 / 本机 Agent 登录态导入 / 高级设置”动态显示。 + +预设供应商会自动填入常见 API 地址、协议、模型和用量读取配置,适合优先使用。自定义供应商需要手动填写 API 地址,CCR 会根据 API 地址和 API 密钥做协议探测,帮助判断当前上游是否兼容 OpenAI Chat、OpenAI Responses、Anthropic Messages 或 Gemini Generate。 + +## 主字段 + +| 字段 | 代表的能力 | +| --- | --- | +| 选择 预设供应商 | 套用 CCR 内置供应商模板,包括默认 API 地址、可用协议、默认模型、图标、官网链接和部分供应商的用量读取配置。选择 `其他 / 自定义 API 地址` 时,可以接入任意兼容 OpenAI、Anthropic 或 Gemini 协议的上游服务。 | +| 导入本机 Agent 登录态 | 添加供应商时,如果 CCR 在本机发现 Claude Code、Codex 或 ZCode 的可用登录状态,会显示导入入口。导入后 CCR 会生成供应商和对应 provider plugin,用本机已有登录凭据访问上游,不需要手动粘贴常规 API Key。 | +| 名称 | CCR 内部显示名,也是路由、模型选择、日志和配置中识别供应商的名字。名称必须唯一,建议短且稳定。 | +| API 地址 | 上游 API Base URL。它决定请求实际发往哪里,也用于协议探测、模型列表探测、图标探测和安全校验。预设供应商添加时默认隐藏该字段,可在高级设置里覆盖。自定义供应商必须填写。 | +| API 密钥 | 默认供应商凭据。没有配置凭据池时,模型请求会使用这条 Key;协议探测、模型探测、连通性检测和默认用量读取也会使用它。只填写由当前 API 地址对应供应商签发的 Key。 | +| 模型 | 暴露给 CCR 的模型 ID 列表。路由规则、Profile 模型选择、模型目录和客户端 `/models` 响应都会基于这里的模型。 | +| 搜索模型 / 全部 / 清除 | 当 CCR 能从上游或模型目录拿到模型列表时,可以搜索、全选、清除并勾选模型。勾选结果会保存到供应商的模型列表。 | +| 自定义模型 | 手动添加没有被探测出来的模型 ID。适合供应商没有 `/models` 接口,或新模型还未进入模型目录的情况。 | +| 检测连通性 | 用当前 API 地址、API 密钥、协议和所选模型发送真实测试请求。它可以验证 Key、模型名和协议是否真的可调用。 | +| 要检测的模型 | 连接检查确认弹窗中的模型选择。用于控制只测试部分模型,避免一次性检查全部模型造成额外消耗。 | +| 检测结果 | 展示每个模型是否可用、命中的协议和上游返回的诊断信息。可用结果不会自动增加模型,仍以弹窗主表单中的模型选择为准。 | + +## 连通性检测 + +`检测连通性` 会对你选择的模型发送真实的模型请求,用来确认 API 地址、API 密钥、协议和模型 ID 是否可用。检测请求会限制输出长度,但仍然可能产生额外 token 消耗或计入供应商侧请求次数。 + +如果供应商按请求、输入 token 或输出 token 计费,建议只勾选需要确认的模型,不要一次性检查全部模型。检测结果只用于诊断连通性,不会自动修改模型列表或用量读取配置。 ## 凭据 -凭据用来保存 API Key。多条凭据可以按优先级和权重轮换,也可以在某条 Key 触发限额或失败时自动切换。 +`API 密钥` 是最简单的单 Key 配置。需要管理多条上游 Key 时,展开“高级设置”中的“凭据池”。 -建议给每条 Key 设置容易识别的名称,方便在请求日志里定位问题。 - -## 供应商选项 - -| 字段 | 说明 | +| 字段 | 代表的能力 | | --- | --- | -| 名称 | CCR 内部显示名,建议短且可识别 | -| 基础 URL | 上游服务地址,自定义供应商要确认包含正确 API 路径 | -| 协议 | OpenAI / Anthropic / Gemini 等协议 | -| 模型 | 暴露给 CCR 的模型列表 | -| 请求头 | 少数兼容服务需要的额外请求头 | +| 显示凭据配置 | 展开或收起凭据池配置。未展开不影响已保存的凭据,只是隐藏编辑区域。 | +| 导入 JSON | 从 JSON 文件批量导入凭据。支持顶层数组,或对象中的 `credentials`、`keys`、`apiKeys` 数组。 | +| 添加 Key | 新增一条上游 API Key。 | +| 启用 | 控制单条凭据是否参与请求转发和用量读取。关闭后保留配置,但不会被选中。 | +| 名称 | 单条凭据的显示名。会出现在账号用量、日志和内部诊断里,建议写成可识别的用途或额度来源。 | +| API 密钥 | 该凭据实际发送给上游的 Key。配置了凭据池后,CCR 会把启用的凭据展开成多个内部上游目标,并优先使用凭据池,而不是主表单的默认 `API 密钥`。 | +| 移除 | 删除当前凭据行。 | +| Key 高级选项 | 展开单条凭据的调度和限额字段。 | +| 优先级 | 凭据优先级,数字越小越优先。未填写时按凭据行顺序作为优先级。 | +| 权重 | 同优先级、相近使用率下的排序权重,数字越大越优先。未填写时为 `1`。 | +| 限制 JSON | 该 Key 的本地限额规则。CCR 会按请求、tokens 或图片数量统计窗口使用量,达到上限后自动跳过该 Key,尝试同供应商的其他可用 Key。 | + +`限制 JSON` 支持的常用字段: + +| 字段 | 含义 | +| --- | --- | +| `rpm` / `rph` / `rpd` | 每分钟 / 每小时 / 每天最多请求数 | +| `tpm` / `tph` / `tpd` | 每分钟 / 每小时 / 每天最多 tokens | +| `ipm` / `iph` / `ipd` | 每分钟 / 每小时 / 每天最多图片数 | +| `maxRequests` + `windowMs` | 自定义时间窗口内最多请求数 | +| `maxTokens` + `quotaWindowMs` | 自定义时间窗口内最多 tokens | + +示例: + +```json +{ + "rpm": 60, + "tpm": 100000 +} +``` + +凭据池的作用是“上游 Key 池”,不同于“API 密钥”页面里的 CCR 客户端访问 Key。前者控制 CCR 调用供应商时使用哪个 Key,后者控制客户端访问 CCR 时使用哪个 Key。 + +## 用量读取 + +“获取用量”用于让 CCR 在供应商列表、托盘或账号面板中展示余额、套餐额度、状态和错误信息。它不会影响模型是否能请求,只影响账号用量展示。 + +| 字段 | 代表的能力 | +| --- | --- | +| 获取用量 | 启用或关闭该供应商的账号用量读取。关闭后不请求用量接口。 | +| 用量模式 | 用量读取方式。`标准用量端点` 使用 CCR 标准账号端点;`HTTP JSON 请求` 手动配置一个 JSON 接口;`原始连接器 JSON` 直接编辑 connector 数组。 | +| 刷新间隔(毫秒) | 用量刷新间隔,单位毫秒。未填写时使用默认刷新间隔,最小有效间隔为 30000ms。 | + +### 标准用量端点 + +该模式会尝试供应商侧的 CCR 标准账号端点,例如 `/.well-known/ccr/account` 和 `/v1/account/limits`。适合已经适配 CCR 标准格式的供应商或内置预设。 + +### HTTP JSON 请求 + +该模式适合供应商已有自己的余额或额度接口,但返回格式不是 CCR 标准格式的情况。 + +| 字段 | 代表的能力 | +| --- | --- | +| 方法 | 用量请求方法,支持 `GET` 或 `POST`。 | +| 用量请求 URL | 用量接口地址。可以是完整 URL。请求会附带供应商 API Key,除非在 raw connector 中改成其他认证方式。 | +| 请求头 | 用量接口需要的额外请求头。不要在这里写固定的敏感认证头,优先使用供应商 API Key 认证。 | +| 请求体 | `POST` 请求体,必须是合法 JSON。 | +| 余额剩余字段 | 余额剩余值在响应 JSON 中的路径。 | +| 余额总额字段 | 余额总量或充值总额在响应 JSON 中的路径。 | +| 余额已用字段 | 已用余额在响应 JSON 中的路径。 | +| 余额单位 | 余额单位,例如 `USD`、`CNY` 或 `%`。 | +| 订阅剩余字段 | 套餐、订阅、tokens 或配额剩余量路径。 | +| 订阅上限字段 | 套餐、订阅、tokens 或配额总量路径。 | +| 订阅重置字段 | 套餐重置时间路径。可以返回 ISO 时间,也可以返回秒级或毫秒级时间戳。 | +| 订阅单位 | 套餐单位,例如 `tokens`、`requests`、`hours`。 | +| 状态字段 | 账号状态路径。支持 `ok`、`warning`、`critical`、`error`、`unsupported`。 | +| 消息字段 | 账号提示信息路径。适合展示供应商返回的错误、套餐说明或风控提示。 | +| 测试用量请求 | 立即请求用量接口并解析映射结果,方便在保存前验证字段路径。 | +| 响应字段 | 测试后列出响应 JSON 中可选字段。点击 `余额剩余`、`余额总额`、`余额已用`、`订阅剩余`、`订阅上限`、`重置时间` 可以把该路径快速填入对应字段。 | + +字段路径支持 CCR 的轻量 JSONPath 语法: + +| 写法 | 说明 | +| --- | --- | +| `$` | 整个响应对象 | +| `$.balance.remaining` | 读取对象字段 | +| `$.items[0].value` | 读取数组下标 | +| `$["weird-key"]` | 读取包含特殊字符的字段名 | +| `$.limits[?(@.type=="TOKENS")].remaining` | 在数组中查找第一个满足简单等值条件的对象 | +| `100 - $.data.percentage` | 数值字段支持简单减法表达式,常用于把“已用百分比”转换成“剩余百分比” | + +### 原始连接器 JSON + +`连接器 JSON` 允许直接编辑 `account.connectors` 数组,适合需要更复杂能力的供应商。 + +| 连接器类型 | 能力 | +| --- | --- | +| `standard` | 使用 CCR 标准账号端点。 | +| `http-json` | 请求一个 JSON 接口,并用 mapping 字段映射余额、套餐、状态和消息。 | +| `plugin` | 调用已安装插件注册的账号用量 connector。 | +| `local-estimate` | 不请求远程接口,基于本地窗口配置展示估算额度。 | + +点击 `插入示例` 会填入一个包含 `standard`、`http-json`、`plugin` 和 `local-estimate` 的示例 connector 数组。 diff --git a/docs/src/content/docs/zh/configuration/server.md b/docs/src/content/docs/zh/configuration/server.md new file mode 100644 index 0000000..3ec3d25 --- /dev/null +++ b/docs/src/content/docs/zh/configuration/server.md @@ -0,0 +1,32 @@ +--- +title: 服务配置 +pageTitle: 服务配置 +eyebrow: 详细配置 +lead: 配置 CCR 网关监听地址、端口,以及通过代理模式进行 MITM 劫持并代理到 CCR 的能力。 +--- + +## 基本概念 + +“服务”页面控制 CCR 本地网关如何监听请求。普通模型请求通常只需要配置 `Host` 和 `Port`。 + +## 主字段 + +| 字段 | 代表的能力 | +| --- | --- | +| Host | CCR 网关监听的主机地址。常见值是 `127.0.0.1` 或 `0.0.0.0`。 | +| Port | CCR 网关监听端口。客户端需要把 API Base URL 指向这个端口。 | + +## 代理模式 + +代理模式是本地代理能力。开启后,客户端可以把 HTTP/HTTPS 流量交给 CCR;CCR 会通过 MITM 劫持识别和解密 HTTPS 请求,并把可处理的模型请求代理到 CCR 网关链路。 + +| 字段 | 代表的能力 | +| --- | --- | +| 代理模式 | 启用本地代理能力。开启后 CCR 可以作为 HTTP/HTTPS 代理接收客户端流量,并通过 MITM 劫持把模型请求代理到 CCR。 | +| 系统代理 | 将系统代理指向 CCR。适合让支持系统代理的应用自动经过 CCR。 | +| 捕获网络 | 保存代理模式下经过 CCR 的网络请求,用于“网络”页面查看请求和响应详情。 | +| CA 证书 | 当前代理 CA 证书的信任状态。 | +| 安装 CA | 将 CCR 代理 CA 安装到系统或当前用户信任存储中。不同系统的安装方式不同。 | +| 检查信任 | 重新检测代理 CA 是否已被系统信任。 | +| 代理状态 | 显示代理服务当前是否运行。 | +| 重启代理 | 代理模式开启时,重新启动代理服务。 | diff --git a/docs/src/content/docs/zh/configuration/tray.md b/docs/src/content/docs/zh/configuration/tray.md new file mode 100644 index 0000000..f2bc0c1 --- /dev/null +++ b/docs/src/content/docs/zh/configuration/tray.md @@ -0,0 +1,45 @@ +--- +title: 托盘配置 +pageTitle: 托盘配置 +eyebrow: 详细配置 +lead: 配置 CCR 系统托盘图标、余额进度条和托盘窗口组件。 +--- + +## 基本概念 + +“托盘”位于“设置”里,用于配置系统托盘图标和托盘窗口。托盘窗口可以展示供应商、账户余额、token 趋势、活跃度、指标和模型占比等信息,适合在不打开主窗口时快速查看 CCR 状态。 + +## 顶部字段 + +| 字段 | 代表的能力 | +| --- | --- | +| 托盘小精灵 | 选择托盘图标样式。可选“随机”“晴岚”“暖阳”“星澜”和“余额进度条”。 | +| 余额进度条 | 使用供应商账户用量作为托盘图标进度。需要先为供应商启用“获取用量”。 | +| 账户 | 选择用于余额进度条的供应商账户。 | +| 数据 | 选择账户里的余额、套餐或额度数据作为进度来源。 | + +如果没有可用账户数据,页面会提示“暂无可用账户数据,请先为供应商启用账户监控。”这通常意味着还没有供应商开启“获取用量”,或用量读取尚未成功。 + +## 托盘窗口布局 + +| 区域 | 代表的能力 | +| --- | --- | +| 组件区 | 左侧组件库,用于添加或启用托盘窗口组件。 | +| 预览 | 中间预览区,展示当前托盘窗口布局。组件可以拖拽排序。 | +| 组件属性 | 右侧配置区,用于调整选中组件的“样式”,或移除当前组件。 | + +## 组件类型 + +| 组件 | 代表的能力 | +| --- | --- | +| 供应商组件 | 显示“供应商切换”,用于在托盘窗口中按供应商查看数据。该组件是单例组件,只能启用一次。 | +| 标题组件 | 显示“标题和状态”。该组件是单例组件,只能启用一次。 | +| 账户组件 | 显示“账户余额”。可以添加多个账户组件,并选择不同样式。 | +| 趋势组件 | 显示“Token 趋势图”。 | +| 活跃度组件 | 显示“Token 活跃度”。 | +| 指标组件 | 显示“Token 指标”。 | +| 构成组件 | 显示“Token 构成”“环形指标”或“模型占比”。 | + +## 样式 + +不同组件支持不同“样式”。常见样式包括“卡片”“紧凑”“列表”“胶囊”“折线图”“面积图”“柱状图”“圆环”“环形图”“仪表盘”“迷你折线”“堆叠”等。样式只影响托盘窗口展示方式,不改变请求路由、供应商配置或用量统计。 diff --git a/docs/src/content/docs/zh/index.md b/docs/src/content/docs/zh/index.md index 00aef30..7ea35a1 100644 --- a/docs/src/content/docs/zh/index.md +++ b/docs/src/content/docs/zh/index.md @@ -26,7 +26,7 @@ CCR 默认监听本机地址 `http://localhost:8080`。Agent 只要指向这个 | --- | --- | | [文档](./) | 产品定位、架构概览、阅读路径 | | [快速开始](guides/) | 从安装、接供应商,到接入 Agent 的上手流程 | -| [详细配置](configuration/) | 供应商、路由、配置、Fusion、Bot 和配置数据库位置 | +| [详细配置](configuration/provider/) | 概览仪表盘、API 密钥、服务、供应商、路由、Agent配置、Fusion、Bot、托盘和配置数据库位置 | | [Q&A](troubleshooting/) | 请求日志、观测面板和常见问题 | Bot 平台教程是「详细配置」分类下的子页面,每个平台有独立页面,方便逐步补齐平台后台字段、回调 URL、签名和 FAQ。 @@ -37,7 +37,7 @@ Bot 平台教程是「详细配置」分类下的子页面,每个平台有独 1. [快速开始](guides/) 覆盖供应商接入和 Agent配置。 2. App 的请求日志页面展示请求是否经过 CCR。 -3. [详细配置](configuration/) 覆盖图像、联网搜索、MCP 工具和 IM 接力。 +3. [详细配置](configuration/provider/) 覆盖概览仪表盘、API 密钥、服务、供应商、图像、联网搜索、MCP 工具、托盘和 IM 接力。 4. [Q&A](troubleshooting/) 覆盖 401、404、超时、路由不对或 Bot 收不到消息等常见问题。 这样文档不会挤在一个长页面里,后续也能按顶部分类逐步扩展。 diff --git a/docs/src/i18n/content.ts b/docs/src/i18n/content.ts index be1739b..731f868 100644 --- a/docs/src/i18n/content.ts +++ b/docs/src/i18n/content.ts @@ -15,7 +15,7 @@ export const docsContent = { navItems: [ { label: "文档", href: "/", pageKey: "documentation" }, { label: "快速开始", href: "/guides/", pageKey: "guides" }, - { label: "详细配置", href: "/configuration/", pageKey: "configuration" }, + { label: "详细配置", href: "/configuration/provider/", pageKey: "configuration" }, { label: "Q&A", href: "/troubleshooting/", pageKey: "troubleshooting" }, ], pages: { @@ -63,12 +63,16 @@ export const docsContent = { label: "详细配置", icon: "wand", items: [ + "概览仪表盘", "供应商配置", "一键导入供应商", "路由配置", "日志&观测", "Fusion 组合模型", "Agent配置", + "API 密钥", + "服务配置", + "托盘配置", "扩展机制", "Bot 与 IM 接力 Agent", "配置数据库位置", @@ -92,6 +96,7 @@ export const docsContent = { ], }, sidebarLinks: { + 概览仪表盘: "/configuration/overview/", 供应商配置: "/configuration/provider/", "一键导入供应商": "/configuration/provider-deeplink/", 路由配置: "/configuration/routing/", @@ -101,6 +106,9 @@ export const docsContent = { 内置联网搜索: "/configuration/fusion-web-search/", "自定义 MCP 工具": "/configuration/fusion-mcp-tool/", Agent配置: "/configuration/profile/", + "API 密钥": "/configuration/api-keys/", + 服务配置: "/configuration/server/", + 托盘配置: "/configuration/tray/", 扩展机制: "/configuration/extensions/", "Bot 与 IM 接力 Agent": "/configuration/bot-relay/", 配置步骤: "/configuration/bot-setup/", @@ -155,7 +163,7 @@ export const docsContent = { navItems: [ { label: "Documentation", href: "/en/", pageKey: "documentation" }, { label: "Quick Start", href: "/en/guides/", pageKey: "guides" }, - { label: "Detailed Configuration", href: "/en/configuration/", pageKey: "configuration" }, + { label: "Detailed Configuration", href: "/en/configuration/providers/", pageKey: "configuration" }, { label: "Q&A", href: "/en/troubleshooting/", pageKey: "troubleshooting" }, ], pages: { @@ -203,12 +211,16 @@ export const docsContent = { label: "Detailed Configuration", icon: "wand", items: [ + "Overview Dashboard", "Provider Config", "One click import", "Routing Config", "Logs & Observability", "Fusion Models", "Agent Config", + "API Keys", + "Server", + "Tray Configuration", "Extension Mechanism", "Bots And IM Agent Relay", "Config Database Location", @@ -222,6 +234,7 @@ export const docsContent = { "Bots And IM Agent Relay": ["Setup", "Slack", "Discord", "Telegram", "LINE", "Weixin", "WeCom", "Feishu", "DingTalk"], }, sidebarLinks: { + "Overview Dashboard": "/en/configuration/overview/", "Provider Config": "/en/configuration/providers/", "One click import": "/en/configuration/provider-deeplink/", "Routing Config": "/en/configuration/routing/", @@ -231,6 +244,9 @@ export const docsContent = { "Built-In Web Search": "/en/configuration/fusion-web-search/", "Custom MCP Tool": "/en/configuration/fusion-mcp-tool/", "Agent Config": "/en/configuration/profiles/", + "API Keys": "/en/configuration/api-keys/", + Server: "/en/configuration/server/", + "Tray Configuration": "/en/configuration/tray/", "Extension Mechanism": "/en/configuration/extensions/", "Bots And IM Agent Relay": "/en/configuration/bots/", Setup: "/en/configuration/bot-setup/", diff --git a/docs/src/pages/configuration.astro b/docs/src/pages/configuration.astro index e241173..1ad9fba 100644 --- a/docs/src/pages/configuration.astro +++ b/docs/src/pages/configuration.astro @@ -1,6 +1,17 @@ --- -import DocPage from "../components/DocPage.astro"; -import * as doc from "../content/docs/zh/configuration.md"; +const target = "/configuration/provider/"; --- - + + + + + + + + + 供应商配置 + + diff --git a/docs/src/pages/configuration/[slug].astro b/docs/src/pages/configuration/[slug].astro index 9253864..3dda58c 100644 --- a/docs/src/pages/configuration/[slug].astro +++ b/docs/src/pages/configuration/[slug].astro @@ -4,6 +4,8 @@ import { configurationSlugFromPath, zhConfigurationDocs } from "../../configurat export function getStaticPaths() { const activeLabels: Record = { + "api-keys": "API 密钥", + overview: "概览仪表盘", provider: "供应商配置", "provider-deeplink": "一键导入供应商", routing: "路由配置", @@ -14,6 +16,8 @@ export function getStaticPaths() { "fusion-web-search": "内置联网搜索", "fusion-mcp-tool": "自定义 MCP 工具", extensions: "扩展机制", + server: "服务配置", + tray: "托盘配置", "bot-relay": "Bot 与 IM 接力 Agent", "bot-setup": "配置步骤", "config-file": "配置数据库位置", diff --git a/docs/src/pages/en/configuration.astro b/docs/src/pages/en/configuration.astro index c2ce75b..01088a9 100644 --- a/docs/src/pages/en/configuration.astro +++ b/docs/src/pages/en/configuration.astro @@ -1,6 +1,17 @@ --- -import DocPage from "../../components/DocPage.astro"; -import * as doc from "../../content/docs/en/configuration.md"; +const target = "/en/configuration/providers/"; --- - + + + + + + + + +
Provider Config + + diff --git a/docs/src/pages/en/configuration/[slug].astro b/docs/src/pages/en/configuration/[slug].astro index 4cd9324..cc2fc90 100644 --- a/docs/src/pages/en/configuration/[slug].astro +++ b/docs/src/pages/en/configuration/[slug].astro @@ -4,6 +4,8 @@ import { configurationSlugFromPath, enConfigurationDocs } from "../../../configu export function getStaticPaths() { const activeLabels: Record = { + "api-keys": "API Keys", + overview: "Overview Dashboard", providers: "Provider Config", "provider-deeplink": "One click import", routing: "Routing Config", @@ -14,6 +16,8 @@ export function getStaticPaths() { "fusion-web-search": "Built-In Web Search", "fusion-mcp-tool": "Custom MCP Tool", extensions: "Extension Mechanism", + server: "Server", + tray: "Tray Configuration", bots: "Bots And IM Agent Relay", "bot-setup": "Setup", "configuration-file": "Config Database Location", diff --git a/src/renderer/pages/home/components/providers.tsx b/src/renderer/pages/home/components/providers.tsx index fa2ee33..200701e 100644 --- a/src/renderer/pages/home/components/providers.tsx +++ b/src/renderer/pages/home/components/providers.tsx @@ -1364,7 +1364,7 @@ export function AddProviderForm({
)} - + onChange({ name: event.target.value })} /> {showBaseUrl ? ( diff --git a/src/renderer/pages/home/components/virtual-models.tsx b/src/renderer/pages/home/components/virtual-models.tsx index ee75dff..453a957 100644 --- a/src/renderer/pages/home/components/virtual-models.tsx +++ b/src/renderer/pages/home/components/virtual-models.tsx @@ -3,11 +3,11 @@ import { Card, CardContent, CardHeader, CardTitle, Check, ChevronDown, ChevronRight, cn, createMcpServerDraftFromConfig, createRouteModelOptions, defaultFusionWebSearchProvider, Dialog, DialogBody, DialogContent, DialogFooter, DialogHeader, DialogTitle, ExtensionInstallDraft, Field, FolderOpen, formatPluginDependencies, - createFusionWebSearchEnvRows, customFusionToolName, fusionToolExecutionFlags, fusionToolOptions, - fusionWebSearchProviderOptions, GatewayMcpServerConfig, GatewayMcpToolInfo, GatewayProviderConfig, Input, KeyValueRowsControl, LoaderCircle, + createFusionWebSearchEnvRows, customFusionToolName, fusionToolExecutionFlagsFromTools, fusionToolOptions, + fusionWebSearchProviderOptions, GatewayMcpServerConfig, GatewayMcpToolInfo, GatewayProviderConfig, Input, isBuiltInFusionToolName, isFusionVisionToolName, isFusionWebSearchToolName, KeyValueRowsControl, LoaderCircle, mcpServerConfigFromDraft, mcpServerEndpointSummary, mcpServerTransportOptions, mcpStdioMessageModeOptions, motion, normalizeFusionToolName, Pencil, - PluginMarketplaceEntry, Plus, PopoverContent, RouteTargetControl, Search, selectedFusionToolName, + PluginMarketplaceEntry, Plus, PopoverContent, RouteTargetControl, Search, selectedFusionToolNames, SelectControl, Toggle, Trash2, translateOptions, useAppErrorText, useAppText, useEffect, useLayoutEffect, useMemo, useRef, useState, validateMcpServerDraft, virtualModelBaseModelSummary, VirtualModelDraft, virtualModelMatchesQuery, virtualModelMatchSummary, VirtualModelProfileConfig, virtualModelToolSummary, X @@ -16,6 +16,25 @@ import { const virtualModelTableGridClass = "grid-cols-[minmax(180px,0.9fr)_minmax(220px,1.1fr)_minmax(220px,1.1fr)_minmax(170px,0.85fr)_112px_96px]"; const virtualModelTableMinWidthClass = "min-w-[1100px]"; +function uniqueFusionTools(tools: string[]): string[] { + const seen = new Set(); + const values: string[] = []; + for (const tool of tools) { + const normalized = normalizeFusionToolName(tool); + if (!normalized || seen.has(normalized)) { + continue; + } + values.push(normalized); + seen.add(normalized); + } + return values; +} + +function visibleFusionToolOption(toolName: string, currentValue: string, excludedValues: Set): boolean { + const normalized = normalizeFusionToolName(toolName); + return Boolean(normalized) && (normalized === currentValue || !excludedValues.has(normalized)); +} + export function VirtualModelsView({ addVirtualModel, editVirtualModel, @@ -157,11 +176,11 @@ export function VirtualModelDialog({ const t = useAppText(); const formatError = useAppErrorText(); const modelOptions = useMemo(() => createRouteModelOptions(providers), [providers]); - const selectedTool = selectedFusionToolName(draft.toolsText); - const selectedToolFlags = fusionToolExecutionFlags(selectedTool); + const selectedTools = selectedFusionToolNames(draft.toolsText); const [customMcpDialogOpen, setCustomMcpDialogOpen] = useState(false); const [customMcpDialogDraft, setCustomMcpDialogDraft] = useState(draft.customMcpServer); const [customMcpDialogError, setCustomMcpDialogError] = useState(""); + const [addingFusionTool, setAddingFusionTool] = useState(false); const [mcpToolStateByServer, setMcpToolStateByServer] = useState isBuiltInFusionToolName(tool) || tool === selectedCustomTool) + : nextTools + ); + const flags = fusionToolExecutionFlagsFromTools(normalizedTools); + const customTool = normalizedTools.find((tool) => !isBuiltInFusionToolName(tool)); onChange({ ...(flags.matchWebSearch && draft.webSearchEnvRows.length === 0 ? { webSearchEnvRows: createFusionWebSearchEnvRows(draft.webSearchProvider) } : {}), - ...(!flags.matchMultimodal && !flags.matchWebSearch ? { + ...(customTool ? { ...(server ? { customMcpServer: createMcpServerDraftFromConfig(server) } : {}), - customToolName: nextTool || draft.customToolName || customFusionToolName + customToolName: customTool || draft.customToolName || customFusionToolName } : {}), - toolsText: nextTool, + toolsText: normalizedTools.join(", "), ...flags }); } + function appendFusionTool(toolName: string, server?: GatewayMcpServerConfig) { + const nextTool = normalizeFusionToolName(toolName); + if (!nextTool) { + return; + } + applyFusionTools([...selectedFusionToolNames(draft.toolsText), nextTool], server, server ? nextTool : undefined); + setAddingFusionTool(false); + } + + function updateFusionTool(index: number, toolName: string, server?: GatewayMcpServerConfig) { + const nextTool = normalizeFusionToolName(toolName); + if (!nextTool) { + return; + } + const currentTools = selectedFusionToolNames(draft.toolsText); + const nextTools = currentTools.map((tool, toolIndex) => toolIndex === index ? nextTool : tool); + applyFusionTools(nextTools, server, server ? nextTool : undefined); + } + + function removeFusionTool(index: number) { + applyFusionTools(selectedFusionToolNames(draft.toolsText).filter((_, toolIndex) => toolIndex !== index)); + } + function openCustomMcpDialog() { setCustomMcpDialogDraft(draft.customMcpServer); setCustomMcpDialogError(""); @@ -289,12 +337,9 @@ export function VirtualModelDialog({ const tools = await discoverMcpServerTools(server, true); const nextTool = normalizeFusionToolName(tools[0]?.name || ""); if (nextTool) { - onChange({ - customMcpServer: createMcpServerDraftFromConfig(server), - customToolName: nextTool, - toolsText: nextTool, - ...fusionToolExecutionFlags(nextTool) - }); + const currentTools = selectedFusionToolNames(draft.toolsText); + applyFusionTools(currentTools.includes(nextTool) ? currentTools : [...currentTools, nextTool], server, nextTool); + setAddingFusionTool(false); } } @@ -322,11 +367,18 @@ export function VirtualModelDialog({
+
- setAddingFusionTool(true)} + onAppendTool={appendFusionTool} + onCancelAddTool={() => setAddingFusionTool(false)} + onChange={onChange} + onChangeTool={updateFusionTool} onDiscoverMcpTools={(server, force) => { if (server) { void discoverMcpServerTools(server, force); @@ -334,28 +386,13 @@ export function VirtualModelDialog({ } discoverVisibleMcpServers(); }} + onRemoveTool={removeFusionTool} selectedMcpServerName={draft.customMcpServer.name} - value={selectedTool} + values={selectedTools} />
- {selectedToolFlags.matchMultimodal ? ( -
-
-
{t("Vision tool configuration")}
-
{t("Choose a configured gateway model for image understanding.")}
-
- - onChange({ visionModel })} value={draft.visionModel} /> - -
- ) : null} - - {selectedToolFlags.matchWebSearch ? ( - - ) : null} - {error ? (
{t(error)}
) : null} @@ -412,9 +449,6 @@ function WebSearchToolConfigurationPanel({ return (
-
-
{t("Web search configuration")}
-
@@ -549,7 +583,166 @@ function CustomMcpToolDialog({ ); } +function FusionToolConfigurationPanel({ + draft, + modelOptions, + onChange, + toolName +}: { + draft: VirtualModelDraft; + modelOptions: ReturnType; + onChange: (patch: Partial) => void; + toolName: string; +}) { + if (isFusionVisionToolName(toolName)) { + return ; + } + if (isFusionWebSearchToolName(toolName)) { + return ; + } + return null; +} + +function VisionToolConfigurationPanel({ + draft, + modelOptions, + onChange +}: { + draft: VirtualModelDraft; + modelOptions: ReturnType; + onChange: (patch: Partial) => void; +}) { + const t = useAppText(); + + return ( +
+ + onChange({ visionModel })} value={draft.visionModel} /> + +
+ ); +} + +function FusionToolsListControl({ + adding, + draft, + mcpServers, + mcpToolStateByServer, + modelOptions, + onAddCustomMcpTool, + onAddTool, + onAppendTool, + onCancelAddTool, + onChange, + onChangeTool, + onDiscoverMcpTools, + onRemoveTool, + selectedMcpServerName, + values +}: { + adding: boolean; + draft: VirtualModelDraft; + mcpServers: GatewayMcpServerConfig[]; + mcpToolStateByServer: Record; + modelOptions: ReturnType; + onAddCustomMcpTool: () => void; + onAddTool: () => void; + onAppendTool: (value: string, server?: GatewayMcpServerConfig) => void; + onCancelAddTool: () => void; + onChange: (patch: Partial) => void; + onChangeTool: (index: number, value: string, server?: GatewayMcpServerConfig) => void; + onDiscoverMcpTools: (server?: GatewayMcpServerConfig, force?: boolean) => void; + onRemoveTool: (index: number) => void; + selectedMcpServerName: string; + values: string[]; +}) { + const t = useAppText(); + const selectedToolValues = values.map(normalizeFusionToolName).filter(Boolean); + + return ( +
+ {values.map((value, index) => ( +
+
+
+ toolIndex !== index)} + mcpServers={mcpServers} + mcpToolStateByServer={mcpToolStateByServer} + onAddCustomMcpTool={onAddCustomMcpTool} + onChange={(toolName, server) => onChangeTool(index, toolName, server)} + onDiscoverMcpTools={onDiscoverMcpTools} + selectedMcpServerName={selectedMcpServerName} + value={value} + /> +
+ +
+ +
+ ))} + + {adding ? ( +
+
+ +
+ +
+ ) : null} + + +
+ ); +} + function FusionToolSelectControl({ + excludedValues, mcpServers, mcpToolStateByServer, onAddCustomMcpTool, @@ -558,6 +751,7 @@ function FusionToolSelectControl({ selectedMcpServerName, value }: { + excludedValues?: string[]; mcpServers: GatewayMcpServerConfig[]; mcpToolStateByServer: Record(); const rootRef = useRef(null); const normalizedValue = normalizeFusionToolName(value); + const excludedValueSet = new Set((excludedValues ?? []).map(normalizeFusionToolName).filter(Boolean)); const selected = fusionToolOptions.find((option) => option.value === normalizedValue); const selectedServer = selectedMcpServerName ? mcpServers.find((server) => server.name === selectedMcpServerName) @@ -703,7 +898,7 @@ function FusionToolSelectControl({ role="listbox" style={{ maxHeight: `${popoverLayout?.maxHeight ?? 360}px` }} > - {fusionToolOptions.map((option) => { + {fusionToolOptions.filter((option) => visibleFusionToolOption(option.value, normalizedValue, excludedValueSet)).map((option) => { const selectedOption = option.value === selected?.value; return ( -
- -
- - - +
+
+ {title} + {subtitle ? {subtitle} : null} + +
+ +
+ + + +
); diff --git a/src/renderer/pages/home/components/providers.tsx b/src/renderer/pages/home/components/providers.tsx index 200701e..bde3e90 100644 --- a/src/renderer/pages/home/components/providers.tsx +++ b/src/renderer/pages/home/components/providers.tsx @@ -250,8 +250,22 @@ export function ProvidersView({ accountSnapshots, addProvider, editProvider, not ); } -export function ModelsView({ config }: { config: AppConfig }) { +export function ModelsView({ + config, + updateModelDescription +}: { + config: AppConfig; + updateModelDescription: (providerIndex: number, model: string, description: string) => void; +}) { const t = useAppText(); + const [descriptionDraft, setDescriptionDraft] = useState(""); + const [descriptionTarget, setDescriptionTarget] = useState<{ + description: string; + displayName?: string; + model: string; + providerIndex: number; + providerName?: string; + }>(); const [query, setQuery] = useState(""); const rows = useMemo(() => createModelCatalogItems(config), [config]); const normalizedQuery = query.trim().toLowerCase(); @@ -260,6 +274,34 @@ export function ModelsView({ config }: { config: AppConfig }) { [normalizedQuery, rows] ); + function openDescriptionDialog(row: (typeof rows)[number]) { + if (row.providerIndex === undefined) { + return; + } + const description = row.description ?? ""; + setDescriptionDraft(description); + setDescriptionTarget({ + description, + displayName: row.displayName, + model: row.model, + providerIndex: row.providerIndex, + providerName: row.providerName + }); + } + + function closeDescriptionDialog() { + setDescriptionDraft(""); + setDescriptionTarget(undefined); + } + + function saveDescriptionDialog() { + if (!descriptionTarget) { + return; + } + updateModelDescription(descriptionTarget.providerIndex, descriptionTarget.model, descriptionDraft); + closeDescriptionDialog(); + } + return ( 0 ? (
-
-
+
+
{t("Model")}
+
{t("Description")}
{visibleRows.map((row) => (
{row.displayName ?? row.model}
+
+ {row.providerName ? `${row.providerName}/${row.model}` : row.model} +
+ {row.providerName ? ( + + {row.providerName} + + ) : null} +
+
+ {row.providerIndex !== undefined ? ( +
+
+
+ {row.description || "-"} +
+
+ +
+ ) : ( +
+ {row.description || "-"} +
+ )}
))} @@ -322,10 +398,74 @@ export function ModelsView({ config }: { config: AppConfig }) { ) : null} + ); } +function ModelCatalogDescriptionDialog({ + draft, + onChange, + onClose, + onSave, + target +}: { + draft: string; + onChange: (value: string) => void; + onClose: () => void; + onSave: () => void; + target?: { + description: string; + displayName?: string; + model: string; + providerName?: string; + }; +}) { + const t = useAppText(); + const open = Boolean(target); + const title = target?.displayName || target?.model || t("Model"); + const subtitle = target?.providerName ? `${target.providerName}/${target.model}` : target?.model; + + return ( + { if (!nextOpen) onClose(); }}> + + + {t("Edit description")} + + +
+
{title}
+ {subtitle ?
{subtitle}
: null} +
+ + + + +
+ + + + diff --git a/extension/chrome/popup.js b/extension/chrome/popup.js new file mode 100644 index 0000000..4b12be1 --- /dev/null +++ b/extension/chrome/popup.js @@ -0,0 +1,297 @@ +const importUrlInput = document.getElementById("import-url"); +const importButton = document.getElementById("import-button"); +const statusElement = document.getElementById("status"); + +document.addEventListener("DOMContentLoaded", () => { + void restoreLastImportUrl(); +}); + +importButton.addEventListener("click", () => { + void runImport(); +}); + +async function restoreLastImportUrl() { + const stored = await chrome.storage.local.get(["lastImportUrl"]); + if (typeof stored.lastImportUrl === "string") { + importUrlInput.value = stored.lastImportUrl; + } +} + +async function runImport() { + const importUrl = normalizeImportUrl(importUrlInput.value); + if (!importUrl) { + setStatus("Paste the import URL copied from CCR.", "error"); + return; + } + + importButton.disabled = true; + try { + await chrome.storage.local.set({ lastImportUrl: importUrl }); + setStatus("Reading CCR import job..."); + const job = await fetchImportJob(importUrl); + const domains = Array.isArray(job.domains) ? job.domains.map(normalizeDomain).filter(Boolean) : []; + if (domains.length === 0) { + throw new Error("CCR import job does not include any domains."); + } + + await ensureHostPermissions(domains); + + setStatus(`Reading cookies for ${domains.join(", ")}...`); + const cookies = await readCookiesForDomains(domains); + setStatus(`Reading localStorage for ${domains.join(", ")}...`); + const localStorageEntries = await readLocalStorageForDomains(domains, cookies); + + if (cookies.length === 0 && localStorageEntries.length === 0) { + throw new Error("No cookies or localStorage entries were found for the selected domains."); + } + + setStatus(`Sending ${cookies.length} cookies and ${localStorageItemCount(localStorageEntries)} localStorage items to CCR...`); + const result = await submitCookies(importUrl, cookies, localStorageEntries, domains); + setStatus( + `Imported ${result.cookieImported ?? 0} cookies and ${result.localStorageImported ?? 0} localStorage items. Skipped ${result.skipped ?? 0}.`, + "ok" + ); + } catch (error) { + setStatus(formatError(error), "error"); + } finally { + importButton.disabled = false; + } +} + +async function fetchImportJob(importUrl) { + const response = await fetch(importUrl, { + headers: { + "x-ccr-login-import": "chrome-extension" + } + }); + const body = await response.json().catch(() => ({})); + if (!response.ok) { + throw new Error(body?.error?.message || `CCR import job request failed (${response.status}).`); + } + if (!body.job || body.job.status !== "pending") { + throw new Error(`CCR import job is ${body.job?.status || "unavailable"}.`); + } + return body.job; +} + +async function submitCookies(importUrl, cookies, localStorage, domains) { + const response = await fetch(`${importUrl.replace(/\/+$/, "")}/cookies`, { + body: JSON.stringify({ + cookies, + domains, + localStorage, + source: { + browser: "chrome", + extension: chrome.runtime.getManifest().version + } + }), + headers: { + "content-type": "application/json", + "x-ccr-login-import": "chrome-extension" + }, + method: "POST" + }); + const body = await response.json().catch(() => ({})); + if (!response.ok) { + throw new Error(body?.error?.message || `CCR cookie import failed (${response.status}).`); + } + return body.result || {}; +} + +async function readCookiesForDomains(domains) { + const cookies = []; + for (const domain of domains) { + cookies.push(...await chrome.cookies.getAll({ domain })); + } + return dedupeCookies(cookies); +} + +async function readLocalStorageForDomains(domains, cookies) { + const origins = localStorageOriginsForDomains(domains, cookies); + const entries = []; + for (const origin of origins) { + const entry = await readLocalStorageForOrigin(origin).catch(() => undefined); + if (entry && Object.keys(entry.items).length > 0) { + entries.push(entry); + } + } + return entries; +} + +async function readLocalStorageForOrigin(origin) { + const tab = await chrome.tabs.create({ + active: false, + url: `${origin}/` + }); + try { + await waitForTabLoad(tab.id, 15000); + const [frameResult] = await chrome.scripting.executeScript({ + func: () => { + const items = {}; + for (let index = 0; index < window.localStorage.length; index += 1) { + const key = window.localStorage.key(index); + if (key !== null) { + items[key] = window.localStorage.getItem(key) || ""; + } + } + return { + items, + origin: window.location.origin + }; + }, + target: { tabId: tab.id }, + world: "MAIN" + }); + const result = frameResult?.result; + if (!result || !allowedLocalStorageOrigin(result.origin, origin)) { + return undefined; + } + return result; + } finally { + if (tab.id !== undefined) { + await chrome.tabs.remove(tab.id).catch(() => undefined); + } + } +} + +function waitForTabLoad(tabId, timeoutMs) { + return new Promise((resolve) => { + let done = false; + const finish = () => { + if (done) { + return; + } + done = true; + chrome.tabs.onUpdated.removeListener(listener); + window.clearTimeout(timeout); + resolve(); + }; + const listener = (updatedTabId, changeInfo) => { + if (updatedTabId === tabId && changeInfo.status === "complete") { + finish(); + } + }; + const timeout = window.setTimeout(finish, timeoutMs); + chrome.tabs.onUpdated.addListener(listener); + }); +} + +function dedupeCookies(cookies) { + const seen = new Set(); + const result = []; + for (const cookie of cookies) { + const partitionKey = cookie.partitionKey ? JSON.stringify(cookie.partitionKey) : ""; + const key = `${cookie.storeId || ""}\n${cookie.domain}\n${cookie.path}\n${cookie.name}\n${partitionKey}`; + if (!seen.has(key)) { + seen.add(key); + result.push(cookie); + } + } + return result; +} + +function localStorageOriginsForDomains(domains, cookies) { + const origins = new Set(); + for (const domain of domains) { + if (domain === "localhost" || isIpAddress(domain)) { + origins.add(`http://${domain}`); + origins.add(`https://${domain}`); + } else { + origins.add(`https://${domain}`); + } + } + + for (const cookie of cookies) { + const host = normalizeDomain(cookie.domain); + if (!host || !cookie.hostOnly || !domains.some((domain) => host === domain || host.endsWith(`.${domain}`))) { + continue; + } + origins.add(`${cookie.secure === false ? "http" : "https"}://${host}`); + } + + return [...origins]; +} + +function allowedLocalStorageOrigin(actualOrigin, requestedOrigin) { + try { + const actual = new URL(actualOrigin); + const requested = new URL(requestedOrigin); + return actual.protocol === requested.protocol && actual.hostname === requested.hostname && actual.port === requested.port; + } catch { + return false; + } +} + +function localStorageItemCount(entries) { + return entries.reduce((count, entry) => count + Object.keys(entry.items || {}).length, 0); +} + +function originsForDomains(domains) { + return [...new Set(domains.flatMap((domain) => { + if (domain === "localhost" || isIpAddress(domain)) { + return [ + `http://${domain}/*`, + `https://${domain}/*` + ]; + } + return [ + `http://${domain}/*`, + `https://${domain}/*`, + `http://*.${domain}/*`, + `https://*.${domain}/*` + ]; + }))]; +} + +async function ensureHostPermissions(domains) { + const origins = originsForDomains(domains); + const granted = await chrome.permissions.contains({ origins }); + if (granted) { + return; + } + throw new Error( + [ + `CCR Login Import does not have Chrome site access for ${domains.join(", ")}.`, + "Reload the unpacked extension after updating it, then grant the extension site access for the requested domains in Chrome extensions settings." + ].join(" ") + ); +} + +function normalizeImportUrl(value) { + const raw = value.trim(); + if (!raw) { + return ""; + } + try { + const url = new URL(raw); + if (!["127.0.0.1", "localhost"].includes(url.hostname)) { + return ""; + } + if (!/^\/chrome-import\/jobs\/[^/]+\/?$/.test(url.pathname)) { + return ""; + } + url.pathname = url.pathname.replace(/\/+$/, ""); + return url.toString(); + } catch { + return ""; + } +} + +function normalizeDomain(value) { + return typeof value === "string" + ? value.trim().replace(/^\*\./, "").replace(/^\./, "").toLowerCase() + : ""; +} + +function isIpAddress(value) { + return /^\d{1,3}(?:\.\d{1,3}){3}$/.test(value) || value.includes(":"); +} + +function setStatus(message, kind = "") { + statusElement.textContent = message; + statusElement.className = `status ${kind}`.trim(); +} + +function formatError(error) { + return error instanceof Error ? error.message : String(error); +} diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts index 5e4e4bc..0cf78ab 100644 --- a/packages/core/src/config/config.ts +++ b/packages/core/src/config/config.ts @@ -700,6 +700,10 @@ function parseToolHub(value: unknown): Partial | undefined { if (typeof value.enabled === "boolean") { toolHub.enabled = value.enabled; } + const browserAutomation = value.browserAutomation ?? value.browser_automation; + if (typeof browserAutomation === "boolean") { + toolHub.browserAutomation = browserAutomation; + } const maxTools = readNumber(value.maxTools ?? value.max_tools); if (maxTools !== undefined) { toolHub.maxTools = clampNumber(maxTools, 1, 20); diff --git a/packages/core/src/config/default-config.ts b/packages/core/src/config/default-config.ts index 63933cb..58a3868 100644 --- a/packages/core/src/config/default-config.ts +++ b/packages/core/src/config/default-config.ts @@ -170,6 +170,7 @@ export function createDefaultAppConfig(options: DefaultAppConfigOptions): AppCon trayWidgets: DEFAULT_TRAY_WIDGETS, trayWindowModules: DEFAULT_TRAY_WINDOW_MODULES, toolHub: { + browserAutomation: false, enabled: false, llm: { apiKey: "", diff --git a/packages/core/src/contracts/app.ts b/packages/core/src/contracts/app.ts index 4c00d21..685a35e 100644 --- a/packages/core/src/contracts/app.ts +++ b/packages/core/src/contracts/app.ts @@ -642,6 +642,7 @@ export type ToolHubLlmConfig = { }; export type ToolHubConfig = { + browserAutomation: boolean; enabled: boolean; llm: ToolHubLlmConfig; mcpServers: GatewayMcpServerConfig[]; @@ -1468,12 +1469,91 @@ export type BuiltInBrowserTabState = { url: string; }; +export type BuiltInBrowserAutomationHandoffKind = + | "blocked" + | "human_verification" + | "login_required" + | "other" + | "verification_code"; + +export type BuiltInBrowserAutomationHandoff = { + id: string; + kind: BuiltInBrowserAutomationHandoffKind; + message: string; + reason?: string; + requestedAt: number; + sessionId?: string; + status: "pending"; + tabId?: string; +}; + export type BuiltInBrowserState = { activeTabId?: string; apps: InstalledBrowserApp[]; + automationHandoff?: BuiltInBrowserAutomationHandoff; tabs: BuiltInBrowserTabState[]; }; +export type ChromeLoginImportTarget = "browser" | "browser-and-web-search"; + +export type ChromeLoginImportStatus = + | "completed" + | "expired" + | "failed" + | "pending"; + +export type ChromeLoginImportRequest = { + domains: string[]; + openConfirmationPage?: boolean; + target?: ChromeLoginImportTarget; +}; + +export type ChromeLoginImportResult = { + completedAt: number; + cookieImported: number; + cookieSkipped: number; + domains: string[]; + errors?: string[]; + imported: number; + localStorageImported: number; + localStorageSkipped: number; + partitions: string[]; + skipped: number; +}; + +export type ChromeLoginImportJob = { + confirmUrl: string; + createdAt: number; + domains: string[]; + endpointUrl: string; + expiresAt: number; + id: string; + importUrl: string; + result?: ChromeLoginImportResult; + status: ChromeLoginImportStatus; + target: ChromeLoginImportTarget; +}; + +export type ChromeLoginImportCookie = { + domain: string; + expirationDate?: number; + hostOnly?: boolean; + httpOnly?: boolean; + name: string; + partitionKey?: unknown; + path?: string; + sameSite?: "lax" | "no_restriction" | "strict" | "unspecified"; + secure?: boolean; + session?: boolean; + storeId?: string; + value: string; +}; + +export type ChromeLoginImportLocalStorage = { + items: Record; + origin: string; +}; + export type ProxyCertificateInstallResult = { caCertFile: string; manualCommand?: string; diff --git a/packages/core/src/contracts/ipc-channels.ts b/packages/core/src/contracts/ipc-channels.ts index 103b0b0..f24d040 100644 --- a/packages/core/src/contracts/ipc-channels.ts +++ b/packages/core/src/contracts/ipc-channels.ts @@ -73,10 +73,13 @@ export const IPC_CHANNELS = { browserBack: "ccr:browser:back", browserCloseTab: "ccr:browser:close-tab", browserForward: "ccr:browser:forward", + browserGetChromeLoginImport: "ccr:browser:get-chrome-login-import", browserGetState: "ccr:browser:get-state", browserNavigate: "ccr:browser:navigate", browserNewTab: "ccr:browser:new-tab", browserReload: "ccr:browser:reload", + browserResolveAutomationHandoff: "ccr:browser:resolve-automation-handoff", browserSelectTab: "ccr:browser:select-tab", + browserStartChromeLoginImport: "ccr:browser:start-chrome-login-import", browserStateChanged: "ccr:browser:state-changed" } as const; diff --git a/packages/core/src/gateway/claude-code-router-plugin.ts b/packages/core/src/gateway/claude-code-router-plugin.ts index b42ea83..c39b3f9 100644 --- a/packages/core/src/gateway/claude-code-router-plugin.ts +++ b/packages/core/src/gateway/claude-code-router-plugin.ts @@ -365,9 +365,11 @@ function injectClaudeCodeToolHubInstructions(body: Record, conf const invokeName = toolNames.invoke ?? "tool_hub.invoke"; appendSystemInstruction(body, [ ccrToolHubSystemInstructionMarker, - `You MUST call ${toolNames.resolve} before answering any request that asks about external services, installed MCP capabilities, business APIs, orders, coupons, stores, accounts, available tools, or capabilities that are not already obvious from the eager tools.`, - `Do this even if the user did not mention ToolHub or ${toolNames.resolve}. Only skip ${toolNames.resolve} when the request is clearly local code/file/shell work or simple conversation that does not need an external or MCP capability.`, - `If ${toolNames.resolve} returns selected tools, use ${invokeName} to call the selected tool instead of telling the user that no such capability is available.`, + `The ToolHub search/resolution tool is ${toolNames.resolve}; call this actual tool, do not merely mention its name in text.`, + `You MUST call the ToolHub search/resolution tool ${toolNames.resolve} before answering any request that asks about external services, installed MCP capabilities, business APIs, orders, coupons, stores, accounts, available tools, or capabilities that are not already obvious from the eager tools.`, + `Do this even if the user did not mention ToolHub or ${toolNames.resolve}. Only skip the ToolHub search/resolution tool when the request is clearly local code/file/shell work or simple conversation that does not need an external or MCP capability.`, + `If ${toolNames.resolve} returns selected tools, call the ToolHub invocation tool ${invokeName} to run the selected tool instead of telling the user that no such capability is available.`, + "When the ToolHub resolve result includes executionPlanJs or workflowSketch, treat that JavaScript as the invocation dependency plan: await means serial order, and only callTool calls grouped inside the same Promise.all may be issued in parallel.", "Use the user's request as the task and include concise context when resolving. Do not ask the user to name the MCP tool unless the task is genuinely ambiguous after resolution." ].join("\n")); } @@ -380,16 +382,40 @@ function claudeCodeToolHubToolNames(tools: unknown[]): { invoke?: string; resolv } const name = claudeCodeToolName(tool); const normalized = normalizeClaudeCodeToolName(name); - if (!names.resolve && normalized.endsWith("toolhubresolve")) { + if (normalized.endsWith("toolhubresolve") && shouldUseClaudeCodeToolHubName(names.resolve, name)) { names.resolve = name; } - if (!names.invoke && normalized.endsWith("toolhubinvoke")) { + if (normalized.endsWith("toolhubinvoke") && shouldUseClaudeCodeToolHubName(names.invoke, name)) { names.invoke = name; } } return names; } +function shouldUseClaudeCodeToolHubName(current: string | undefined, candidate: string | undefined): boolean { + if (!candidate) { + return false; + } + if (!current) { + return true; + } + return claudeCodeToolHubNameScore(candidate) > claudeCodeToolHubNameScore(current); +} + +function claudeCodeToolHubNameScore(name: string): number { + const normalized = name.toLowerCase(); + if (normalized.startsWith("mcp__ccr-toolhub__") || normalized.startsWith("mcp__ccr_toolhub__")) { + return 3; + } + if (normalized.startsWith("mcp__") && normalized.includes("toolhub")) { + return 2; + } + if (normalized.startsWith("mcp__")) { + return 1; + } + return 0; +} + function appendSystemInstruction(body: Record, instruction: string): void { if (systemContainsInstruction(body.system, ccrToolHubSystemInstructionMarker)) { return; diff --git a/packages/core/src/gateway/service.ts b/packages/core/src/gateway/service.ts index a10ff09..ef274df 100644 --- a/packages/core/src/gateway/service.ts +++ b/packages/core/src/gateway/service.ts @@ -45,7 +45,7 @@ import { loadPersistedApiKeys } from "@ccr/core/config/api-key-store"; import { codexDefaultBaseUrl, readCodexAuth } from "@ccr/core/agents/local-providers/service"; import { fetchWithSystemProxy, getSystemProxyUrlForProtocol } from "@ccr/core/proxy/system-proxy-fetch"; import { handleNetworkCaptureMcpRequest, isNetworkCaptureMcpPath } from "@ccr/core/mcp/network-capture-mcp"; -import { TOOL_HUB_MCP_SERVER_NAME, toolHubMcpRuntimeConfig } from "@ccr/core/mcp/toolhub-config"; +import { BROWSER_AUTOMATION_MCP_PATH, TOOL_HUB_MCP_SERVER_NAME, browserAutomationMcpEnabled, toolHubBuiltInBackendServers, toolHubMcpRuntimeConfig, toolHubRequestTimeoutMs } from "@ccr/core/mcp/toolhub-config"; import { pluginService } from "@ccr/core/plugins/service"; import { proxyService } from "@ccr/core/proxy/service"; import { createSseErrorDetector, recordGatewayRequestLog, updateGatewayRequestLogFromRawTrace, type RequestLogRawTraceUpdateInput } from "@ccr/core/observability/request-log-store"; @@ -130,6 +130,7 @@ export type BrowserWebSearchMcpRegistration = { export type BrowserWebSearchProtocolResult = { content?: string; + diagnostics?: string[]; snippet?: string; title: string; url: string; @@ -151,6 +152,11 @@ export type BrowserWebSearchMcpIntegration = { stopBrowserWebSearchMcpServers: () => Promise; }; +export type BrowserAutomationMcpIntegration = { + handleBrowserAutomationMcpRequest: (request: IncomingMessage, response: ServerResponse) => Promise; + stopBrowserAutomationMcpServer: () => Promise; +}; + type CoreGatewayHealth = { runtimeId?: string; status?: string; @@ -312,6 +318,7 @@ const persistedApiKeyCacheTtlMs = 1000; let persistedApiKeyCache: { loadedAt: number; values: ApiKeyConfig[] } | undefined; class GatewayService { + private browserAutomationMcpIntegration?: BrowserAutomationMcpIntegration; private browserWebSearchMcpIntegration?: BrowserWebSearchMcpIntegration; private child?: ChildProcess; private config?: AppConfig; @@ -332,6 +339,10 @@ class GatewayService { this.browserWebSearchMcpIntegration = integration; } + setBrowserAutomationMcpIntegration(integration: BrowserAutomationMcpIntegration): void { + this.browserAutomationMcpIntegration = integration; + } + async start(config: AppConfig): Promise { const coreHostError = loopbackCoreHostError(config.gateway.coreHost); if (coreHostError) { @@ -439,6 +450,9 @@ class GatewayService { await this.browserWebSearchMcpIntegration?.stopBrowserWebSearchMcpServers().catch((error) => { console.warn(`[gateway] Failed to stop browser web search MCP: ${formatError(error)}`); }); + await this.browserAutomationMcpIntegration?.stopBrowserAutomationMcpServer().catch((error) => { + console.warn(`[gateway] Failed to stop browser automation MCP: ${formatError(error)}`); + }); this.status = { ...this.status, @@ -551,6 +565,31 @@ class GatewayService { return; } + if (path === BROWSER_AUTOMATION_MCP_PATH || path === `${BROWSER_AUTOMATION_MCP_PATH}/`) { + if (!browserAutomationMcpEnabled(this.config)) { + sendJson(response, 404, { + error: { + message: "CCR browser automation MCP is disabled." + } + }); + return; + } + const authorization = await authorize(request, response, this.config); + if (!authorization.ok) { + return; + } + if (!this.browserAutomationMcpIntegration) { + sendJson(response, 503, { + error: { + message: "CCR browser automation MCP is only available in the Electron desktop app." + } + }); + return; + } + await this.browserAutomationMcpIntegration.handleBrowserAutomationMcpRequest(request, response); + return; + } + if (isNetworkCaptureMcpPath(path)) { if (!this.config.proxy.captureNetwork) { sendJson(response, 404, { error: { message: "Network capture MCP is disabled." } }); @@ -819,6 +858,15 @@ class GatewayService { sinceMs: startedAt - 1_000 }); + if (hostedWebSearchProtocolContext && !this.browserWebSearchMcpIntegration) { + const message = browserWebSearchUnavailableMessage(hostedWebSearchProtocolContext.toolName); + const responseHeaders = new Headers({ "content-type": "application/json; charset=utf-8" }); + const responseBody = JSON.stringify({ error: { message } }); + writeRequestLog(503, responseHeaders, responseBody, false, message); + sendJson(response, 503, { error: { message } }); + return; + } + if (hostedWebSearchProtocolContext && this.browserWebSearchMcpIntegration) { const records = await selectHostedWebSearchProtocolRecords( hostedWebSearchProtocolContext, @@ -1546,16 +1594,20 @@ function bundledFusionToolFallbackMcpEntryPath(): string { function toolHubMcpServer(config: AppConfig, backendServers: unknown[]): GatewayMcpServerConfig | undefined { const toolHub = config.toolHub; - const runtimeConfig = toolHubMcpRuntimeConfig(config, backendServers); + const runtimeBackendServers = [ + ...toolHubBuiltInBackendServers(config), + ...backendServers + ]; + const runtimeConfig = toolHubMcpRuntimeConfig(config, runtimeBackendServers); if (!toolHub?.enabled || !runtimeConfig) { return undefined; } return { ...runtimeConfig, - name: uniqueMcpServerName(TOOL_HUB_MCP_SERVER_NAME, backendServers), + name: uniqueMcpServerName(TOOL_HUB_MCP_SERVER_NAME, runtimeBackendServers), protocolVersion: "2024-11-05", - requestTimeoutMs: Math.max(toolHub.requestTimeoutMs ?? 60000, 60000), + requestTimeoutMs: toolHubRequestTimeoutMs(config, runtimeBackendServers), startupTimeoutMs: 600000, stdioMessageMode: "content-length", transport: "stdio" @@ -1565,53 +1617,121 @@ function toolHubMcpServer(config: AppConfig, backendServers: unknown[]): Gateway export function fusionFallbackToolDefinitions( profiles: unknown[], backedToolNames: Set = new Set() -): Array<{ description?: string; inputSchema?: Record; name: string }> { - const byName = new Map; name: string }>(); +): FusionFallbackToolDefinition[] { + const byName = new Map(); for (const profile of profiles) { - if (!isRecord(profile) || profile.enabled === false || !Array.isArray(profile.tools)) { + if (!isRecord(profile) || profile.enabled === false) { continue; } - for (const tool of profile.tools) { - if (!isRecord(tool)) { - continue; - } - const name = stringValue(tool.name); - if (!name) { - continue; - } - if (backedToolNames.has(name)) { - continue; - } - const existing = byName.get(name); - const description = stringValue(tool.description); - const inputSchema = isRecord(tool.inputSchema) - ? tool.inputSchema - : isRecord(tool.input_schema) - ? tool.input_schema - : undefined; - if (existing) { - if (!existing.description && description) { - existing.description = description; + if (Array.isArray(profile.tools)) { + for (const tool of profile.tools) { + if (!isRecord(tool)) { + continue; } - if (!existing.inputSchema && inputSchema) { - existing.inputSchema = inputSchema; + const name = stringValue(tool.name); + if (!name) { + continue; + } + if (backedToolNames.has(name)) { + continue; } - continue; - } - byName.set(name, { - ...(description ? { description } : {}), - ...(inputSchema ? { inputSchema } : {}), - name - }); + const existing = byName.get(name); + const description = stringValue(tool.description); + const inputSchema = isRecord(tool.inputSchema) + ? tool.inputSchema + : isRecord(tool.input_schema) + ? tool.input_schema + : undefined; + const unavailableMessage = fusionFallbackToolUnavailableMessage(profile, name); + if (existing) { + if (!existing.description && description) { + existing.description = description; + } + if (!existing.inputSchema && inputSchema) { + existing.inputSchema = inputSchema; + } + if (!existing.unavailableMessage && unavailableMessage) { + existing.unavailableMessage = unavailableMessage; + } + continue; + } + + byName.set(name, { + ...(description ? { description } : {}), + ...(inputSchema ? { inputSchema } : {}), + ...(unavailableMessage ? { unavailableMessage } : {}), + name + }); + } + } + + const browserFallback = browserWebSearchFallbackToolDefinition(profile, backedToolNames); + if (browserFallback && !byName.has(browserFallback.name)) { + byName.set(browserFallback.name, browserFallback); } } return [...byName.values()]; } +type FusionFallbackToolDefinition = { + description?: string; + inputSchema?: Record; + name: string; + unavailableMessage?: string; +}; + +function fusionFallbackToolUnavailableMessage(profile: unknown, toolName: string): string | undefined { + if (!isRecord(profile)) { + return undefined; + } + const metadata = isRecord(profile.metadata) ? profile.metadata : undefined; + const fusionWebSearch = isRecord(metadata?.fusionWebSearch) ? metadata.fusionWebSearch : undefined; + const webSearchConfig = readFusionWebSearchConfig(fusionWebSearch); + if (webSearchConfig?.provider !== "browser" || webSearchConfig.toolName !== toolName) { + return undefined; + } + return browserWebSearchUnavailableMessage(toolName); +} + +function browserWebSearchUnavailableMessage(toolName: string): string { + return [ + `Fusion MCP tool "${toolName}" is unavailable because In-app Browser web search requires CCR Desktop.`, + "This runtime did not register the Electron browser web search integration, so the hidden browser search tool cannot run here.", + "Run the profile in CCR Desktop or switch the Fusion web search provider to Brave, Bing, Google CSE, Serper, SerpAPI, Tavily, or Exa." + ].join(" "); +} + +function browserWebSearchFallbackToolDefinition( + profile: Record, + backedToolNames: Set +): FusionFallbackToolDefinition | undefined { + const metadata = isRecord(profile.metadata) ? profile.metadata : undefined; + const fusionWebSearch = isRecord(metadata?.fusionWebSearch) ? metadata.fusionWebSearch : undefined; + const webSearchConfig = readFusionWebSearchConfig(fusionWebSearch); + if (webSearchConfig?.provider !== "browser" || !webSearchConfig.toolName || backedToolNames.has(webSearchConfig.toolName)) { + return undefined; + } + return { + description: "Fallback registration for CCR In-app Browser web search when the Electron browser integration is unavailable.", + inputSchema: { + additionalProperties: true, + properties: { + count: { maximum: 20, minimum: 1, type: "number" }, + prompt: { type: "string" }, + query: { type: "string" } + }, + required: ["prompt"], + type: "object" + }, + name: webSearchConfig.toolName, + unavailableMessage: fusionFallbackToolUnavailableMessage(profile, webSearchConfig.toolName) + }; +} + export function fusionToolNamesBackedByMcpServers(servers: unknown[]): Set { const names = new Set(); for (const server of servers) { @@ -3037,7 +3157,8 @@ function hostedWebSearchEvidenceText(records: BrowserWebSearchProtocolRecord[], const content = focusedWebSearchContent(result.content, queryHint); const details = [ result.snippet ? `Search snippet: ${result.snippet}` : "", - content ? `Extracted page content: ${content}` : "" + content ? `Extracted page content: ${content}` : "", + result.diagnostics?.length ? `Diagnostics: ${result.diagnostics.join("; ")}` : "" ].filter(Boolean).join("\n"); return [ `${resultIndex + 1}. ${result.title}`, @@ -5201,7 +5322,8 @@ function anthropicWebSearchResultBlock(result: BrowserWebSearchProtocolResult): function anthropicWebSearchResultSnippet(result: BrowserWebSearchProtocolResult): string | undefined { const parts = [ result.snippet ? `Search snippet: ${sanitizeWebSearchEvidenceText(result.snippet)}` : "", - result.content ? `Extracted page content: ${sanitizeWebSearchEvidenceText(result.content)}` : "" + result.content ? `Extracted page content: ${sanitizeWebSearchEvidenceText(result.content)}` : "", + result.diagnostics?.length ? `Diagnostics: ${result.diagnostics.join("; ")}` : "" ].filter(Boolean); return parts.length > 0 ? parts.join("\n") : undefined; } diff --git a/packages/core/src/mcp/fusion-tool-fallback-mcp.ts b/packages/core/src/mcp/fusion-tool-fallback-mcp.ts index a67635d..87c9b62 100644 --- a/packages/core/src/mcp/fusion-tool-fallback-mcp.ts +++ b/packages/core/src/mcp/fusion-tool-fallback-mcp.ts @@ -28,6 +28,7 @@ type McpTool = { description: string; inputSchema: JsonValue; name: string; + unavailableMessage?: string; }; type ToolCallResult = { @@ -134,10 +135,11 @@ async function handleJsonRpcRequest(payload: unknown): Promise item.name === toolLabel); const knownSuffix = toolNames.has(toolLabel) ? "" : " The requested tool was not in the fallback catalog."; return { content: [{ - text: + text: tool?.unavailableMessage || `Fusion MCP tool "${toolLabel}" is temporarily unavailable. ` + "CCR registered a fallback definition because the real MCP server did not provide the tool during discovery. " + `Check the Fusion MCP server logs and retry.${knownSuffix}`, @@ -179,7 +181,8 @@ function readFallbackTools(): McpTool[] { readString(item.description) || `Fallback registration for Fusion MCP tool "${name}". The real MCP server should handle successful calls.`, inputSchema: isRecord(item.inputSchema) ? item.inputSchema as JsonValue : objectSchema({}), - name + name, + unavailableMessage: readString(item.unavailableMessage) }); } return result; diff --git a/packages/core/src/mcp/toolhub-config.ts b/packages/core/src/mcp/toolhub-config.ts index be91a80..5c4cbd1 100644 --- a/packages/core/src/mcp/toolhub-config.ts +++ b/packages/core/src/mcp/toolhub-config.ts @@ -1,9 +1,13 @@ import { join as pathJoin } from "node:path"; import { CONFIGDIR } from "@ccr/core/config/constants"; -import type { AppConfig } from "@ccr/core/contracts/app"; +import type { AppConfig, GatewayMcpServerConfig } from "@ccr/core/contracts/app"; export const TOOL_HUB_MCP_SERVER_NAME = "ccr-toolhub"; export const TOOL_HUB_MCP_RUNTIME_FILE_NAME = "toolhub-mcp.js"; +export const BROWSER_AUTOMATION_MCP_SERVER_NAME = "ccr-browser-automation"; +export const BROWSER_AUTOMATION_MCP_PATH = "/__ccr/browser-automation/mcp"; +export const BROWSER_AUTOMATION_HANDOFF_TIMEOUT_MS = 600000; +export const TOOL_HUB_DEFAULT_REQUEST_TIMEOUT_MS = 60000; export type ToolHubMcpRuntimeConfig = { args: string[]; @@ -26,17 +30,53 @@ export type ToolHubClaudeCodeMcpConfig = { mcpServers: Record; }; -export function toolHubBackendServers(config: AppConfig | undefined, extraServers: unknown[] = []): unknown[] { +export function toolHubBackendServers( + config: AppConfig | undefined, + extraServers: unknown[] = [], + options: { + apiKey?: string; + includeBuiltIns?: boolean; + } = {} +): unknown[] { return [ + ...(options.includeBuiltIns === false ? [] : toolHubBuiltInBackendServers(config, options)), ...(Array.isArray(config?.agent?.mcpServers) ? config.agent.mcpServers : []), ...(Array.isArray(config?.toolHub?.mcpServers) ? config.toolHub.mcpServers : []), ...extraServers ].filter(isToolHubBackendServer); } +export function toolHubBuiltInBackendServers( + config: AppConfig | undefined, + options: { + apiKey?: string; + } = {} +): GatewayMcpServerConfig[] { + if (!config || !browserAutomationMcpEnabled(config) || !hasGatewayEndpoint(config)) { + return []; + } + + return [ + { + apiKey: options.apiKey || firstConfiguredApiKey(config), + headers: {}, + name: BROWSER_AUTOMATION_MCP_SERVER_NAME, + protocolVersion: "2024-11-05", + requestTimeoutMs: BROWSER_AUTOMATION_HANDOFF_TIMEOUT_MS, + startupTimeoutMs: 60000, + transport: "streamable-http", + url: `${gatewayEndpoint(config)}${BROWSER_AUTOMATION_MCP_PATH}` + } + ]; +} + +export function browserAutomationMcpEnabled(config: AppConfig | undefined): boolean { + return Boolean(config?.toolHub?.enabled && config.toolHub.browserAutomation); +} + export function toolHubMcpRuntimeConfig( config: AppConfig | undefined, - backendServers = toolHubBackendServers(config), + backendServers?: unknown[], options: { command?: string; entryPath?: string; @@ -52,10 +92,14 @@ export function toolHubMcpRuntimeConfig( return undefined; } - const normalizedBackendServers = backendServers.filter(isToolHubBackendServer); + const resolvedBackendServers = backendServers ?? toolHubBackendServers(config, [], { + apiKey: options.resolver?.apiKey + }); + const normalizedBackendServers = resolvedBackendServers.filter(isToolHubBackendServer); if (normalizedBackendServers.length === 0) { return undefined; } + const requestTimeoutMs = toolHubRequestTimeoutMs(config, normalizedBackendServers); return { args: [options.entryPath ?? bundledToolHubMcpEntryPath()], @@ -68,11 +112,18 @@ export function toolHubMcpRuntimeConfig( TOOLHUB_OPENAI_API_KEY: options.resolver?.apiKey ?? toolHub.llm?.apiKey ?? "", TOOLHUB_OPENAI_BASE_URL: options.resolver?.baseUrl ?? toolHub.llm?.baseUrl ?? "https://api.openai.com/v1", TOOLHUB_OPENAI_MODEL: options.resolver?.model ?? toolHub.llm?.model ?? "", - TOOLHUB_REQUEST_TIMEOUT_MS: String(toolHub.requestTimeoutMs ?? 60000) + TOOLHUB_REQUEST_TIMEOUT_MS: String(requestTimeoutMs) } }; } +export function toolHubRequestTimeoutMs(config: AppConfig | undefined, backendServers?: unknown[]): number { + const configuredTimeout = positiveInteger(config?.toolHub?.requestTimeoutMs, TOOL_HUB_DEFAULT_REQUEST_TIMEOUT_MS); + const backendTimeouts = (backendServers ?? toolHubBackendServers(config)) + .map((server) => isRecord(server) ? positiveInteger(server.requestTimeoutMs, 0) : 0); + return Math.max(configuredTimeout, ...backendTimeouts); +} + export function toolHubClaudeCodeMcpConfig( config: AppConfig | undefined, options: Parameters[2] = {} @@ -82,7 +133,9 @@ export function toolHubClaudeCodeMcpConfig( return undefined; } - const backendServers = toolHubBackendServers(config); + const backendServers = toolHubBackendServers(config, [], { + apiKey: options.resolver?.apiKey + }); if (backendServers.length === 0) { return undefined; } @@ -126,6 +179,39 @@ function stringValue(value: unknown): string | undefined { return typeof value === "string" && value.trim() ? value.trim() : undefined; } +function firstConfiguredApiKey(config: AppConfig): string | undefined { + return (Array.isArray(config.APIKEYS) ? config.APIKEYS : []) + .find((apiKey) => apiKey.key.trim())?.key.trim() || stringValue(config.APIKEY); +} + +function positiveInteger(value: unknown, fallback: number): number { + return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.trunc(value) : fallback; +} + +function gatewayEndpoint(config: AppConfig): string { + return `http://${formatHost(clientGatewayHost(config.gateway.host))}:${config.gateway.port}`; +} + +function hasGatewayEndpoint(config: AppConfig): boolean { + const gateway = (config as Partial).gateway; + return Boolean(gateway && stringValue(gateway.host) && Number.isFinite(gateway.port)); +} + +function formatHost(host: string): string { + return host.includes(":") && !host.startsWith("[") ? `[${host}]` : host; +} + +function clientGatewayHost(host: string): string { + const value = stringValue(host) ?? "127.0.0.1"; + if (value === "0.0.0.0") { + return "127.0.0.1"; + } + if (value === "::" || value === "[::]") { + return "::1"; + } + return value; +} + function uniqueStrings(values: string[]): string[] { const seen = new Set(); const result: string[] = []; diff --git a/packages/core/src/mcp/toolhub-mcp.ts b/packages/core/src/mcp/toolhub-mcp.ts index 1a5e9c7..9d440d9 100644 --- a/packages/core/src/mcp/toolhub-mcp.ts +++ b/packages/core/src/mcp/toolhub-mcp.ts @@ -149,6 +149,8 @@ type LlmToolResolution = { type ResolveOutput = { alreadyResolved?: boolean; + executionPlanInstructions?: string; + executionPlanJs?: string; nextAction?: { confirmationRequiredFor: string[]; firstAction: { @@ -186,6 +188,17 @@ const defaultRequestTimeoutMs = 60_000; const defaultMaxTools = 10; const discoveryCacheMaxAgeMs = 10_000; const repeatedResolveWindowMs = 5 * 60_000; +const executionPlanInstructions = [ + "Treat executionPlanJs as the dependency plan for ToolHub invocations.", + "Each callTool(toolName, args) call maps to one tool_hub.invoke call with tool=toolName and args=args.", + "await means the next ToolHub invocation depends on the previous result and must not be started early.", + "Only callTool calls inside the same Promise.all([...]) expression may be issued as parallel tool calls.", + "If a later call needs values from an earlier result, wait for that earlier result and fill the arguments after it returns." +].join(" "); +const reservedJavaScriptWords = new Set( + "await break case catch class const continue debugger default delete do else enum export extends false finally for function if import in instanceof new null return super switch this throw true try typeof var void while with yield" + .split(" ") +); type RuntimeLike = { callTool(params: unknown): Promise; @@ -365,15 +378,25 @@ class ToolHubRuntime { await this.registry.refreshServers(undefined, true); catalog = await this.registry.listTools(); } + if (taskWantsChromeLoginImport(task) && !catalogHasChromeLoginImportTool(catalog)) { + await this.registry.refreshServers(["ccr-browser-automation"], true); + catalog = await this.registry.listTools(); + } if (catalog.length === 0) { throw new Error("No MCP tools are available to resolve."); } const cached = this.findRecentlyResolvedTask(scopeKey, taskHash); if (cached) { - const selectedTools = cached.toolNames - .map((toolName) => this.resolveCatalogEntry(toolName, catalog)) - .filter((entry): entry is CatalogEntry => Boolean(entry)); + const selectedTools = expandToolBundleWithCompanionTools( + uniqueToolEntries([ + ...cached.toolNames + .map((toolName) => this.resolveCatalogEntry(toolName, catalog)) + .filter((entry): entry is CatalogEntry => Boolean(entry)), + ...getDeterministicTaskTools(task, catalog) + ]), + catalog + ); if (selectedTools.length > 0) { this.markToolsLoaded(scopeKey, selectedTools); return toolResult(this.buildRepeatedResolveOutput(selectedTools)); @@ -383,9 +406,15 @@ class ToolHubRuntime { const inFlightResolve = session.inFlightResolves.get(taskHash); if (inFlightResolve) { const output = await inFlightResolve; - const selectedTools = output.selectedTools - .map((tool) => this.resolveCatalogEntry(tool.toolName, catalog) ?? tool) - .filter((entry): entry is CatalogEntry => Boolean(entry)); + const selectedTools = expandToolBundleWithCompanionTools( + uniqueToolEntries([ + ...output.selectedTools + .map((tool) => this.resolveCatalogEntry(tool.toolName, catalog) ?? tool) + .filter((entry): entry is CatalogEntry => Boolean(entry)), + ...getDeterministicTaskTools(task, catalog) + ]), + catalog + ); if (selectedTools.length > 0) { this.markToolsLoaded(scopeKey, selectedTools); return toolResult(this.buildRepeatedResolveOutput(selectedTools)); @@ -461,7 +490,13 @@ class ToolHubRuntime { usedLlm = false; } - let selectedTools = resolution.selectedTools; + let selectedTools = expandToolBundleWithCompanionTools( + uniqueToolEntries([ + ...resolution.selectedTools, + ...getDeterministicTaskTools(input.task, input.catalog) + ]), + input.catalog + ); if (input.withoutSideEffects) { selectedTools = selectedTools.filter((tool) => !tool.invocation.sideEffect); } @@ -471,13 +506,14 @@ class ToolHubRuntime { this.markToolsLoaded(input.scopeKey, selectedTools); this.rememberResolvedTask(input.scopeKey, input.taskHash, selectedTools); + const executionPlanJs = buildExecutionPlanJs(resolution.workflowSketch, selectedTools); return { - ...this.buildResolveOutput(resolution.summary, selectedTools), + ...this.buildResolveOutput(resolution.summary, selectedTools, executionPlanJs), plannedSteps: resolution.plannedSteps, referencedTokens: resolution.referencedTokens, retriever, usedLlm, - workflowSketch: resolution.workflowSketch + workflowSketch: executionPlanJs }; } @@ -587,39 +623,50 @@ class ToolHubRuntime { return catalog.find((entry) => entry.toolName === resolvedName); } - private buildResolveOutput(summary: string, selectedTools: CatalogEntry[]): ResolveOutput { + private buildResolveOutput(summary: string, selectedTools: CatalogEntry[], executionPlanJs = buildSequentialExecutionPlanJs(selectedTools)): ResolveOutput { return { + executionPlanInstructions, + executionPlanJs, nextAction: this.buildResolveNextAction(selectedTools), reasoningSummary: summary, runtimeContext: { - availableContextKeys: ["selectedTools"], - summary: selectedTools.map((tool) => `${tool.toolName}: ${tool.description || tool.title}`) + availableContextKeys: ["selectedTools", "executionPlanJs", "executionPlanInstructions"], + summary: [ + ...selectedTools.map((tool) => `${tool.toolName}: ${tool.description || tool.title}`), + "Follow executionPlanJs for dependency ordering before invoking selected tools." + ] }, selectedToolNames: selectedTools.map((tool) => tool.toolName), selectedTools, - tsDefinitions: buildTsDefinitions(selectedTools) + tsDefinitions: buildTsDefinitions(selectedTools), + workflowSketch: executionPlanJs }; } private buildRepeatedResolveOutput(selectedTools: CatalogEntry[]): ResolveOutput { const nextAction = this.buildResolveNextAction(selectedTools); + const executionPlanJs = buildSequentialExecutionPlanJs(selectedTools); return { alreadyResolved: true, + executionPlanInstructions, + executionPlanJs, nextAction, reasoningSummary: [ "This task has already been resolved in the current ToolHub session.", nextAction.instruction ].join(" "), runtimeContext: { - availableContextKeys: ["selectedTools", "nextAction"], + availableContextKeys: ["selectedTools", "nextAction", "executionPlanJs", "executionPlanInstructions"], summary: [ "The selected tools are already loaded for tool_hub.invoke.", "Do not repeat discovery for this task.", - nextAction.instruction + nextAction.instruction, + "Follow executionPlanJs for dependency ordering before invoking selected tools." ] }, selectedToolNames: selectedTools.map((tool) => tool.toolName), - selectedTools + selectedTools, + workflowSketch: executionPlanJs }; } @@ -646,7 +693,10 @@ class ToolHubRuntime { toolName: firstTool?.toolName, type: "invoke_tool" as const }; - const instructionParts = ["Do not call tool_hub.resolve again for this task."]; + const instructionParts = [ + "Do not call tool_hub.resolve again for this task.", + "Follow executionPlanJs: await is serial dependency; only Promise.all groups may run in parallel." + ]; if (firstTool && firstRequiredArguments.length > 0) { instructionParts.push(`Ask the user for missing required arguments for ${firstTool.toolName}: ${firstRequiredArguments.join(", ")}.`); } else if (firstTool) { @@ -790,7 +840,7 @@ class ToolHubRegistry { private async ensureDiscoveryFresh(maxAgeMs = discoveryCacheMaxAgeMs): Promise { const staleServerNames = [...this.entries.values()] - .filter((entry) => !entry.loadedFromCache && (!entry.lastCheckedAt || Date.now() - entry.lastCheckedAt > maxAgeMs)) + .filter((entry) => !entry.lastCheckedAt || Date.now() - entry.lastCheckedAt > maxAgeMs) .map((entry) => entry.config.name); if (staleServerNames.length === 0) { return; @@ -1752,6 +1802,7 @@ function metaTools(): Array<{ description: string; inputSchema: Record\", {...}) or tools.call(\"\", {...}).", "3. Call the tree-sitter tool on that sketch.", "4. Inspect the tree-sitter result and check whether the bundle is complete end-to-end.", @@ -2000,10 +2051,18 @@ function buildSearchSystemPrompt(catalog: SearchCatalogItem[], topK: number): st "{ \"summary\": string, \"steps\": string[], \"workflowSketch\": string, \"toolNames\": string[] }", "Rules:", "- toolNames must be exact catalog names or aliases.", + "- workflowSketch is the dependency plan that will be returned to the caller as executionPlanJs.", + "- In workflowSketch, use await to show serial dependencies and Promise.all([...]) only for tool calls that are independent and safe to invoke in parallel.", + "- Do not put side-effecting calls in Promise.all unless they are truly independent and safe to run concurrently.", "- In workflowSketch, prefer string-literal tool calls over reconstructed member chains whenever possible.", "- Include prerequisite and downstream tools you will likely need after the first action.", "- Never invent tool names.", "- Generic browser automation tools are a strong match for web tasks such as ordering, buying, booking, delivery, or checkout when no domain-specific MCP tool exists.", + "- For browser navigation/open calls, prefer omitting waitUntil or using waitUntil: \"interactive\" so the agent can inspect and act as soon as the page is usable.", + "- Do not use waitUntil: \"network_idle\" for Gmail, Google sign-in, SPAs, mail, chat, auth, checkout, verification, or pages with long-lived requests. Use network_idle only when the user explicitly asks for network quiescence.", + "- When selecting CCR browser automation tools, include the human-handoff follow-up tools needed if login, CAPTCHA, verification, blocked navigation, or manual confirmation appears.", + "- For CCR browser automation bundles, include browser_handoff_request and browser_handoff_wait when the workflow may need user help. Do not assume all browser tools are preloaded.", + "- For importing existing Chrome login state into CCR's in-app browser, select browser_chrome_login_import and include browser_chrome_login_import_status to check completion.", "- If using member-call syntax, prefer tools.(...) or mcp..(...).", "- For lookup tasks, do not stop at opening or navigating. Include the tools needed to read or extract the answer.", "- If the catalog has no strong match, return an empty toolNames array.", @@ -2312,6 +2371,21 @@ function getLocalFallbackPreferredTools(taskText: string): Map { add("browser_screenshot", 60); add("browser_events_await", 44); } + if (hasAnyToken(tokens, [ + "auth", + "chrome", + "cookie", + "cookies", + "import", + "localstorage", + "login", + "session", + "signin", + "storage" + ]) || /chrome|cookie|cookies|localstorage|local storage|登录态|登录状态|导入登录|浏览器登录|本地存储/.test(normalizedText)) { + add("browser_chrome_login_import", 128); + add("browser_chrome_login_import_status", 92); + } if (hasAnyToken(tokens, ["address", "delivery", "geo", "geolocation", "location", "nearby", "pickup", "store"]) || /地址|定位|位置|附近|配送|外卖|自取|门店/.test(normalizedText)) { add("location_permission_status", 90); @@ -2324,6 +2398,28 @@ function getLocalFallbackPreferredTools(taskText: string): Map { return scores; } +function getDeterministicTaskTools(taskText: string, catalog: CatalogEntry[]): CatalogEntry[] { + if (!taskWantsChromeLoginImport(taskText)) { + return []; + } + return [ + catalog.find((tool) => isBrowserAutomationTool(tool) && tool.remoteToolName === "browser_chrome_login_import"), + catalog.find((tool) => isBrowserAutomationTool(tool) && tool.remoteToolName === "browser_chrome_login_import_status") + ].filter((tool): tool is CatalogEntry => Boolean(tool)); +} + +function taskWantsChromeLoginImport(taskText: string): boolean { + const normalizedText = taskText.toLowerCase(); + return normalizedText.includes("browser_chrome_login_import") || + /(chrome|谷歌浏览器|浏览器).*(login|signin|auth|cookie|localstorage|local storage|session|登录态|登录状态|登录信息|本地存储|cookie)/.test(normalizedText) || + /(import|导入|迁移|同步).*(chrome|谷歌浏览器).*(login|signin|auth|cookie|localstorage|local storage|session|登录态|登录状态|登录信息|本地存储)/.test(normalizedText) || + /(登录态|登录状态|登录信息).*(chrome|谷歌浏览器|浏览器).*(导入|迁移|同步)/.test(normalizedText); +} + +function catalogHasChromeLoginImportTool(catalog: CatalogEntry[]): boolean { + return catalog.some((tool) => isBrowserAutomationTool(tool) && tool.remoteToolName === "browser_chrome_login_import"); +} + function tokenizeLocalSearchText(text: string): string[] { const tokens = text .replace(/([a-z0-9])([A-Z])/g, "$1 $2") @@ -2350,10 +2446,113 @@ function uniqueToolEntries(entries: CatalogEntry[]): CatalogEntry[] { return output; } +function expandToolBundleWithCompanionTools(selectedTools: CatalogEntry[], catalog: CatalogEntry[]): CatalogEntry[] { + const output = uniqueToolEntries(selectedTools); + const selectedNames = new Set(output.map((tool) => tool.toolName)); + const selectedRemoteNames = new Set(output.map((tool) => tool.remoteToolName)); + const hasBrowserAutomationTool = output.some((tool) => isBrowserAutomationTool(tool)); + const hasHandoffRequestTool = output.some((tool) => + isBrowserAutomationTool(tool) && + (tool.remoteToolName === "browser_handoff_request" || tool.remoteToolName === "askHumanHelp") + ); + const hasChromeLoginImportTool = output.some((tool) => + isBrowserAutomationTool(tool) && + tool.remoteToolName === "browser_chrome_login_import" + ); + const companionRemoteNames = new Set(); + + if (hasBrowserAutomationTool) { + companionRemoteNames.add("browser_handoff_request"); + companionRemoteNames.add("browser_handoff_status"); + companionRemoteNames.add("browser_handoff_wait"); + } + if (hasHandoffRequestTool) { + companionRemoteNames.add("browser_handoff_wait"); + } + if (hasChromeLoginImportTool) { + companionRemoteNames.add("browser_chrome_login_import_status"); + } + + for (const remoteToolName of companionRemoteNames) { + if (selectedRemoteNames.has(remoteToolName)) { + continue; + } + const companion = catalog.find((tool) => + isBrowserAutomationTool(tool) && + tool.remoteToolName === remoteToolName && + !selectedNames.has(tool.toolName) + ); + if (companion) { + output.push(companion); + selectedNames.add(companion.toolName); + selectedRemoteNames.add(companion.remoteToolName); + } + } + + return output; +} + +function isBrowserAutomationTool(tool: CatalogEntry): boolean { + return tool.serverName === "ccr-browser-automation" || + tool.serverId === "ccr-browser-automation" || + tool.serverNamespace === "ccr_browser_automation" || + tool.toolName.startsWith("mcp.ccr_browser_automation."); +} + function buildLocalFallbackWorkflowSketch(selectedTools: CatalogEntry[]): string | undefined { - return selectedTools.length > 0 - ? selectedTools.slice(0, 5).map((tool) => `await callTool(${JSON.stringify(tool.toolName)}, {});`).join("\n") - : undefined; + return selectedTools.length > 0 ? buildSequentialExecutionPlanJs(selectedTools) : undefined; +} + +function isBrowserNavigationTool(tool: CatalogEntry): boolean { + return isBrowserAutomationTool(tool) && ( + tool.toolName.endsWith("browser_session_open") || + tool.toolName.endsWith("browser_navigate") || + tool.remoteToolName === "browser_session_open" || + tool.remoteToolName === "browser_navigate" + ); +} + +function buildExecutionPlanJs(workflowSketch: string | undefined, selectedTools: CatalogEntry[]): string { + const trimmed = typeof workflowSketch === "string" ? workflowSketch.trim() : ""; + return trimmed || buildSequentialExecutionPlanJs(selectedTools); +} + +function buildSequentialExecutionPlanJs(selectedTools: CatalogEntry[]): string { + if (selectedTools.length === 0) { + return [ + "async function runWithToolHub() {", + " // Ask the user for missing task details before invoking tools.", + "}" + ].join("\n"); + } + const lines = [ + "async function runWithToolHub() {", + " // Invoke calls in this order unless the plan explicitly uses Promise.all." + ]; + selectedTools.forEach((tool, index) => { + lines.push(` const step${index + 1} = await callTool(${JSON.stringify(tool.toolName)}, ${buildExecutionPlanArgs(tool)});`); + }); + lines.push("}"); + return lines.join("\n"); +} + +function buildExecutionPlanArgs(tool: CatalogEntry): string { + if (isBrowserNavigationTool(tool)) { + return "{ url, waitUntil: \"interactive\" }"; + } + const required = getSchemaRequiredProperties(tool.inputSchema); + if (required.length === 0) { + return "{}"; + } + return `{ ${required.map((key) => `${JSON.stringify(key)}: ${toPlanVariableName(key)}`).join(", ")} }`; +} + +function toPlanVariableName(value: string): string { + const identifier = toIdentifier(value).replace(/^[A-Z]/, (match) => match.toLowerCase()); + if (!identifier || reservedJavaScriptWords.has(identifier)) { + return "value"; + } + return identifier; } function inferInvocation(tool: ToolDefinition): ToolInvocation { diff --git a/packages/electron/src/main/browser-automation-mcp.ts b/packages/electron/src/main/browser-automation-mcp.ts new file mode 100644 index 0000000..884905f --- /dev/null +++ b/packages/electron/src/main/browser-automation-mcp.ts @@ -0,0 +1,3517 @@ +import type { IncomingMessage, ServerResponse } from "node:http"; +import { randomUUID } from "node:crypto"; +import type { Event as ElectronEvent, WebContents } from "electron"; +import { loadAppConfig } from "@ccr/core/config/config"; +import type { + BuiltInBrowserAutomationHandoff, + BuiltInBrowserAutomationHandoffKind, + BuiltInBrowserState, + BuiltInBrowserTabState +} from "@ccr/core/contracts/app"; +import type { BrowserAutomationMcpIntegration } from "@ccr/core/gateway/service"; +import { BROWSER_AUTOMATION_MCP_PATH } from "@ccr/core/mcp/toolhub-config"; +import { builtInBrowserService, type BrowserAutomationEvent } from "./built-in-browser"; +import { chromeLoginImportService } from "./chrome-login-import"; + +type JsonPrimitive = boolean | null | number | string; +type JsonValue = JsonPrimitive | JsonValue[] | { [key: string]: JsonValue }; + +type JsonRpcRequest = { + id?: null | number | string; + jsonrpc?: string; + method?: string; + params?: unknown; +}; + +type JsonRpcResponse = + | { + id: null | number | string; + jsonrpc: "2.0"; + result: JsonValue; + } + | { + error: { + code: number; + data?: JsonValue; + message: string; + }; + id: null | number | string; + jsonrpc: "2.0"; + }; + +type McpTool = { + description: string; + inputSchema: JsonValue; + name: string; + tags?: string[]; + title?: string; +}; + +type ToolCallResult = { + content: Array<{ text: string; type: "text" }>; + isError?: boolean; +}; + +type BrowserTarget = { + axNodeId?: string; + backendNodeId?: number; + exact?: boolean; + index?: number; + ref?: string; + role?: string; + selector?: string; + text?: string; +}; + +type BrowserSessionRef = { + frameId?: string; + sessionId: string; + tabId: string; + userId?: string; +}; + +type AttachedSession = { + attachedAt: number; + leaseId: string; + observeOnly: boolean; + ref: BrowserSessionRef; +}; + +type EventSubscription = { + channels: Set; + dropped: boolean; + events: BrowserAutomationEvent[]; + ref?: BrowserSessionRef; + redactTextInput: boolean; + sampleMouseMove: boolean; + startedAt: number; + subscriptionId: string; + unsubscribe: () => void; +}; + +type AxSnapshotResult = { + handoff?: BuiltInBrowserAutomationHandoff; + handoffRequired?: boolean; + humanHelp?: Record; + humanHelpRequired?: boolean; + nextAction?: "human_help"; + nodes: Array>; + scope: string; + session: BrowserSessionRef; + title: string; + url: string; +}; + +type BrowserHandoffDetection = { + kind: BuiltInBrowserAutomationHandoffKind; + message: string; + reason: string; +}; + +type ReadinessResult = { + matched: boolean; + matchedEvent?: string; + navigationError?: { + errorCode?: number; + errorDescription?: string; + url?: string; + }; + readinessUrl?: string; + timedOut: boolean; +}; + +type ReadinessEvent = { + errorCode?: number; + errorDescription?: string; + isMainFrame?: boolean; + type: string; + url?: string; +}; + +type NavigationReadinessContext = { + expectedUrl?: string; + previousUrl?: string; +}; + +const protocolVersion = "2024-11-05"; +const automationEventReplayMs = 15_000; +const defaultAxSnapshotLimit = 60; +const defaultSnapshotMaxElements = 80; +const defaultSnapshotMaxText = 3_000; +const defaultEventAwaitTimeoutMs = 10_000; +const defaultJavascriptTimeoutMs = 8_000; +const maxMcpRequestBytes = 2 * 1024 * 1024; +const maxSubscriptionEvents = 512; +const maxToolResultChars = 60_000; +const maxToolResultArrayItems = 120; +const maxToolResultStringChars = 2_000; +const maxToolResultObjectKeys = 120; +const maxSnapshotResultElements = 80; +const maxAxSnapshotResultNodes = 80; +const maxBrowserResultTextChars = 3_000; +const defaultWaitTimeoutMs = 10_000; + +const sessionSchema = objectSchema({ + frameId: { description: "Optional frame id. CCR currently targets the main frame.", type: "string" }, + sessionId: { description: "Browser automation session id.", type: "string" }, + tabId: { description: "Built-in browser tab id.", type: "string" }, + userId: { description: "Optional logical user id.", type: "string" } +}, ["sessionId", "tabId"]); + +const cursorSchema = objectSchema({ + dropped: { description: "Whether earlier events were dropped before this cursor.", type: "boolean" }, + seq: { description: "Last observed event sequence.", type: "number" }, + subscriptionId: { description: "Event subscription id.", type: "string" }, + ts: { description: "Last observed event timestamp.", type: "number" } +}, ["subscriptionId", "seq"]); + +const targetSchema = objectSchema({ + axNodeId: { description: "Accessibility node id returned by browser_ax_snapshot/query. In CCR this is a stable element ref.", type: "string" }, + backendNodeId: { description: "Reserved for CDP-compatible clients. CSS/ref targeting is preferred in CCR.", type: "number" }, + exact: { description: "Match text/name exactly instead of by substring.", type: "boolean" }, + index: { description: "Zero-based match index when text/role resolves multiple elements.", minimum: 0, type: "number" }, + ref: { description: "Element ref returned by browser_snapshot. Refs are CSS selectors.", type: "string" }, + role: { description: "Accessible or implicit role such as button, link, textbox, combobox, checkbox.", type: "string" }, + selector: { description: "CSS selector. Used directly when provided.", type: "string" }, + text: { description: "Visible text, accessible name, placeholder, label, or value to match.", type: "string" } +}); + +const browserAutomationTools: McpTool[] = [ + ...browserPublicAutomationTools(), + ...browserLegacyAliasTools() +].filter((tool, index, tools) => tools.findIndex((candidate) => candidate.name === tool.name) === index); + +function browserPublicAutomationTools(): McpTool[] { + return [ + { + description: "Open a URL or attach an existing CCR built-in browser tab and create an automation session.", + inputSchema: objectSchema({ + observeOnly: { description: "If true, action tools reject this session.", type: "boolean" }, + tabId: { description: "Existing tab id to attach.", type: "string" }, + timeoutMs: { description: "Optional navigation timeout in milliseconds.", type: "number" }, + url: { description: "Optional URL or search query to open.", type: "string" }, + userId: { description: "Optional logical user id.", type: "string" }, + waitUntil: { description: "Readiness condition. Default/recommended: interactive, which waits until the page is inspectable/actionable and avoids long-lived network requests. network_idle is rarely appropriate for SPAs, Google, mail, chat, auth, or streaming pages.", enum: ["none", "interactive", "domcontentloaded", "load", "network_idle"], type: "string" }, + windowId: { description: "Ignored in CCR; included for agentic-browser compatibility.", type: "string" } + }), + name: "browser_session_open", + tags: ["browser", "automation", "session", "open", "tab"], + title: "Open Browser Automation Session" + }, + { + description: "Detach and release a browser automation session. Does not close the tab.", + inputSchema: objectSchema({ session: sessionSchema }, ["session"]), + name: "browser_session_close", + tags: ["browser", "automation", "session", "close"], + title: "Close Browser Automation Session" + }, + { + description: "Create a new CCR built-in browser tab. Accepts an existing session or the fixed CCR browser window id.", + inputSchema: objectSchema({ + activate: { description: "Whether the new tab should become active. Defaults to true.", type: "boolean" }, + session: sessionSchema, + url: { description: "Optional URL or search query.", type: "string" }, + windowId: { description: "Ignored in CCR; included for compatibility.", type: "string" } + }), + name: "browser_tab_create", + tags: ["browser", "automation", "tab", "create"], + title: "Create Browser Tab" + }, + { + description: "List all CCR built-in browser tabs with active, loading, URL, title, and navigation state.", + inputSchema: objectSchema({ + session: sessionSchema, + windowId: { description: "Ignored in CCR; included for compatibility.", type: "string" } + }), + name: "browser_tab_list", + tags: ["browser", "automation", "tab", "list"], + title: "List Browser Tabs" + }, + { + description: "Activate an existing CCR built-in browser tab.", + inputSchema: objectSchema({ + session: sessionSchema, + tabId: { description: "Tab id to activate. Defaults to session.tabId.", type: "string" } + }), + name: "browser_tab_activate", + tags: ["browser", "automation", "tab", "activate", "focus"], + title: "Activate Browser Tab" + }, + { + description: "Close a CCR built-in browser tab.", + inputSchema: objectSchema({ + session: sessionSchema, + tabId: { description: "Tab id to close. Defaults to session.tabId.", type: "string" } + }), + name: "browser_tab_close", + tags: ["browser", "automation", "tab", "close"], + title: "Close Browser Tab" + }, + { + description: "Navigate the session tab or a specified tab to a URL or search query.", + inputSchema: objectSchema({ + session: sessionSchema, + tabId: { description: "Optional tab id. Defaults to session.tabId or active tab.", type: "string" }, + timeoutMs: { description: "Optional navigation timeout in milliseconds.", maximum: 120000, minimum: 100, type: "number" }, + url: { description: "URL or search query to load.", type: "string" }, + waitUntil: { description: "Readiness condition. Default/recommended: interactive, which waits until the page is inspectable/actionable and avoids long-lived network requests. network_idle is rarely appropriate for SPAs, Google, mail, chat, auth, or streaming pages.", enum: ["none", "interactive", "domcontentloaded", "load", "network_idle"], type: "string" } + }, ["url"]), + name: "browser_navigate", + tags: ["browser", "automation", "navigate", "tab", "url"], + title: "Navigate Browser Tab" + }, + { + description: "Go back in the session tab or a specified tab.", + inputSchema: objectSchema({ session: sessionSchema, tabId: { type: "string" } }), + name: "browser_tab_go_back", + tags: ["browser", "automation", "tab", "back"], + title: "Browser Tab Back" + }, + { + description: "Go forward in the session tab or a specified tab.", + inputSchema: objectSchema({ session: sessionSchema, tabId: { type: "string" } }), + name: "browser_tab_go_forward", + tags: ["browser", "automation", "tab", "forward"], + title: "Browser Tab Forward" + }, + { + description: "Reload the session tab or a specified tab.", + inputSchema: objectSchema({ session: sessionSchema, tabId: { type: "string" } }), + name: "browser_tab_reload", + tags: ["browser", "automation", "tab", "reload"], + title: "Reload Browser Tab" + }, + { + description: "Read a condensed accessibility-oriented page snapshot. Nodes include axNodeId/ref, role, name, value, text, and rect.", + inputSchema: objectSchema({ + includeIgnored: { description: "Included for compatibility; CCR returns visible/high-signal nodes.", type: "boolean" }, + limit: { description: "Maximum nodes to return.", maximum: 300, minimum: 1, type: "number" }, + maxDepth: { description: "Included for compatibility.", type: "number" }, + rootAxNodeId: { description: "Optional root node/ref to scope the snapshot.", type: "string" }, + scope: { description: "full or outline. CCR returns the same compact shape for both.", enum: ["full", "outline"], type: "string" }, + session: sessionSchema + }, ["session"]), + name: "browser_ax_snapshot", + tags: ["browser", "automation", "accessibility", "snapshot", "dom", "page"], + title: "Browser Accessibility Snapshot" + }, + { + description: "Search the page accessibility outline by role, accessible name, visible text, label, placeholder, or value.", + inputSchema: objectSchema({ + includeIgnored: { description: "Included for compatibility; CCR searches visible/high-signal nodes.", type: "boolean" }, + limit: { description: "Maximum matches.", maximum: 300, minimum: 1, type: "number" }, + name: { description: "Accessible name filter.", type: "string" }, + role: { description: "Role filter such as button, link, textbox, combobox.", type: "string" }, + rootAxNodeId: { description: "Optional root node/ref to scope the query.", type: "string" }, + session: sessionSchema, + text: { description: "Visible text/value/description filter.", type: "string" } + }, ["session"]), + name: "browser_ax_query", + tags: ["browser", "automation", "accessibility", "query", "find"], + title: "Query Browser Accessibility Tree" + }, + { + description: "Click an element resolved by axNodeId/ref, CSS selector, role, or text.", + inputSchema: objectSchema({ force: { type: "boolean" }, session: sessionSchema, target: targetSchema }, ["session", "target"]), + name: "browser_element_click", + tags: ["browser", "automation", "element", "click"], + title: "Click Browser Element" + }, + { + description: "Set text into an input, textarea, contenteditable, or textbox-like element.", + inputSchema: objectSchema({ + replace: { description: "Replace existing text. Defaults to true.", type: "boolean" }, + session: sessionSchema, + target: targetSchema, + text: { type: "string" } + }, ["session", "target", "text"]), + name: "browser_element_input", + tags: ["browser", "automation", "element", "input", "type", "form"], + title: "Input Browser Element" + }, + { + description: "Select an option on a native select by value or visible label.", + inputSchema: objectSchema({ + exact: { type: "boolean" }, + session: sessionSchema, + target: targetSchema, + value: { description: "Requested option value or visible label.", type: "string" } + }, ["session", "target", "value"]), + name: "browser_element_select", + tags: ["browser", "automation", "element", "select", "form"], + title: "Select Browser Element Option" + }, + { + description: "Press a keyboard key against a resolved target or the current focused element.", + inputSchema: objectSchema({ + key: { description: "Keyboard key, e.g. Enter, Tab, Escape, ArrowDown.", type: "string" }, + session: sessionSchema, + target: targetSchema + }, ["session", "key"]), + name: "browser_element_press", + tags: ["browser", "automation", "element", "press", "keyboard"], + title: "Press Browser Element Key" + }, + { + description: "Scroll the page or a target element.", + inputSchema: objectSchema({ + amount: { description: "Scroll amount in CSS pixels.", type: "number" }, + direction: { description: "Scroll direction.", enum: ["up", "down"], type: "string" }, + session: sessionSchema, + target: targetSchema + }, ["session"]), + name: "browser_element_scroll", + tags: ["browser", "automation", "element", "scroll"], + title: "Scroll Browser Element" + }, + { + description: "Subscribe to browser automation events such as tab, navigation, DOM-ready, runtime, and loading signals.", + inputSchema: objectSchema({ + channels: { description: "Event channels: tab, navigation, dom, runtime, dialog, download, input, focus, selection, handoff.", items: { type: "string" }, type: "array" }, + redactTextInput: { type: "boolean" }, + sampleMouseMove: { type: "boolean" }, + session: sessionSchema + }, ["session", "channels"]), + name: "browser_events_subscribe", + tags: ["browser", "automation", "events", "subscribe"], + title: "Subscribe Browser Events" + }, + { + description: "Read buffered browser automation events from a subscription.", + inputSchema: objectSchema({ + cursor: cursorSchema, + limit: { maximum: 200, minimum: 1, type: "number" }, + subscriptionId: { type: "string" } + }, ["subscriptionId"]), + name: "browser_events_read", + tags: ["browser", "automation", "events", "read"], + title: "Read Browser Events" + }, + { + description: "Wait for browser automation events matching kind, tabId, URL pattern, title pattern, or summary pattern.", + inputSchema: objectSchema({ + coalesceMs: { description: "Small delay after a first match to collect related events.", type: "number" }, + cursor: cursorSchema, + kinds: { items: { type: "string" }, type: "array" }, + maxEvents: { maximum: 50, minimum: 1, type: "number" }, + summaryPattern: { type: "string" }, + tabId: { type: "string" }, + timeoutMs: { maximum: 120000, minimum: 100, type: "number" }, + titlePattern: { type: "string" }, + urlPattern: { type: "string" }, + subscriptionId: { type: "string" } + }, ["subscriptionId"]), + name: "browser_events_await", + tags: ["browser", "automation", "events", "await", "wait"], + title: "Await Browser Events" + }, + { + description: "Unsubscribe from browser automation events and release the subscription.", + inputSchema: objectSchema({ subscriptionId: { type: "string" } }, ["subscriptionId"]), + name: "browser_events_unsubscribe", + tags: ["browser", "automation", "events", "unsubscribe"], + title: "Unsubscribe Browser Events" + }, + { + description: "Handle a currently open JavaScript alert/confirm/prompt dialog using Chromium CDP.", + inputSchema: objectSchema({ + accept: { type: "boolean" }, + promptText: { type: "string" }, + session: sessionSchema + }, ["session", "accept"]), + name: "browser_dialog_handle", + tags: ["browser", "automation", "dialog", "alert", "confirm", "prompt"], + title: "Handle Browser Dialog" + }, + { + description: "Request human intervention for the current browser task. Shows the hidden browser window and displays the requested action in the top toolbar.", + inputSchema: objectSchema({ + kind: { + description: "Type of help needed.", + enum: ["login_required", "verification_code", "human_verification", "blocked", "other"], + type: "string" + }, + message: { description: "Short instruction shown in the top toolbar.", type: "string" }, + reason: { description: "Why automation is blocked.", type: "string" }, + session: sessionSchema, + tabId: { description: "Optional tab id. Defaults to session.tabId or active tab.", type: "string" } + }, ["reason"]), + name: "browser_handoff_request", + tags: ["browser", "automation", "human", "handoff", "intervention"], + title: "Request Browser Human Handoff" + }, + { + description: "Read the current browser human handoff status.", + inputSchema: objectSchema({}), + name: "browser_handoff_status", + tags: ["browser", "automation", "human", "handoff"], + title: "Browser Handoff Status" + }, + { + description: "Wait until the user clicks Done or Hide on the current browser handoff toolbar. Use after browser_handoff_request or a tool result with humanHelpRequired.", + inputSchema: objectSchema({ + handoffId: { description: "Optional handoff id returned by browser_handoff_request or humanHelp.handoff.", type: "string" }, + session: sessionSchema, + tabId: { description: "Optional tab id. Defaults to session.tabId.", type: "string" }, + timeoutMs: { description: "Maximum wait time in milliseconds.", maximum: 600000, minimum: 100, type: "number" } + }), + name: "browser_handoff_wait", + tags: ["browser", "automation", "human", "handoff", "wait"], + title: "Wait For Browser Human Handoff" + }, + { + description: "Clear the current browser human handoff prompt.", + inputSchema: objectSchema({ + status: { enum: ["completed", "dismissed"], type: "string" } + }), + name: "browser_handoff_clear", + tags: ["browser", "automation", "human", "handoff"], + title: "Clear Browser Handoff" + }, + { + description: "Ask the user to confirm importing Chrome cookies and localStorage for selected domains into CCR's in-app browser. Opens a browser confirmation page; the Chrome extension performs the import after user confirmation.", + inputSchema: objectSchema({ + domain: { description: "Single domain to import, such as github.com. Used with domains if both are provided.", type: "string" }, + domains: { description: "Domains to import. If omitted, CCR derives the current tab hostname.", items: { type: "string" }, type: "array" }, + openConfirmationPage: { description: "Open the system browser confirmation page. Defaults to true.", type: "boolean" }, + session: sessionSchema, + tabId: { description: "Optional tab id used to derive a domain when domain/domains are omitted.", type: "string" }, + target: { description: "Target CCR browser storage partition.", enum: ["browser", "browser-and-web-search"], type: "string" } + }), + name: "browser_chrome_login_import", + tags: ["browser", "automation", "chrome", "login", "import", "cookies", "localStorage"], + title: "Import Chrome Login State" + }, + { + description: "Read the status of a Chrome login import job created by browser_chrome_login_import.", + inputSchema: objectSchema({ + jobId: { description: "Chrome login import job id.", type: "string" } + }, ["jobId"]), + name: "browser_chrome_login_import_status", + tags: ["browser", "automation", "chrome", "login", "import", "status"], + title: "Chrome Login Import Status" + } + ]; +} + +function browserLegacyAliasTools(): McpTool[] { + return [ + { + description: "Open or focus the CCR built-in browser window. Optionally navigate the active tab to a URL.", + inputSchema: objectSchema({ + url: { description: "Optional URL or search query to load in the active tab.", type: "string" } + }), + name: "browser_open", + tags: ["browser", "automation", "open", "focus"], + title: "Open Browser" + }, + { + description: "List CCR built-in browser tabs, including active tab, URL, title, and loading state.", + inputSchema: objectSchema({}), + name: "browser_tabs", + tags: ["browser", "automation", "tabs"], + title: "List Browser Tabs" + }, + { + description: "Open a new CCR built-in browser tab. Optionally load a URL or search query.", + inputSchema: objectSchema({ + url: { description: "Optional URL or search query to load in the new tab.", type: "string" } + }), + name: "browser_tab_new", + tags: ["browser", "automation", "tab"], + title: "New Browser Tab" + }, + { + description: "Focus a CCR built-in browser tab by tabId.", + inputSchema: objectSchema({ + tabId: { description: "Tab id returned by browser_tabs or browser_tab_new.", type: "string" } + }, ["tabId"]), + name: "browser_tab_activate", + tags: ["browser", "automation", "tab", "focus"], + title: "Activate Browser Tab" + }, + { + description: "Close a CCR built-in browser tab by tabId.", + inputSchema: objectSchema({ + tabId: { description: "Tab id returned by browser_tabs or browser_tab_new.", type: "string" } + }, ["tabId"]), + name: "browser_tab_close", + tags: ["browser", "automation", "tab"], + title: "Close Browser Tab" + }, + { + description: "Navigate a CCR built-in browser tab to a URL or search query.", + inputSchema: objectSchema({ + tabId: { description: "Optional tab id. Defaults to the active tab.", type: "string" }, + url: { description: "URL or search query to load.", type: "string" } + }, ["url"]), + name: "browser_navigate", + tags: ["browser", "automation", "navigate", "url"], + title: "Navigate Browser" + }, + { + description: "Capture page text plus interactable element refs for browser automation. Use returned refs for browser_click, browser_type, browser_select, browser_press_key, or browser_scroll.", + inputSchema: objectSchema({ + maxElements: { description: "Maximum interactable elements to include.", maximum: 300, minimum: 1, type: "number" }, + maxText: { description: "Maximum page text characters to include.", maximum: 20000, minimum: 0, type: "number" }, + tabId: { description: "Optional tab id. Defaults to the active tab.", type: "string" } + }), + name: "browser_snapshot", + tags: ["browser", "automation", "snapshot", "accessibility", "dom", "page"], + title: "Browser Page Snapshot" + }, + { + description: "Click an element in the CCR built-in browser by ref, CSS selector, role, or visible text.", + inputSchema: objectSchema({ + tabId: { description: "Optional tab id. Defaults to the active tab.", type: "string" }, + target: targetSchema + }, ["target"]), + name: "browser_click", + tags: ["browser", "automation", "click", "element"], + title: "Click Browser Element" + }, + { + description: "Type text into an input, textarea, select-like textbox, or contenteditable element in the CCR built-in browser.", + inputSchema: objectSchema({ + replaceExisting: { description: "Replace existing text. Defaults to true.", type: "boolean" }, + tabId: { description: "Optional tab id. Defaults to the active tab.", type: "string" }, + target: targetSchema, + text: { description: "Text to type.", type: "string" } + }, ["target", "text"]), + name: "browser_type", + tags: ["browser", "automation", "type", "input", "form"], + title: "Type In Browser Element" + }, + { + description: "Choose an option in a select element by value or visible label.", + inputSchema: objectSchema({ + exact: { description: "Match label exactly instead of by substring.", type: "boolean" }, + label: { description: "Visible option label to choose.", type: "string" }, + tabId: { description: "Optional tab id. Defaults to the active tab.", type: "string" }, + target: targetSchema, + value: { description: "Option value to choose.", type: "string" } + }, ["target"]), + name: "browser_select", + tags: ["browser", "automation", "select", "form"], + title: "Select Browser Option" + }, + { + description: "Press a keyboard key in the CCR built-in browser. Optionally focus an element first.", + inputSchema: objectSchema({ + key: { description: "Electron keyCode, for example Enter, Tab, Escape, Backspace, ArrowDown.", type: "string" }, + tabId: { description: "Optional tab id. Defaults to the active tab.", type: "string" }, + target: targetSchema + }, ["key"]), + name: "browser_press_key", + tags: ["browser", "automation", "keyboard", "press"], + title: "Press Browser Key" + }, + { + description: "Scroll the page or a target element in the CCR built-in browser.", + inputSchema: objectSchema({ + deltaX: { description: "Horizontal scroll delta in pixels.", type: "number" }, + deltaY: { description: "Vertical scroll delta in pixels.", type: "number" }, + tabId: { description: "Optional tab id. Defaults to the active tab.", type: "string" }, + target: targetSchema + }), + name: "browser_scroll", + tags: ["browser", "automation", "scroll"], + title: "Scroll Browser" + }, + { + description: "Wait until a browser page condition is true: URL includes/matches, selector is visible, or text is visible.", + inputSchema: objectSchema({ + selector: { description: "CSS selector that must be visible.", type: "string" }, + tabId: { description: "Optional tab id. Defaults to the active tab.", type: "string" }, + text: { description: "Text that must be visible in document body.", type: "string" }, + timeoutMs: { description: "Maximum wait time in milliseconds.", maximum: 120000, minimum: 100, type: "number" }, + urlIncludes: { description: "Substring that must appear in the URL.", type: "string" }, + urlMatches: { description: "JavaScript regular expression that must match the URL.", type: "string" } + }), + name: "browser_wait_for", + tags: ["browser", "automation", "wait", "url", "selector", "text"], + title: "Wait For Browser Page" + }, + { + description: "Agentic-browser-compatible alias for browser_handoff_request. Request human help for login, verification, CAPTCHA, or other manual-only blockers.", + inputSchema: objectSchema({ + browserSessionId: { type: "string" }, + kind: { enum: ["login_required", "verification_code", "human_verification", "blocked", "other"], type: "string" }, + message: { type: "string" }, + reason: { type: "string" }, + tabId: { type: "string" } + }, ["reason", "kind"]), + name: "askHumanHelp", + tags: ["browser", "automation", "human", "handoff", "intervention"], + title: "Ask Human Help" + } + ]; +} + +class BrowserAutomationMcpService implements BrowserAutomationMcpIntegration { + private readonly sessions = new Map(); + private readonly subscriptions = new Map(); + + async handleBrowserAutomationMcpRequest(request: IncomingMessage, response: ServerResponse): Promise { + response.setHeader("MCP-Protocol-Version", protocolVersion); + const path = request.url ? new URL(request.url, "http://127.0.0.1").pathname : "/"; + + if (request.method === "GET" && (path === BROWSER_AUTOMATION_MCP_PATH || path === `${BROWSER_AUTOMATION_MCP_PATH}/`)) { + sendJson(response, 200, { + endpoint: BROWSER_AUTOMATION_MCP_PATH, + name: "ccr-browser-automation", + protocol: "mcp", + transport: "streamable-http" + }); + return; + } + + if (request.method !== "POST") { + sendJson(response, 405, { error: { message: "Browser automation MCP endpoint only supports GET and POST." } }); + return; + } + + let payload: unknown; + try { + payload = JSON.parse((await readRequestBody(request, maxMcpRequestBytes)).toString("utf8")) as unknown; + } catch (error) { + sendJson(response, 400, jsonRpcError(null, -32700, `Invalid JSON-RPC request: ${formatError(error)}`)); + return; + } + + const requests = Array.isArray(payload) ? payload : [payload]; + const responses = await Promise.all(requests.map((item) => this.handleJsonRpcRequest(item))); + const filtered = responses.filter((item): item is JsonRpcResponse => Boolean(item)); + if (filtered.length === 0) { + response.writeHead(204); + response.end(); + return; + } + + sendJson(response, 200, Array.isArray(payload) ? filtered : filtered[0]); + } + + async stopBrowserAutomationMcpServer(): Promise { + for (const subscription of this.subscriptions.values()) { + subscription.unsubscribe(); + } + this.subscriptions.clear(); + this.sessions.clear(); + // The gateway owns this MCP route. Browser tabs remain user-visible state and are + // intentionally not closed when the gateway restarts. + } + + private async handleJsonRpcRequest(payload: unknown): Promise { + if (!isRecord(payload)) { + return jsonRpcError(null, -32600, "JSON-RPC request must be an object."); + } + + const request = payload as JsonRpcRequest; + const id = request.id ?? null; + if (request.id === undefined && request.method?.startsWith("notifications/")) { + return undefined; + } + if (request.jsonrpc !== "2.0" || !request.method) { + return jsonRpcError(id, -32600, "Invalid JSON-RPC 2.0 request."); + } + + try { + switch (request.method) { + case "initialize": + return jsonRpcResult(id, { + capabilities: { + tools: {} + }, + protocolVersion, + serverInfo: { + name: "ccr-browser-automation", + title: "CCR Browser Automation", + version: "1.0.0" + } + }); + case "ping": + return jsonRpcResult(id, {}); + case "tools/list": + return jsonRpcResult(id, { tools: browserAutomationTools as unknown as JsonValue }); + case "tools/call": + return jsonRpcResult(id, await this.callTool(request.params) as unknown as JsonValue); + default: + return jsonRpcError(id, -32601, `Unsupported MCP method: ${request.method}`); + } + } catch (error) { + return jsonRpcError(id, -32603, formatError(error)); + } + } + + private async callTool(params: unknown): Promise { + if (!isRecord(params) || typeof params.name !== "string") { + throw new Error("tools/call params must include a tool name."); + } + const args = isRecord(params.arguments) ? params.arguments : {}; + try { + const result = await this.runTool(params.name, args); + return textResult(formatToolResult(params.name, result)); + } catch (error) { + return { + ...textResult(formatError(error)), + isError: true + }; + } + } + + private async runTool(name: string, args: Record): Promise { + switch (name) { + case "browser_session_open": + return await this.openSession(args); + case "browser_session_close": + return this.closeSession(args); + case "browser_tab_create": + return await this.createTab(args); + case "browser_tab_list": + await this.ensureBrowserOpen(); + return browserWindowState(); + case "browser_tab_activate": { + await this.ensureBrowserOpen(); + const session = this.resolveOptionalSession(args); + const tabId = readString(args.tabId) || session?.ref.tabId; + if (!tabId) { + throw new Error("browser_tab_activate requires tabId or session."); + } + const state = builtInBrowserService.selectAutomationTab(tabId); + return { + ...browserWindowState(state), + tab: summarizeTab(requiredTabState(state, tabId), state.activeTabId) + }; + } + case "browser_tab_close": { + await this.ensureBrowserOpen(); + const session = this.resolveOptionalSession(args); + this.assertCanMutate(session); + const tabId = readString(args.tabId) || session?.ref.tabId; + if (!tabId) { + throw new Error("browser_tab_close requires tabId or session."); + } + const state = builtInBrowserService.closeAutomationTab(tabId); + this.removeSessionsForTab(tabId); + return browserWindowState(state); + } + case "browser_navigate": { + await this.ensureBrowserOpen(); + const session = this.resolveOptionalSession(args); + this.assertCanMutate(session); + const tabId = readString(args.tabId) || session?.ref.tabId || builtInBrowserService.getAutomationState().activeTabId; + if (!tabId) { + throw new Error("browser_navigate could not resolve an active tab."); + } + const url = requiredString(args.url, "browser_navigate requires url."); + const waitUntil = normalizeWaitUntil(readString(args.waitUntil), "interactive"); + const readiness = await waitForReadiness( + builtInBrowserService.getAutomationWebContents(tabId), + waitUntil, + clampInteger(readNumber(args.timeoutMs) ?? defaultWaitTimeoutMs, 100, 120000), + () => builtInBrowserService.navigateAutomationTab(url, tabId), + url + ); + const state = builtInBrowserService.getAutomationState(); + const webContents = builtInBrowserService.getAutomationWebContents(tabId); + const handoff = await maybeRequestHandoffAfterNavigation(webContents, readiness, { + requestedUrl: url, + session: session?.ref, + tabId, + waitUntil + }); + return { + ...browserWindowState(state), + ...(handoff ? humanHelpResultFields(handoff) : {}), + session: session?.ref, + tab: summarizeTab(requiredTabState(state, tabId), state.activeTabId), + ...readiness + }; + } + case "browser_tab_go_back": { + await this.ensureBrowserOpen(); + const session = this.resolveOptionalSession(args); + this.assertCanMutate(session); + return browserWindowState(builtInBrowserService.goBackAutomationTab(readString(args.tabId) || session?.ref.tabId)); + } + case "browser_tab_go_forward": { + await this.ensureBrowserOpen(); + const session = this.resolveOptionalSession(args); + this.assertCanMutate(session); + return browserWindowState(builtInBrowserService.goForwardAutomationTab(readString(args.tabId) || session?.ref.tabId)); + } + case "browser_tab_reload": { + await this.ensureBrowserOpen(); + const session = this.resolveOptionalSession(args); + this.assertCanMutate(session); + return browserWindowState(builtInBrowserService.reloadAutomationTab(readString(args.tabId) || session?.ref.tabId)); + } + case "browser_ax_snapshot": { + await this.ensureBrowserOpen(); + const session = this.requireSession(args); + return await captureAxSnapshot( + builtInBrowserService.getAutomationWebContents(session.ref.tabId), + session.ref, + { + limit: clampInteger(readNumber(args.limit) ?? defaultAxSnapshotLimit, 1, 300), + rootAxNodeId: readString(args.rootAxNodeId), + scope: readString(args.scope) === "outline" ? "outline" : "full" + } + ); + } + case "browser_ax_query": { + await this.ensureBrowserOpen(); + const session = this.requireSession(args); + return await queryAxSnapshot( + builtInBrowserService.getAutomationWebContents(session.ref.tabId), + session.ref, + { + limit: clampInteger(readNumber(args.limit) ?? 50, 1, 300), + name: readString(args.name), + role: readString(args.role), + rootAxNodeId: readString(args.rootAxNodeId), + text: readString(args.text) + } + ); + } + case "browser_element_click": { + await this.ensureBrowserOpen(); + const session = this.requireSession(args); + this.assertCanMutate(session); + const webContents = builtInBrowserService.getAutomationWebContents(session.ref.tabId); + const result = await runTargetAction(webContents, "click", readTarget(args.target)); + return await pageActionResult(webContents, session.ref, "click", result); + } + case "browser_element_input": { + await this.ensureBrowserOpen(); + const session = this.requireSession(args); + this.assertCanMutate(session); + const webContents = builtInBrowserService.getAutomationWebContents(session.ref.tabId); + const result = await runTargetAction(webContents, "type", readTarget(args.target), { + replaceExisting: args.replace !== false, + text: typeof args.text === "string" ? args.text : "" + }); + return await pageActionResult(webContents, session.ref, "input", result); + } + case "browser_element_select": { + await this.ensureBrowserOpen(); + const session = this.requireSession(args); + this.assertCanMutate(session); + const value = requiredString(args.value, "browser_element_select requires value."); + const webContents = builtInBrowserService.getAutomationWebContents(session.ref.tabId); + const result = await runTargetAction(webContents, "select", readTarget(args.target), { + exact: args.exact === true, + label: value, + value + }); + return await pageActionResult(webContents, session.ref, "select", result); + } + case "browser_element_press": { + await this.ensureBrowserOpen(); + const session = this.requireSession(args); + this.assertCanMutate(session); + const webContents = builtInBrowserService.getAutomationWebContents(session.ref.tabId); + const result = await pressKey( + webContents, + requiredString(args.key, "browser_element_press requires key."), + isRecord(args.target) ? readTarget(args.target) : undefined + ); + return await pageActionResult(webContents, session.ref, "press", result); + } + case "browser_element_scroll": { + await this.ensureBrowserOpen(); + const session = this.requireSession(args); + this.assertCanMutate(session); + const amount = Math.abs(readNumber(args.amount) ?? 700); + const direction = readString(args.direction) === "up" ? "up" : "down"; + const webContents = builtInBrowserService.getAutomationWebContents(session.ref.tabId); + const result = await runTargetAction( + webContents, + "scroll", + isRecord(args.target) ? readTarget(args.target) : undefined, + { + deltaX: 0, + deltaY: direction === "up" ? -amount : amount + } + ); + return await pageActionResult(webContents, session.ref, "scroll", result); + } + case "browser_events_subscribe": + await this.ensureBrowserOpen(); + return this.subscribeEvents(args); + case "browser_events_read": + return this.readEvents(args); + case "browser_events_await": + return await this.awaitEvents(args); + case "browser_events_unsubscribe": + return this.unsubscribeEvents(args); + case "browser_dialog_handle": { + await this.ensureBrowserOpen(); + const session = this.requireSession(args); + this.assertCanMutate(session); + const webContents = builtInBrowserService.getAutomationWebContents(session.ref.tabId); + return await handleJavaScriptDialog(webContents, args.accept === true, readString(args.promptText), session.ref); + } + case "browser_handoff_request": + case "askHumanHelp": + await this.ensureBrowserOpen(); + return this.requestHandoff(args); + case "browser_handoff_status": + await this.ensureBrowserOpen(); + return { + handoff: builtInBrowserService.getAutomationState().automationHandoff, + state: browserWindowState() + }; + case "browser_handoff_wait": + await this.ensureBrowserOpen(); + return await this.waitForHandoff(args); + case "browser_handoff_clear": + return { + handoff: undefined, + state: builtInBrowserService.resolveAutomationHandoff(readString(args.status) === "dismissed" ? "dismissed" : "completed") + }; + case "browser_chrome_login_import": { + await this.ensureBrowserOpen(); + const session = this.resolveOptionalSession(args); + const domains = resolveChromeLoginImportDomains(args, session?.ref.tabId); + const job = await chromeLoginImportService.createJob({ + domains, + openConfirmationPage: args.openConfirmationPage !== false, + target: readString(args.target) === "browser-and-web-search" ? "browser-and-web-search" : "browser" + }); + return { + humanHelpRequired: true, + job, + nextAction: "user_confirm_chrome_import", + state: browserWindowState(), + summary: `Opened Chrome login import confirmation for ${job.domains.join(", ")}.` + }; + } + case "browser_chrome_login_import_status": { + const jobId = requiredString(args.jobId, "browser_chrome_login_import_status requires jobId."); + const job = chromeLoginImportService.getJob(jobId); + if (!job) { + throw new Error(`Chrome login import job was not found: ${jobId}`); + } + return { job }; + } + case "browser_open": { + const state = await this.ensureBrowserVisible(); + const url = readString(args.url); + return url ? await builtInBrowserService.navigateAutomationTab(url, state.activeTabId) : builtInBrowserService.getAutomationState(); + } + case "browser_tabs": + await this.ensureBrowserOpen(); + return builtInBrowserService.getAutomationState(); + case "browser_tab_new": { + await this.ensureBrowserOpen(); + const tab = builtInBrowserService.createAutomationTab(readString(args.url) || undefined); + return { state: builtInBrowserService.getAutomationState(), tab }; + } + case "browser_snapshot": + await this.ensureBrowserOpen(); + return await captureSnapshotWithHandoff( + builtInBrowserService.getAutomationWebContents(readString(args.tabId)), + readString(args.tabId), + { + maxElements: clampInteger(readNumber(args.maxElements) ?? defaultSnapshotMaxElements, 1, 300), + maxText: clampInteger(readNumber(args.maxText) ?? defaultSnapshotMaxText, 0, 20000) + } + ); + case "browser_click": + await this.ensureBrowserOpen(); + return await runTargetAction( + builtInBrowserService.getAutomationWebContents(readString(args.tabId)), + "click", + readTarget(args.target) + ); + case "browser_type": + await this.ensureBrowserOpen(); + return await runTargetAction( + builtInBrowserService.getAutomationWebContents(readString(args.tabId)), + "type", + readTarget(args.target), + { + replaceExisting: args.replaceExisting !== false, + text: typeof args.text === "string" ? args.text : "" + } + ); + case "browser_select": + await this.ensureBrowserOpen(); + return await runTargetAction( + builtInBrowserService.getAutomationWebContents(readString(args.tabId)), + "select", + readTarget(args.target), + { + exact: args.exact === true, + label: readString(args.label), + value: readString(args.value) + } + ); + case "browser_press_key": + await this.ensureBrowserOpen(); + return await pressKey( + builtInBrowserService.getAutomationWebContents(readString(args.tabId)), + requiredString(args.key, "browser_press_key requires key."), + isRecord(args.target) ? readTarget(args.target) : undefined + ); + case "browser_scroll": + await this.ensureBrowserOpen(); + return await runTargetAction( + builtInBrowserService.getAutomationWebContents(readString(args.tabId)), + "scroll", + isRecord(args.target) ? readTarget(args.target) : undefined, + { + deltaX: readNumber(args.deltaX) ?? 0, + deltaY: readNumber(args.deltaY) ?? 700 + } + ); + case "browser_wait_for": + await this.ensureBrowserOpen(); + return await waitForPageCondition( + builtInBrowserService.getAutomationWebContents(readString(args.tabId)), + { + selector: readString(args.selector), + text: readString(args.text), + timeoutMs: clampInteger(readNumber(args.timeoutMs) ?? defaultWaitTimeoutMs, 100, 120000), + urlIncludes: readString(args.urlIncludes), + urlMatches: readString(args.urlMatches) + } + ); + default: + throw new Error(`Unknown browser automation tool: ${name}`); + } + } + + private async ensureBrowserOpen(): Promise { + await builtInBrowserService.openHidden(await loadAppConfig()); + return builtInBrowserService.getAutomationState(); + } + + private async ensureBrowserVisible(): Promise { + await builtInBrowserService.open(await loadAppConfig()); + return builtInBrowserService.getAutomationState(); + } + + private async openSession(args: Record): Promise { + let state = await this.ensureBrowserOpen(); + const url = readString(args.url); + const requestedTabId = readString(args.tabId); + const previousActiveTabId = state.activeTabId; + let tabId = requestedTabId; + let createdTab = false; + + if (tabId && !state.tabs.some((tab) => tab.id === tabId)) { + throw new Error(`Browser tab was not found: ${tabId}`); + } + + if (!tabId && url) { + const tab = builtInBrowserService.createAutomationTab(); + tabId = tab.id; + createdTab = true; + } else if (!tabId) { + tabId = state.activeTabId || builtInBrowserService.createAutomationTab().id; + createdTab = !state.activeTabId; + } else if (state.activeTabId !== tabId) { + builtInBrowserService.selectAutomationTab(tabId); + } + + if (!tabId) { + throw new Error("Unable to resolve browser tab for automation session."); + } + + const waitUntil = normalizeWaitUntil(readString(args.waitUntil), url ? "interactive" : "none"); + const timeoutMs = clampInteger(readNumber(args.timeoutMs) ?? defaultWaitTimeoutMs, 100, 120000); + const webContents = builtInBrowserService.getAutomationWebContents(tabId); + const readiness = await waitForReadiness( + webContents, + waitUntil, + timeoutMs, + url ? () => builtInBrowserService.navigateAutomationTab(url, tabId) : undefined, + url + ); + state = builtInBrowserService.getAutomationState(); + const tab = requiredTabState(state, tabId); + const ref: BrowserSessionRef = { + sessionId: randomUUID(), + tabId, + ...(readString(args.userId) ? { userId: readString(args.userId) } : {}) + }; + const attached: AttachedSession = { + attachedAt: Date.now(), + leaseId: randomUUID(), + observeOnly: args.observeOnly === true, + ref + }; + this.sessions.set(ref.sessionId, attached); + const handoff = await maybeRequestHandoffAfterNavigation(webContents, readiness, { + requestedUrl: url, + session: ref, + tabId, + waitUntil + }); + + return { + session: ref, + attachedAt: attached.attachedAt, + createdTab, + ...(handoff ? humanHelpResultFields(handoff) : {}), + matched: readiness.matched, + matchedEvent: readiness.matchedEvent, + ...(readiness.navigationError ? { navigationError: readiness.navigationError } : {}), + previousActiveTabId, + readinessUrl: readiness.readinessUrl, + tabId, + timedOut: readiness.timedOut, + title: tab.title, + url: tab.url, + waitUntil, + windowId: builtInBrowserService.getAutomationWindowId() + }; + } + + private closeSession(args: Record): unknown { + const session = this.requireSession(args); + this.sessions.delete(session.ref.sessionId); + for (const [subscriptionId, subscription] of this.subscriptions) { + if (subscription.ref?.sessionId === session.ref.sessionId) { + subscription.unsubscribe(); + this.subscriptions.delete(subscriptionId); + } + } + return { success: true }; + } + + private async createTab(args: Record): Promise { + const state = await this.ensureBrowserOpen(); + const session = this.resolveOptionalSession(args); + this.assertCanMutate(session); + const previousActiveTabId = state.activeTabId; + const tab = builtInBrowserService.createAutomationTab(readString(args.url) || undefined); + if (args.activate === false && previousActiveTabId) { + builtInBrowserService.selectAutomationTab(previousActiveTabId); + } + const nextState = builtInBrowserService.getAutomationState(); + return summarizeTab(requiredTabState(nextState, tab.id), nextState.activeTabId); + } + + private requireSession(args: Record): AttachedSession { + const session = this.resolveOptionalSession(args); + if (!session) { + throw new Error("A valid browser automation session is required."); + } + return session; + } + + private resolveOptionalSession(args: Record): AttachedSession | undefined { + const ref = readSessionRef(args.session); + if (!ref) { + return undefined; + } + const existing = this.sessions.get(ref.sessionId); + if (existing) { + if (existing.ref.tabId !== ref.tabId) { + throw new Error("Browser automation session tabId does not match the attached session."); + } + return existing; + } + const state = builtInBrowserService.getAutomationState(); + if (!state.tabs.some((tab) => tab.id === ref.tabId)) { + throw new Error(`Browser automation session tab was not found: ${ref.tabId}`); + } + const restored: AttachedSession = { + attachedAt: Date.now(), + leaseId: randomUUID(), + observeOnly: false, + ref + }; + this.sessions.set(ref.sessionId, restored); + return restored; + } + + private assertCanMutate(session?: AttachedSession): void { + if (session?.observeOnly) { + throw new Error("This browser automation session is observeOnly and cannot mutate browser state."); + } + } + + private removeSessionsForTab(tabId: string): void { + for (const [sessionId, session] of this.sessions) { + if (session.ref.tabId === tabId) { + this.sessions.delete(sessionId); + } + } + for (const [subscriptionId, subscription] of this.subscriptions) { + if (subscription.ref?.tabId === tabId) { + subscription.unsubscribe(); + this.subscriptions.delete(subscriptionId); + } + } + } + + private subscribeEvents(args: Record): unknown { + const session = this.requireSession(args); + const channels = normalizeEventChannels(readStringArray(args.channels)); + const subscription: EventSubscription = { + channels, + dropped: false, + events: [], + ref: session.ref, + redactTextInput: args.redactTextInput !== false, + sampleMouseMove: args.sampleMouseMove === true, + startedAt: Date.now(), + subscriptionId: randomUUID(), + unsubscribe: () => undefined + }; + const listener = (event: BrowserAutomationEvent) => { + if (!matchesSubscriptionEvent(subscription, event)) { + return; + } + subscription.events.push(event); + if (subscription.events.length > maxSubscriptionEvents) { + subscription.events.splice(0, subscription.events.length - maxSubscriptionEvents); + subscription.dropped = true; + } + }; + subscription.unsubscribe = builtInBrowserService.subscribeAutomationEvents(listener, { + replayRecentMs: automationEventReplayMs + }); + this.subscriptions.set(subscription.subscriptionId, subscription); + + return { + subscription: subscriptionDescriptor(subscription) + }; + } + + private readEvents(args: Record): unknown { + const subscription = this.requireSubscription(args); + const limit = clampInteger(readNumber(args.limit) ?? 100, 1, 200); + const cursorSeq = readCursorSeq(args.cursor); + const events = eventsAfterCursor(subscription, cursorSeq).slice(0, limit); + return { + dropped: subscription.dropped || cursorDropped(subscription, cursorSeq), + events, + nextCursor: makeEventCursor(subscription, events.at(-1)?.seq ?? cursorSeq), + subscriptionId: subscription.subscriptionId + }; + } + + private async awaitEvents(args: Record): Promise { + const subscription = this.requireSubscription(args); + const timeoutMs = clampInteger(readNumber(args.timeoutMs) ?? defaultEventAwaitTimeoutMs, 100, 120000); + const maxEvents = clampInteger(readNumber(args.maxEvents) ?? 10, 1, 50); + const coalesceMs = clampInteger(readNumber(args.coalesceMs) ?? 100, 0, 2000); + const cursorSeq = readCursorSeq(args.cursor); + const deadline = Date.now() + timeoutMs; + let matched: BrowserAutomationEvent[] = []; + + while (Date.now() <= deadline) { + matched = eventsAfterCursor(subscription, cursorSeq) + .filter((event) => matchesAwaitFilter(event, args)) + .slice(0, maxEvents); + if (matched.length > 0) { + if (coalesceMs > 0) { + await sleep(coalesceMs); + matched = eventsAfterCursor(subscription, cursorSeq) + .filter((event) => matchesAwaitFilter(event, args)) + .slice(0, maxEvents); + } + break; + } + await sleep(100); + } + + return { + dropped: subscription.dropped || cursorDropped(subscription, cursorSeq), + event: matched[0], + events: matched, + matched: matched.length > 0, + nextCursor: makeEventCursor(subscription, matched.at(-1)?.seq ?? cursorSeq), + subscriptionId: subscription.subscriptionId, + timedOut: matched.length === 0 + }; + } + + private unsubscribeEvents(args: Record): unknown { + const subscription = this.requireSubscription(args); + subscription.unsubscribe(); + this.subscriptions.delete(subscription.subscriptionId); + return { + ok: true, + subscriptionId: subscription.subscriptionId + }; + } + + private requireSubscription(args: Record): EventSubscription { + const subscriptionId = requiredString(args.subscriptionId, "Browser event subscriptionId is required."); + const subscription = this.subscriptions.get(subscriptionId); + if (!subscription) { + throw new Error(`Browser event subscription was not found: ${subscriptionId}`); + } + return subscription; + } + + private requestHandoff(args: Record): unknown { + const session = this.resolveOptionalSession(args); + const sessionId = session?.ref.sessionId || readString(args.browserSessionId); + const tabId = readString(args.tabId) || session?.ref.tabId; + const handoff = builtInBrowserService.requestAutomationHandoff({ + kind: readHandoffKind(args.kind), + message: readString(args.message), + reason: requiredString(args.reason, "browser_handoff_request requires reason."), + sessionId, + tabId + }); + return { + ...humanHelpResultFields(handoff), + state: browserWindowState() + }; + } + + private async waitForHandoff(args: Record): Promise { + const session = this.resolveOptionalSession(args); + const handoffId = readString(args.handoffId); + const tabId = readString(args.tabId) || session?.ref.tabId; + const timeoutMs = clampInteger(readNumber(args.timeoutMs) ?? 300000, 100, 600000); + + const current = builtInBrowserService.getAutomationState().automationHandoff; + if (!handoffMatches(current, { handoffId, tabId })) { + const recentResolution = findRecentHandoffResolution({ handoffId, tabId }); + if (recentResolution) { + return handoffResolutionResult(recentResolution, undefined, false); + } + return { + handoff: current, + matched: false, + reason: current ? "A browser handoff is pending, but it does not match the requested handoffId/tabId." : "No browser handoff is currently pending.", + state: browserWindowState(), + timedOut: false + }; + } + + return await new Promise((resolve) => { + let settled = false; + const finish = (result: Record) => { + if (settled) { + return; + } + settled = true; + clearTimeout(timer); + unsubscribe(); + resolve(result); + }; + const unsubscribe = builtInBrowserService.subscribeAutomationEvents((event) => { + if (event.kind !== "handoff.completed" && event.kind !== "handoff.dismissed") { + return; + } + if (handoffId && event.handoffId !== handoffId) { + return; + } + if (tabId && event.tabId !== tabId) { + return; + } + finish(handoffResolutionResult(event, current, false)); + }); + const timer = setTimeout(() => { + finish({ + handoff: current, + matched: false, + state: browserWindowState(), + timedOut: true + }); + }, timeoutMs); + }); + } +} + +export const browserAutomationMcpService = new BrowserAutomationMcpService(); + +function browserWindowState(state = builtInBrowserService.getAutomationState()): Record { + return { + activeTabId: state.activeTabId, + tabs: state.tabs.map((tab) => summarizeTab(tab, state.activeTabId)), + windowId: builtInBrowserService.getAutomationWindowId() + }; +} + +function resolveChromeLoginImportDomains(args: Record, fallbackTabId?: string): string[] { + const explicit = uniqueStrings([ + ...readStringArray(args.domains), + ...(readString(args.domain) ? [readString(args.domain) as string] : []) + ].map(normalizeChromeLoginImportDomain).filter((domain): domain is string => Boolean(domain))); + if (explicit.length > 0) { + return explicit; + } + + const state = builtInBrowserService.getAutomationState(); + const tabId = readString(args.tabId) || fallbackTabId || state.activeTabId; + const tab = state.tabs.find((candidate) => candidate.id === tabId) || state.tabs.find((candidate) => candidate.id === state.activeTabId); + const domain = normalizeChromeLoginImportDomain(tab?.url); + if (!domain) { + throw new Error("browser_chrome_login_import requires domains or an active http(s) tab."); + } + return [domain]; +} + +function normalizeChromeLoginImportDomain(value: unknown): string | undefined { + const raw = readString(value)?.toLowerCase(); + if (!raw) { + return undefined; + } + try { + const url = new URL(raw.includes("://") ? raw : `https://${raw}`); + return url.hostname.replace(/^\*\./, "").replace(/^\./, ""); + } catch { + const domain = raw.replace(/^\*\./, "").replace(/^\./, "").split("/")[0]; + return domain && !domain.includes(" ") ? domain : undefined; + } +} + +function summarizeTab(tab: BuiltInBrowserTabState, activeTabId?: string): Record { + return { + canGoBack: tab.canGoBack, + canGoForward: tab.canGoForward, + displayUrl: tab.url, + isActive: tab.id === activeTabId, + isLoading: tab.isLoading, + tabId: tab.id, + title: tab.title, + url: tab.url, + windowId: builtInBrowserService.getAutomationWindowId() + }; +} + +function requiredTabState(state: BuiltInBrowserState, tabId: string): BuiltInBrowserTabState { + const tab = state.tabs.find((candidate) => candidate.id === tabId); + if (!tab) { + throw new Error(`Browser tab was not found: ${tabId}`); + } + return tab; +} + +function readSessionRef(value: unknown): BrowserSessionRef | undefined { + if (!isRecord(value)) { + return undefined; + } + const sessionId = readString(value.sessionId); + const tabId = readString(value.tabId); + if (!sessionId || !tabId) { + return undefined; + } + return { + sessionId, + tabId, + ...(readString(value.frameId) ? { frameId: readString(value.frameId) } : {}), + ...(readString(value.userId) ? { userId: readString(value.userId) } : {}) + }; +} + +async function pageActionResult( + webContents: WebContents, + session: BrowserSessionRef, + action: string, + result: unknown +): Promise> { + const handoff = await maybeRequestPageHandoff(webContents, { + session, + tabId: session.tabId + }); + return { + action, + ...(handoff ? humanHelpResultFields(handoff) : {}), + result, + session, + title: await webContents.getTitle(), + url: webContents.getURL() + }; +} + +async function captureAxSnapshot( + webContents: WebContents, + session: BrowserSessionRef, + options: { limit: number; rootAxNodeId?: string; scope: string } +): Promise { + const snapshot = await captureSnapshot(webContents, { + maxElements: options.limit, + maxText: options.scope === "outline" ? 0 : defaultSnapshotMaxText + }); + const record = isRecord(snapshot) ? snapshot : {}; + const title = readString(record.title) || await webContents.getTitle(); + const url = readString(record.url) || webContents.getURL(); + const elements = Array.isArray(record.elements) ? record.elements.filter(isRecord) : []; + const elementNodes = elements.map((element, index) => axNodeFromSnapshotElement(element, index)); + const rootNode = { + axNodeId: "document", + childAxNodeIds: elementNodes.map((node) => String(node.axNodeId)), + ignored: false, + name: title || url, + role: "document", + url + }; + let nodes: Array> = [rootNode, ...elementNodes]; + if (options.rootAxNodeId && options.rootAxNodeId !== "document") { + nodes = nodes.filter((node) => node.axNodeId === options.rootAxNodeId || node.ref === options.rootAxNodeId); + } + const result: AxSnapshotResult = { + nodes: nodes.slice(0, options.limit), + scope: options.scope, + session, + title, + url + }; + const handoff = await maybeRequestPageHandoff(webContents, { + session, + snapshot, + tabId: session.tabId + }); + return handoff ? { ...result, ...humanHelpResultFields(handoff) } : result; +} + +async function queryAxSnapshot( + webContents: WebContents, + session: BrowserSessionRef, + options: { limit: number; name?: string; role?: string; rootAxNodeId?: string; text?: string } +): Promise> { + const snapshot = await captureAxSnapshot(webContents, session, { + limit: 300, + rootAxNodeId: options.rootAxNodeId, + scope: "outline" + }); + const role = options.role?.toLowerCase(); + const name = options.name?.toLowerCase(); + const text = options.text?.toLowerCase(); + const matches = snapshot.nodes + .filter((node) => node.role !== "document") + .filter((node) => { + if (role && String(node.role || "").toLowerCase() !== role) { + return false; + } + if (name && !String(node.name || "").toLowerCase().includes(name)) { + return false; + } + if (text) { + const haystack = [ + node.name, + node.value, + node.description, + node.text, + node.placeholder + ].join(" ").toLowerCase(); + if (!haystack.includes(text)) { + return false; + } + } + return true; + }) + .slice(0, options.limit); + return { + ...(snapshot.handoff ? humanHelpResultFields(snapshot.handoff) : {}), + matches, + session, + title: snapshot.title, + url: snapshot.url + }; +} + +function axNodeFromSnapshotElement(element: Record, index: number): Record { + const ref = readString(element.ref) || `element-${index}`; + const text = readString(element.text); + const name = readString(element.name) || text || readString(element.placeholder) || ref; + const description = text && text !== name ? text : undefined; + return { + axNodeId: ref, + backendNodeId: readNumber(element.backendNodeId), + checked: element.checked === true ? true : undefined, + description, + disabled: element.disabled === true ? true : undefined, + href: readString(element.href), + ignored: false, + index, + name, + placeholder: readString(element.placeholder), + rect: isRecord(element.rect) ? element.rect : undefined, + ref, + role: readString(element.role) || readString(element.tag) || "generic", + tag: readString(element.tag), + text: description, + value: readString(element.value) + }; +} + +async function waitForReadiness( + webContents: WebContents, + waitUntil: string, + timeoutMs: number, + requestNavigation?: () => Promise, + expectedUrl?: string +): Promise { + if (waitUntil === "none") { + if (requestNavigation) { + await requestNavigation(); + } + return { matched: true, matchedEvent: "none", readinessUrl: safeWebContentsUrl(webContents), timedOut: false }; + } + if (webContents.isDestroyed()) { + return { matched: false, matchedEvent: "destroyed", timedOut: false }; + } + if (requestNavigation) { + const readiness = waitForNextReadinessEvent(webContents, waitUntil, timeoutMs, { + expectedUrl: normalizeNavigationUrlForMatch(expectedUrl), + previousUrl: safeWebContentsUrl(webContents) + }); + try { + await requestNavigation(); + } catch (error) { + return navigationFailureResult("navigation_request_failed", { + errorDescription: formatError(error), + url: normalizeNavigationUrlForMatch(expectedUrl) || safeWebContentsUrl(webContents) + }); + } + return await readiness; + } + + const deadline = Date.now() + timeoutMs; + while (Date.now() <= deadline) { + if (webContents.isDestroyed()) { + return { matched: false, matchedEvent: "destroyed", timedOut: false }; + } + + if (!webContents.isLoading()) { + const readinessUrl = safeWebContentsUrl(webContents); + if (isChromiumErrorPage(readinessUrl)) { + return navigationFailureResult("chrome-error", { + errorDescription: "Chromium displayed an internal error page.", + url: readinessUrl + }); + } + if (waitUntil === "interactive") { + const interactive = await interactiveReadinessResult(webContents, readinessUrl); + if (interactive) { + return interactive; + } + } + if (waitUntil === "domcontentloaded") { + return { matched: true, matchedEvent: "domcontentloaded", readinessUrl, timedOut: false }; + } + if (waitUntil === "load") { + return { matched: true, matchedEvent: "load", readinessUrl, timedOut: false }; + } + if (waitUntil === "network_idle") { + const idle = await waitForNoLoadingStart(webContents, Math.min(500, Math.max(0, deadline - Date.now()))); + if (idle && !webContents.isDestroyed() && !webContents.isLoading()) { + return { matched: true, matchedEvent: "network_idle", readinessUrl: safeWebContentsUrl(webContents), timedOut: false }; + } + } + } + + const event = await waitForReadinessEvent(webContents, Math.max(0, deadline - Date.now())); + if (event.type === "timeout") { + break; + } + if (event.type === "destroyed") { + return { matched: false, matchedEvent: "destroyed", timedOut: false }; + } + if (event.type === "did-fail-load" && event.errorCode !== -3) { + return navigationFailureResult("did-fail-load", event); + } + const eventUrl = event.url || safeWebContentsUrl(webContents); + if (isChromiumErrorPage(eventUrl)) { + return navigationFailureResult("chrome-error", { + errorDescription: "Chromium displayed an internal error page.", + url: eventUrl + }); + } + if (waitUntil === "interactive" && isInteractiveReadinessEvent(event)) { + const interactive = await interactiveReadinessResult(webContents, eventUrl); + if (interactive) { + return interactive; + } + } + if (waitUntil === "domcontentloaded" && event.type === "dom-ready") { + return { matched: true, matchedEvent: "domcontentloaded", readinessUrl: eventUrl, timedOut: false }; + } + if (waitUntil === "load" && (event.type === "did-finish-load" || event.type === "did-stop-loading") && !webContents.isDestroyed() && !webContents.isLoading()) { + return { matched: true, matchedEvent: "load", readinessUrl: eventUrl, timedOut: false }; + } + if (waitUntil === "network_idle" && (event.type === "did-finish-load" || event.type === "did-stop-loading") && !webContents.isDestroyed() && !webContents.isLoading()) { + const idle = await waitForNoLoadingStart(webContents, Math.min(500, Math.max(0, deadline - Date.now()))); + if (idle && !webContents.isDestroyed() && !webContents.isLoading()) { + return { matched: true, matchedEvent: "network_idle", readinessUrl: safeWebContentsUrl(webContents), timedOut: false }; + } + } + } + if (waitUntil === "interactive" || waitUntil === "network_idle") { + const fallback = await interactiveReadinessResult( + webContents, + undefined, + waitUntil === "network_idle" ? "interactive_after_network_idle_timeout" : "interactive" + ); + if (fallback) { + return fallback; + } + } + return { + matched: false, + matchedEvent: webContents.isDestroyed() ? "destroyed" : undefined, + timedOut: !webContents.isDestroyed() + }; +} + +async function waitForNextReadinessEvent( + webContents: WebContents, + waitUntil: string, + timeoutMs: number, + context: NavigationReadinessContext +): Promise { + const deadline = Date.now() + timeoutMs; + let navigationStarted = false; + let lastNavigationUrl: string | undefined; + + while (Date.now() <= deadline) { + if (webContents.isDestroyed()) { + return { matched: false, matchedEvent: "destroyed", timedOut: false }; + } + + const event = await waitForReadinessEvent(webContents, Math.max(0, deadline - Date.now())); + if (event.type === "timeout") { + break; + } + if (event.type === "destroyed") { + return { matched: false, matchedEvent: "destroyed", timedOut: false }; + } + + const eventUrl = event.url || safeWebContentsUrl(webContents); + if (isNavigationStartEvent(event)) { + if (event.isMainFrame !== false && isRelevantNavigationUrl(eventUrl, context)) { + navigationStarted = true; + lastNavigationUrl = eventUrl; + } + continue; + } + + if (event.type === "did-fail-load") { + if (event.errorCode === -3) { + continue; + } + if (event.isMainFrame !== false && (navigationStarted || isRelevantNavigationUrl(eventUrl, context))) { + return navigationFailureResult("did-fail-load", event); + } + continue; + } + + if (!navigationStarted) { + continue; + } + + if (isChromiumErrorPage(eventUrl)) { + return navigationFailureResult("chrome-error", { + errorDescription: "Chromium displayed an internal error page.", + url: eventUrl + }); + } + if (waitUntil === "interactive" && isInteractiveReadinessEvent(event)) { + const interactive = await interactiveReadinessResult(webContents, eventUrl || lastNavigationUrl); + if (interactive) { + return interactive; + } + } + if (waitUntil === "domcontentloaded" && event.type === "dom-ready") { + return { matched: true, matchedEvent: "domcontentloaded", readinessUrl: eventUrl || lastNavigationUrl, timedOut: false }; + } + if (waitUntil === "load" && (event.type === "did-finish-load" || event.type === "did-stop-loading") && !webContents.isDestroyed() && !webContents.isLoading()) { + return { matched: true, matchedEvent: "load", readinessUrl: eventUrl || lastNavigationUrl, timedOut: false }; + } + if (waitUntil === "network_idle" && (event.type === "did-finish-load" || event.type === "did-stop-loading") && !webContents.isDestroyed() && !webContents.isLoading()) { + const idle = await waitForNoLoadingStart(webContents, Math.min(500, Math.max(0, deadline - Date.now()))); + if (idle && !webContents.isDestroyed() && !webContents.isLoading()) { + return { matched: true, matchedEvent: "network_idle", readinessUrl: safeWebContentsUrl(webContents) || lastNavigationUrl, timedOut: false }; + } + } + } + if (waitUntil === "interactive" || waitUntil === "network_idle") { + const fallback = await interactiveReadinessResult( + webContents, + lastNavigationUrl, + waitUntil === "network_idle" ? "interactive_after_network_idle_timeout" : "interactive" + ); + if (fallback && (navigationStarted || isRelevantNavigationUrl(fallback.readinessUrl, context))) { + return fallback; + } + } + return { + matched: false, + matchedEvent: webContents.isDestroyed() ? "destroyed" : undefined, + timedOut: !webContents.isDestroyed() + }; +} + +function waitForReadinessEvent(webContents: WebContents, timeoutMs: number): Promise { + return new Promise((resolve) => { + let settled = false; + const finish = (event: ReadinessEvent) => { + if (settled) { + return; + } + settled = true; + clearTimeout(timer); + webContents.off("did-navigate", onDidNavigate); + webContents.off("did-redirect-navigation", onDidRedirectNavigation); + webContents.off("did-fail-load", onDidFailLoad); + webContents.off("did-finish-load", onDidFinishLoad); + webContents.off("did-start-navigation", onDidStartNavigation); + webContents.off("did-start-loading", onDidStartLoading); + webContents.off("did-stop-loading", onDidStopLoading); + webContents.off("dom-ready", onDomReady); + webContents.off("destroyed", onDestroyed); + resolve(event); + }; + const onDidFailLoad = ( + _event: ElectronEvent, + errorCode: number, + errorDescription: string, + validatedUrl: string, + isMainFrame?: boolean + ) => finish({ + errorCode, + errorDescription, + isMainFrame, + type: "did-fail-load", + url: validatedUrl || safeWebContentsUrl(webContents) + }); + const onDidFinishLoad = () => finish({ type: "did-finish-load", url: safeWebContentsUrl(webContents) }); + const onDidNavigate = (_event: ElectronEvent, url: string) => finish({ isMainFrame: true, type: "did-navigate", url }); + const onDidRedirectNavigation = (_event: ElectronEvent, url: string, isInPlace?: boolean, isMainFrame?: boolean) => { + finish({ isMainFrame, type: isInPlace ? "did-navigate-in-page" : "did-redirect-navigation", url }); + }; + const onDidStartNavigation = (_event: ElectronEvent, url: string, isInPlace?: boolean, isMainFrame?: boolean) => { + finish({ isMainFrame, type: isInPlace ? "did-navigate-in-page" : "did-start-navigation", url }); + }; + const onDidStartLoading = () => finish({ type: "did-start-loading", url: safeWebContentsUrl(webContents) }); + const onDidStopLoading = () => finish({ type: "did-stop-loading", url: safeWebContentsUrl(webContents) }); + const onDomReady = () => finish({ type: "dom-ready", url: safeWebContentsUrl(webContents) }); + const onDestroyed = () => finish({ type: "destroyed" }); + const timer = setTimeout(() => finish({ type: "timeout" }), Math.max(0, timeoutMs)); + + webContents.once("did-navigate", onDidNavigate); + webContents.once("did-redirect-navigation", onDidRedirectNavigation); + webContents.once("did-fail-load", onDidFailLoad); + webContents.once("did-finish-load", onDidFinishLoad); + webContents.once("did-start-navigation", onDidStartNavigation); + webContents.once("did-start-loading", onDidStartLoading); + webContents.once("did-stop-loading", onDidStopLoading); + webContents.once("dom-ready", onDomReady); + webContents.once("destroyed", onDestroyed); + }); +} + +function waitForNoLoadingStart(webContents: WebContents, timeoutMs: number): Promise { + return new Promise((resolve) => { + let settled = false; + const finish = (idle: boolean) => { + if (settled) { + return; + } + settled = true; + clearTimeout(timer); + webContents.off("did-start-loading", onDidStartLoading); + webContents.off("destroyed", onDestroyed); + resolve(idle); + }; + const onDidStartLoading = () => finish(false); + const onDestroyed = () => finish(false); + const timer = setTimeout(() => finish(true), Math.max(0, timeoutMs)); + webContents.once("did-start-loading", onDidStartLoading); + webContents.once("destroyed", onDestroyed); + }); +} + +async function interactiveReadinessResult( + webContents: WebContents, + fallbackUrl?: string, + matchedEvent = "interactive" +): Promise { + if (webContents.isDestroyed()) { + return undefined; + } + const readinessUrl = safeWebContentsUrl(webContents) || fallbackUrl; + if (!isUsableInteractiveUrl(readinessUrl)) { + return undefined; + } + const readyState = await documentReadyState(webContents); + if (readyState === "interactive" || readyState === "complete" || !webContents.isLoading()) { + return { matched: true, matchedEvent, readinessUrl, timedOut: false }; + } + return undefined; +} + +async function documentReadyState(webContents: WebContents): Promise { + try { + const value = await executeJavaScriptWithTimeout( + webContents, + "document.readyState", + 1000, + "Document readiness check" + ); + return typeof value === "string" ? value : undefined; + } catch { + return undefined; + } +} + +function isInteractiveReadinessEvent(event: ReadinessEvent): boolean { + return event.type === "dom-ready" || + event.type === "did-finish-load" || + event.type === "did-stop-loading"; +} + +function isUsableInteractiveUrl(value: string | undefined): value is string { + return Boolean(value) && !isBlankPageUrl(value) && !isChromiumErrorPage(value); +} + +function navigationFailureResult(matchedEvent: string, event: Pick): ReadinessResult { + return { + matched: false, + matchedEvent, + navigationError: { + ...(event.errorCode !== undefined ? { errorCode: event.errorCode } : {}), + ...(event.errorDescription ? { errorDescription: event.errorDescription } : {}), + ...(event.url ? { url: event.url } : {}) + }, + readinessUrl: event.url, + timedOut: false + }; +} + +function isNavigationStartEvent(event: ReadinessEvent): boolean { + return event.type === "did-start-navigation" || + event.type === "did-redirect-navigation" || + event.type === "did-navigate"; +} + +function isRelevantNavigationUrl(url: string | undefined, context: NavigationReadinessContext): boolean { + if (!url) { + return false; + } + const normalizedUrl = normalizeUrlForComparison(url); + if (!normalizedUrl) { + return false; + } + if (context.expectedUrl && urlsMatchForNavigation(normalizedUrl, context.expectedUrl)) { + return true; + } + if (isChromiumErrorPage(normalizedUrl)) { + return true; + } + if (isBlankPageUrl(normalizedUrl)) { + return isBlankPageUrl(context.expectedUrl); + } + if (!context.previousUrl) { + return true; + } + return !urlsMatchForNavigation(normalizedUrl, context.previousUrl); +} + +function normalizeNavigationUrlForMatch(value: string | undefined): string | undefined { + const trimmed = value?.trim(); + if (!trimmed) { + return undefined; + } + if (isBlankPageUrl(trimmed) || isChromiumErrorPage(trimmed)) { + return trimmed; + } + if (/^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed)) { + return normalizeUrlForComparison(trimmed); + } + if (/^(localhost|127\.0\.0\.1|\[::1\])(?::\d+)?(?:\/|$)/i.test(trimmed) || trimmed.includes(".")) { + return normalizeUrlForComparison(`https://${trimmed}`); + } + return normalizeUrlForComparison(`https://www.google.com/search?q=${encodeURIComponent(trimmed)}`); +} + +function normalizeUrlForComparison(value: string | undefined): string | undefined { + const trimmed = value?.trim(); + if (!trimmed) { + return undefined; + } + try { + const url = new URL(trimmed); + url.hash = ""; + return url.toString(); + } catch { + return trimmed; + } +} + +function urlsMatchForNavigation(left: string | undefined, right: string | undefined): boolean { + if (!left || !right) { + return false; + } + const normalizedLeft = normalizeUrlForComparison(left); + const normalizedRight = normalizeUrlForComparison(right); + if (!normalizedLeft || !normalizedRight) { + return false; + } + if (normalizedLeft === normalizedRight) { + return true; + } + try { + const leftUrl = new URL(normalizedLeft); + const rightUrl = new URL(normalizedRight); + return leftUrl.protocol === rightUrl.protocol && + leftUrl.hostname === rightUrl.hostname && + leftUrl.pathname === rightUrl.pathname && + leftUrl.search === rightUrl.search; + } catch { + return normalizedLeft === normalizedRight; + } +} + +function isBlankPageUrl(value: string | undefined): boolean { + return value === "about:blank"; +} + +function isChromiumErrorPage(value: string | undefined): boolean { + return value === "chrome-error://chromewebdata/"; +} + +function safeWebContentsUrl(webContents: WebContents): string | undefined { + if (webContents.isDestroyed()) { + return undefined; + } + try { + return webContents.getURL(); + } catch { + return undefined; + } +} + +function normalizeWaitUntil(value: string | undefined, fallback: string): string { + const normalized = (value || fallback).toLowerCase().replace(/[-\s]/g, "_"); + if (normalized === "auto" || normalized === "interactive" || normalized === "ready") { + return "interactive"; + } + if (normalized === "dom_ready" || normalized === "domcontentloaded") { + return "domcontentloaded"; + } + if (normalized === "networkidle" || normalized === "network_idle") { + return "network_idle"; + } + if (normalized === "none" || normalized === "load") { + return normalized; + } + return fallback; +} + +function readHandoffKind(value: unknown): BuiltInBrowserAutomationHandoffKind { + const kind = readString(value); + if ( + kind === "blocked" || + kind === "human_verification" || + kind === "login_required" || + kind === "other" || + kind === "verification_code" + ) { + return kind; + } + return "other"; +} + +function normalizeEventChannels(values: string[]): Set { + const normalized = values + .map((value) => value.toLowerCase().trim()) + .filter(Boolean); + if (normalized.length === 0) { + return new Set(["tab", "navigation", "dom", "runtime", "dialog", "download", "input", "focus", "selection", "handoff"]); + } + return new Set(normalized); +} + +function matchesSubscriptionEvent(subscription: EventSubscription, event: BrowserAutomationEvent): boolean { + const channel = eventChannel(event.kind); + if (!subscription.channels.has(channel) && !subscription.channels.has(event.kind) && !subscription.channels.has("*")) { + return false; + } + if (!subscription.ref) { + return true; + } + if (!event.tabId || event.tabId === subscription.ref.tabId) { + return true; + } + return channel === "tab"; +} + +function eventChannel(kind: string): string { + if (kind.startsWith("tab.")) return "tab"; + if (kind.startsWith("page.navigation") || kind.startsWith("page.loading")) return "navigation"; + if (kind === "page.dom_ready") return "dom"; + if (kind.startsWith("runtime.")) return "runtime"; + if (kind.startsWith("dialog.")) return "dialog"; + if (kind.startsWith("download.")) return "download"; + if (kind.startsWith("handoff.")) return "handoff"; + if (kind.startsWith("input.")) return "input"; + if (kind.startsWith("focus.")) return "focus"; + if (kind.startsWith("selection.")) return "selection"; + return kind.split(".")[0] || kind; +} + +function subscriptionDescriptor(subscription: EventSubscription): Record { + return { + channels: [...subscription.channels], + cursor: makeEventCursor(subscription), + redactTextInput: subscription.redactTextInput, + sampleMouseMove: subscription.sampleMouseMove, + session: subscription.ref, + startedAt: subscription.startedAt, + subscriptionId: subscription.subscriptionId + }; +} + +function findRecentHandoffResolution(filter: { handoffId?: string; tabId?: string }): BrowserAutomationEvent | undefined { + const events = builtInBrowserService.getAutomationEvents({ replayRecentMs: automationEventReplayMs }); + for (let index = events.length - 1; index >= 0; index -= 1) { + const event = events[index]; + if (event.kind !== "handoff.completed" && event.kind !== "handoff.dismissed") { + continue; + } + if (filter.handoffId && event.handoffId !== filter.handoffId) { + continue; + } + if (filter.tabId && event.tabId !== filter.tabId) { + continue; + } + return event; + } + return undefined; +} + +function handoffResolutionResult( + event: BrowserAutomationEvent, + handoff: BuiltInBrowserAutomationHandoff | undefined, + timedOut: boolean +): Record { + return { + event, + handoff, + matched: !timedOut, + state: browserWindowState(), + status: event.handoffStatus || (event.kind === "handoff.completed" ? "completed" : "dismissed"), + timedOut, + title: event.title, + url: event.url + }; +} + +function eventsAfterCursor(subscription: EventSubscription, cursorSeq?: number): BrowserAutomationEvent[] { + const seq = cursorSeq ?? 0; + return subscription.events.filter((event) => event.seq > seq); +} + +function cursorDropped(subscription: EventSubscription, cursorSeq?: number): boolean { + if (cursorSeq === undefined || subscription.events.length === 0) { + return subscription.dropped; + } + return subscription.dropped && cursorSeq < subscription.events[0].seq - 1; +} + +function makeEventCursor(subscription: EventSubscription, seq?: number): Record { + const lastEvent = subscription.events.findLast((event) => seq === undefined || event.seq <= seq) || subscription.events.at(-1); + return { + dropped: subscription.dropped, + seq: seq ?? lastEvent?.seq ?? 0, + subscriptionId: subscription.subscriptionId, + ts: lastEvent?.ts ?? subscription.startedAt + }; +} + +function readCursorSeq(value: unknown): number | undefined { + if (!isRecord(value)) { + return undefined; + } + return readNumber(value.seq); +} + +function matchesAwaitFilter(event: BrowserAutomationEvent, args: Record): boolean { + const match = isRecord(args.match) ? args.match : args; + const kinds = readStringArray(match.kinds); + const kind = readString(match.kind); + if (kind && event.kind !== kind) { + return false; + } + if (kinds.length > 0 && !kinds.includes(event.kind)) { + return false; + } + const tabId = readString(match.tabId); + if (tabId && event.tabId !== tabId) { + return false; + } + const windowId = readString(match.windowId); + if (windowId && event.windowId !== windowId) { + return false; + } + return stringMatchesPattern(event.url, readString(match.urlPattern)) + && stringMatchesPattern(event.title, readString(match.titlePattern)) + && stringMatchesPattern(event.summary, readString(match.summaryPattern)); +} + +function stringMatchesPattern(value: string | undefined, pattern: string | undefined): boolean { + if (!pattern) { + return true; + } + const haystack = value || ""; + try { + return new RegExp(pattern).test(haystack); + } catch { + return haystack.toLowerCase().includes(pattern.toLowerCase()); + } +} + +async function handleJavaScriptDialog( + webContents: WebContents, + accept: boolean, + promptText: string | undefined, + session: BrowserSessionRef +): Promise> { + let attachedHere = false; + try { + if (!webContents.debugger.isAttached()) { + webContents.debugger.attach("1.3"); + attachedHere = true; + } + await withTimeout(webContents.debugger.sendCommand("Page.enable"), defaultJavascriptTimeoutMs, "Timed out enabling browser dialog handling."); + await withTimeout( + webContents.debugger.sendCommand("Page.handleJavaScriptDialog", { + accept, + ...(promptText !== undefined ? { promptText } : {}) + }), + defaultJavascriptTimeoutMs, + "Timed out handling browser dialog." + ); + return { + accepted: accept, + ok: true, + promptText, + session, + title: await webContents.getTitle(), + url: webContents.getURL() + }; + } catch (error) { + throw new Error(`Failed to handle browser dialog: ${formatError(error)}`); + } finally { + if (attachedHere && webContents.debugger.isAttached()) { + try { + webContents.debugger.detach(); + } catch { + // Ignore detach errors after the dialog is resolved. + } + } + } +} + +async function executeJavaScriptWithTimeout( + webContents: WebContents, + script: string, + timeoutMs: number, + label: string +): Promise { + if (webContents.isDestroyed()) { + throw new Error(`${label} failed because the browser tab is destroyed.`); + } + return await withTimeout( + webContents.executeJavaScript(script, true) as Promise, + timeoutMs, + `${label} timed out after ${timeoutMs}ms.` + ); +} + +async function withTimeout(promise: Promise, timeoutMs: number, message: string): Promise { + let timer: ReturnType | undefined; + try { + return await Promise.race([ + promise, + new Promise((_resolve, reject) => { + timer = setTimeout(() => reject(new Error(message)), Math.max(0, timeoutMs)); + }) + ]); + } finally { + if (timer) { + clearTimeout(timer); + } + } +} + +async function captureSnapshot(webContents: WebContents, options: { maxElements: number; maxText: number }): Promise { + return await executeJavaScriptWithTimeout( + webContents, + `(${snapshotScript})(${JSON.stringify(options)})`, + defaultJavascriptTimeoutMs, + "Browser snapshot" + ) as unknown; +} + +async function captureSnapshotWithHandoff( + webContents: WebContents, + tabId: string | undefined, + options: { maxElements: number; maxText: number } +): Promise { + const snapshot = await captureSnapshot(webContents, options); + const handoff = await maybeRequestPageHandoff(webContents, { + snapshot, + tabId + }); + if (!handoff || !isRecord(snapshot)) { + return snapshot; + } + return { + ...snapshot, + ...humanHelpResultFields(handoff) + }; +} + +async function maybeRequestHandoffAfterNavigation( + webContents: WebContents, + readiness: ReadinessResult, + options: { + requestedUrl?: string; + session?: BrowserSessionRef; + tabId?: string; + waitUntil?: string; + } +): Promise { + if (webContents.isDestroyed()) { + return undefined; + } + + if (!readiness.navigationError) { + const pageHandoff = await maybeRequestPageHandoff(webContents, { + session: options.session, + tabId: options.tabId + }); + if (pageHandoff) { + return pageHandoff; + } + } + + return maybeRequestNavigationHandoff(webContents, readiness, options); +} + +function maybeRequestNavigationHandoff( + webContents: WebContents, + readiness: ReadinessResult, + options: { + requestedUrl?: string; + session?: BrowserSessionRef; + tabId?: string; + waitUntil?: string; + } +): BuiltInBrowserAutomationHandoff | undefined { + if (readiness.matched || webContents.isDestroyed()) { + return undefined; + } + + const tabId = options.tabId || options.session?.tabId; + const existing = existingHandoffForTab(tabId); + if (existing) { + return existing; + } + + const currentUrl = safeWebContentsUrl(webContents); + const target = options.requestedUrl || readiness.readinessUrl || currentUrl || "the requested page"; + const error = readiness.navigationError; + const reason = error + ? `Browser navigation was blocked or failed while opening ${target}${error.errorCode !== undefined ? ` (error ${error.errorCode})` : ""}${error.errorDescription ? `: ${error.errorDescription}` : "."}` + : `Browser automation did not reach ${options.waitUntil || "the requested readiness state"} while opening ${target}.`; + + return builtInBrowserService.requestAutomationHandoff({ + kind: "blocked", + message: "Please inspect this browser page and complete or retry the blocked step, then click Done.", + reason, + sessionId: options.session?.sessionId, + tabId + }); +} + +async function maybeRequestPageHandoff( + webContents: WebContents, + options: { + session?: BrowserSessionRef; + snapshot?: unknown; + tabId?: string; + } = {} +): Promise { + if (webContents.isDestroyed()) { + return undefined; + } + + let snapshot = options.snapshot; + if (snapshot === undefined) { + try { + snapshot = await captureSnapshot(webContents, { + maxElements: 20, + maxText: 1200 + }); + } catch { + snapshot = undefined; + } + } + + const detection = detectPageHandoff(snapshot, { + title: await webContents.getTitle(), + url: webContents.getURL() + }); + if (!detection) { + return undefined; + } + + const tabId = options.tabId || options.session?.tabId; + const existing = existingHandoffForTab(tabId); + if (existing) { + return existing; + } + + return builtInBrowserService.requestAutomationHandoff({ + kind: detection.kind, + message: detection.message, + reason: detection.reason, + sessionId: options.session?.sessionId, + tabId + }); +} + +function existingHandoffForTab(tabId: string | undefined): BuiltInBrowserAutomationHandoff | undefined { + const existing = builtInBrowserService.getAutomationState().automationHandoff; + if (existing && (!tabId || existing.tabId === tabId)) { + return existing; + } + return undefined; +} + +function handoffMatches( + handoff: BuiltInBrowserAutomationHandoff | undefined, + filter: { handoffId?: string; tabId?: string } +): boolean { + if (!handoff) { + return false; + } + if (filter.handoffId && handoff.id !== filter.handoffId) { + return false; + } + if (filter.tabId && handoff.tabId !== filter.tabId) { + return false; + } + return true; +} + +function detectPageHandoff(snapshot: unknown, fallback: { title?: string; url?: string }): BrowserHandoffDetection | undefined { + const record = isRecord(snapshot) ? snapshot : {}; + const title = readString(record.title) || fallback.title || ""; + const url = readString(record.url) || fallback.url || ""; + const signal = pageHandoffSignal(record, title, url); + if (isChromiumErrorPage(url)) { + return { + kind: "blocked", + message: "Please inspect this browser error page and retry or complete the blocked step, then click Done.", + reason: "Chromium displayed an internal error page for this navigation." + }; + } + if (!signal) { + return undefined; + } + + const verificationCode = /verification code|security code|one[-\s]?time code|2[-\s]?step|two[-\s]?step|authenticator|check your (?:phone|email)|enter the code/.test(signal); + if (verificationCode) { + return { + kind: "verification_code", + message: "Please enter the verification code in this browser window, then click Done.", + reason: `The page appears to require a verification code: ${title || url || "current page"}.` + }; + } + + const cloudflare = signal.includes("cloudflare") || /\bray id\s*:/.test(signal); + const botVerification = /performing security verification|security verification|verif(?:y|ies|ying)[^.\n]{0,80}(?:not a bot|human)|not a bot|not a robot|human verification|are you human|checking your browser|security check/.test(signal); + const challenge = /captcha|hcaptcha|recaptcha|turnstile|cf-challenge|cf-turnstile/.test(signal); + const justAMoment = title.trim().toLowerCase() === "just a moment..." || signal.includes("just a moment..."); + if (challenge || (cloudflare && (botVerification || justAMoment))) { + return { + kind: "human_verification", + message: "Please complete the security verification in this browser window, then click Done.", + reason: `The page appears to be a human verification or bot-protection challenge: ${title || url || "current page"}.` + }; + } + + const loginUrl = /(?:^|[/.?=&_-])(?:login|signin|sign-in|accounts)(?:[/.?=&_-]|$)/.test(url.toLowerCase()) || + /accounts\.google\.com|mail\.google\.com/.test(url.toLowerCase()); + const loginTitle = /\bsign in\b|\blog in\b|\blogin\b|google accounts|account login/.test(title.toLowerCase()); + const credentialFields = /email or phone|email address|username|password|forgot (?:email|password)|create account|\bnext\b/.test(signal); + if ((loginUrl || loginTitle) && credentialFields) { + return { + kind: "login_required", + message: "Please sign in in this browser window, then click Done.", + reason: `The page appears to require a human login: ${title || url || "current page"}.` + }; + } + + return undefined; +} + +function humanHelpResultFields(handoff: BuiltInBrowserAutomationHandoff): Record { + return { + handoff, + handoffRequired: true, + humanHelp: { + aliasTool: "askHumanHelp", + instruction: "Browser automation needs human help. The browser window has been shown with a top toolbar instruction. Call browser_handoff_wait to receive the user's Done/Hide result before continuing automation.", + handoffId: handoff.id, + message: handoff.message, + reason: handoff.reason, + required: true, + status: "requested", + tool: "browser_handoff_wait" + }, + humanHelpRequired: true, + nextAction: "human_help" + }; +} + +function pageHandoffSignal(record: Record, title: string, url: string): string { + const parts = [title, url, rawString(record.text)]; + const activeElement = isRecord(record.activeElement) ? record.activeElement : undefined; + if (activeElement) { + parts.push(rawString(activeElement.name), rawString(activeElement.text), rawString(activeElement.role)); + } + const elements = Array.isArray(record.elements) ? record.elements : []; + for (const element of elements.slice(0, 40)) { + if (!isRecord(element)) { + continue; + } + parts.push( + rawString(element.name), + rawString(element.text), + rawString(element.role), + rawString(element.tag), + rawString(element.href) + ); + } + return parts + .filter((part): part is string => Boolean(part)) + .join("\n") + .replace(/\s+/g, " ") + .toLowerCase(); +} + +async function runTargetAction( + webContents: WebContents, + action: "click" | "focus" | "scroll" | "select" | "type", + target?: BrowserTarget, + value: Record = {} +): Promise { + const result = await executeJavaScriptWithTimeout( + webContents, + `(${targetActionScript})(${JSON.stringify({ action, target, value })})`, + defaultJavascriptTimeoutMs, + `Browser target action ${action}` + ) as { error?: string; ok?: boolean }; + if (!result?.ok) { + throw new Error(result?.error || `Browser target action failed: ${action}`); + } + return result; +} + +async function pressKey(webContents: WebContents, key: string, target?: BrowserTarget): Promise { + if (target) { + await runTargetAction(webContents, "focus", target); + } + webContents.sendInputEvent({ keyCode: key, type: "keyDown" }); + webContents.sendInputEvent({ keyCode: key, type: "keyUp" }); + return { + key, + ok: true, + title: await webContents.getTitle(), + url: webContents.getURL() + }; +} + +async function waitForPageCondition( + webContents: WebContents, + condition: { + selector?: string; + text?: string; + timeoutMs: number; + urlIncludes?: string; + urlMatches?: string; + } +): Promise { + const deadline = Date.now() + condition.timeoutMs; + let last: unknown; + while (Date.now() <= deadline) { + try { + last = await executeJavaScriptWithTimeout( + webContents, + `(${waitForConditionScript})(${JSON.stringify(condition)})`, + Math.max(100, Math.min(1000, deadline - Date.now())), + "Browser wait condition check" + ) as unknown; + if (isRecord(last) && last.matched === true) { + return last; + } + } catch (error) { + last = { + error: formatError(error), + matched: false + }; + } + await sleep(150); + } + return { + last, + matched: false, + timedOut: true, + title: await webContents.getTitle(), + url: webContents.getURL() + }; +} + +const snapshotScript = function(options: { maxElements: number; maxText: number }) { + const maxElements = Math.max(1, Math.min(300, Math.floor(options.maxElements || 80))); + const maxText = Math.max(0, Math.min(20000, Math.floor(options.maxText || 3000))); + const refAttribute = "data-ccr-browser-ref"; + const elementSelector = [ + "a[href]", + "button", + "input", + "textarea", + "select", + "summary", + "[role]", + "[contenteditable='true']", + "[contenteditable='plaintext-only']", + "[tabindex]:not([tabindex='-1'])" + ].join(","); + + function visible(el: Element) { + const style = window.getComputedStyle(el); + const rect = el.getBoundingClientRect(); + return style.display !== "none" && + style.visibility !== "hidden" && + Number(style.opacity || "1") > 0 && + rect.width > 0 && + rect.height > 0; + } + + function cssEscape(value: string) { + return window.CSS && typeof window.CSS.escape === "function" + ? window.CSS.escape(value) + : value.replace(/[^a-zA-Z0-9_-]/g, "\\$&"); + } + + function uniqueIdSelector(id: string) { + if (!id) return ""; + const selector = `#${cssEscape(id)}`; + try { + return document.querySelectorAll(selector).length === 1 ? selector : ""; + } catch { + return ""; + } + } + + function refFor(el: Element) { + const win = window as Window & { __ccrBrowserRefSeq?: number }; + const existing = el.getAttribute(refAttribute); + if (existing) { + return `[${refAttribute}="${cssEscape(existing)}"]`; + } + for (let attempt = 0; attempt < 1000; attempt += 1) { + win.__ccrBrowserRefSeq = (win.__ccrBrowserRefSeq || 0) + 1; + const nextRef = `ccr-${win.__ccrBrowserRefSeq}`; + const selector = `[${refAttribute}="${cssEscape(nextRef)}"]`; + if (!document.querySelector(selector)) { + el.setAttribute(refAttribute, nextRef); + return selector; + } + } + + const idSelector = uniqueIdSelector((el as HTMLElement).id || ""); + if (idSelector) return idSelector; + const parts: string[] = []; + let current: Element | null = el; + while (current && current.nodeType === 1 && current !== document.documentElement && parts.length < 8) { + const tag = current.tagName.toLowerCase(); + const parent: HTMLElement | null = current.parentElement; + if (!parent) { + parts.unshift(tag); + break; + } + const parentIdSelector = uniqueIdSelector(parent.id || ""); + const sameTag = Array.from(parent.children).filter((child): child is Element => child instanceof Element && child.tagName === current!.tagName); + const index = sameTag.indexOf(current) + 1; + parts.unshift(`${tag}:nth-of-type(${Math.max(index, 1)})`); + if (parentIdSelector) { + parts.unshift(parentIdSelector); + break; + } + current = parent; + } + return parts.join(" > "); + } + + function text(el: Element) { + return ((el as HTMLElement).innerText || el.textContent || "").replace(/\s+/g, " ").trim(); + } + + function labelText(el: Element) { + const labelledBy = el.getAttribute("aria-labelledby"); + if (labelledBy) { + const value = labelledBy + .split(/\s+/) + .map((id) => document.getElementById(id)?.innerText || "") + .join(" ") + .replace(/\s+/g, " ") + .trim(); + if (value) return value; + } + const id = (el as HTMLInputElement).id; + if (id) { + const label = document.querySelector(`label[for="${cssEscape(id)}"]`); + if (label) return text(label); + } + const wrappingLabel = el.closest("label"); + return wrappingLabel ? text(wrappingLabel) : ""; + } + + function unsafeField(el: Element) { + const input = el as HTMLInputElement; + const haystack = [ + input.type, + input.name, + input.id, + input.autocomplete, + el.getAttribute("aria-label"), + labelText(el) + ].join(" ").toLowerCase(); + return /\b(password|secret|token|api[-_ ]?key|bearer|credential)\b/.test(haystack); + } + + function valueOf(el: Element) { + if (!(el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement)) { + return ""; + } + if (unsafeField(el)) { + return ""; + } + return el.value || ""; + } + + function roleOf(el: Element) { + const explicit = el.getAttribute("role"); + if (explicit) return explicit; + const tag = el.tagName.toLowerCase(); + const input = el as HTMLInputElement; + if (tag === "a") return "link"; + if (tag === "button") return "button"; + if (tag === "textarea") return "textbox"; + if (tag === "select") return "combobox"; + if (tag === "summary") return "button"; + if (tag === "input") { + if (["button", "submit", "reset"].includes(input.type)) return "button"; + if (input.type === "checkbox") return "checkbox"; + if (input.type === "radio") return "radio"; + if (input.type === "range") return "slider"; + return "textbox"; + } + if (el.getAttribute("contenteditable")) return "textbox"; + return ""; + } + + function nameOf(el: Element) { + const input = el as HTMLInputElement; + return ( + el.getAttribute("aria-label") || + labelText(el) || + el.getAttribute("alt") || + el.getAttribute("title") || + input.placeholder || + (input.type && ["button", "submit", "reset"].includes(input.type) ? input.value : "") || + text(el) + ).replace(/\s+/g, " ").trim(); + } + + const elements = Array.from(document.querySelectorAll(elementSelector)) + .filter(visible) + .slice(0, maxElements) + .map((el, index) => { + const rect = el.getBoundingClientRect(); + return { + checked: el instanceof HTMLInputElement && ["checkbox", "radio"].includes(el.type) ? el.checked : undefined, + disabled: (el as HTMLButtonElement | HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement).disabled || undefined, + href: el instanceof HTMLAnchorElement ? el.href : undefined, + index, + name: nameOf(el).slice(0, 240), + placeholder: (el as HTMLInputElement).placeholder || undefined, + rect: { + height: Math.round(rect.height), + width: Math.round(rect.width), + x: Math.round(rect.x), + y: Math.round(rect.y) + }, + ref: refFor(el), + role: roleOf(el), + tag: el.tagName.toLowerCase(), + text: text(el).slice(0, 180), + value: valueOf(el) || undefined + }; + }); + + return { + activeElement: document.activeElement ? { + name: nameOf(document.activeElement).slice(0, 240), + ref: refFor(document.activeElement), + role: roleOf(document.activeElement), + tag: document.activeElement.tagName.toLowerCase() + } : undefined, + elements, + text: (document.body?.innerText || "").replace(/\s+\n/g, "\n").replace(/\n{3,}/g, "\n\n").trim().slice(0, maxText), + title: document.title, + url: location.href + }; +}; + +const targetActionScript = function(request: { + action: "click" | "focus" | "scroll" | "select" | "type"; + target?: BrowserTarget; + value?: Record; +}) { + function visible(el: Element) { + const style = window.getComputedStyle(el); + const rect = el.getBoundingClientRect(); + return style.display !== "none" && style.visibility !== "hidden" && rect.width > 0 && rect.height > 0; + } + function text(el: Element) { + return ((el as HTMLElement).innerText || el.textContent || "").replace(/\s+/g, " ").trim(); + } + function cssEscape(value: string) { + return window.CSS && typeof window.CSS.escape === "function" + ? window.CSS.escape(value) + : value.replace(/[^a-zA-Z0-9_-]/g, "\\$&"); + } + function labelText(el: Element) { + const id = (el as HTMLInputElement).id; + if (id) { + const label = document.querySelector(`label[for="${cssEscape(id)}"]`); + if (label) return text(label); + } + const wrappingLabel = el.closest("label"); + return wrappingLabel ? text(wrappingLabel) : ""; + } + function roleOf(el: Element) { + const explicit = el.getAttribute("role"); + if (explicit) return explicit.toLowerCase(); + const tag = el.tagName.toLowerCase(); + const input = el as HTMLInputElement; + if (tag === "a") return "link"; + if (tag === "button") return "button"; + if (tag === "textarea") return "textbox"; + if (tag === "select") return "combobox"; + if (tag === "summary") return "button"; + if (tag === "input") { + if (["button", "submit", "reset"].includes(input.type)) return "button"; + if (input.type === "checkbox") return "checkbox"; + if (input.type === "radio") return "radio"; + return "textbox"; + } + if (el.getAttribute("contenteditable")) return "textbox"; + return ""; + } + function nameOf(el: Element) { + const input = el as HTMLInputElement; + return ( + el.getAttribute("aria-label") || + labelText(el) || + el.getAttribute("alt") || + el.getAttribute("title") || + input.placeholder || + input.value || + text(el) + ).replace(/\s+/g, " ").trim(); + } + function summarize(el: Element) { + const rect = el.getBoundingClientRect(); + return { + name: nameOf(el).slice(0, 240), + rect: { + height: Math.round(rect.height), + width: Math.round(rect.width), + x: Math.round(rect.x), + y: Math.round(rect.y) + }, + role: roleOf(el), + tag: el.tagName.toLowerCase(), + text: text(el).slice(0, 180) + }; + } + function selectorElement(selector: string) { + try { + return document.querySelector(selector); + } catch { + return null; + } + } + function matchesText(value: string, needle: string, exact?: boolean) { + const left = value.trim().toLowerCase(); + const right = needle.trim().toLowerCase(); + return exact ? left === right : left.includes(right); + } + function findTarget(target?: BrowserTarget) { + if (!target && request.action !== "scroll") return null; + const directSelector = target?.selector || target?.ref || target?.axNodeId; + if (directSelector) { + return selectorElement(directSelector); + } + const selector = [ + "a[href]", + "button", + "input", + "textarea", + "select", + "summary", + "[role]", + "[contenteditable='true']", + "[contenteditable='plaintext-only']", + "[tabindex]:not([tabindex='-1'])" + ].join(","); + const candidates = Array.from(document.querySelectorAll(selector)).filter(visible); + const role = target?.role?.trim().toLowerCase(); + const needle = target?.text?.trim(); + const matches = candidates.filter((el) => { + if (role && roleOf(el) !== role) return false; + if (!needle) return true; + const haystack = [ + nameOf(el), + text(el), + (el as HTMLInputElement).placeholder || "", + (el as HTMLInputElement).value || "" + ].join(" "); + return matchesText(haystack, needle, target?.exact); + }); + return matches[Math.max(0, Math.floor(target?.index || 0))] || null; + } + function dispatchValueEvents(el: Element) { + el.dispatchEvent(new Event("input", { bubbles: true })); + el.dispatchEvent(new Event("change", { bubbles: true })); + } + + const el = findTarget(request.target); + if (!el && request.action !== "scroll") { + return { error: "No matching browser element found.", ok: false }; + } + if (el) { + el.scrollIntoView({ block: "center", inline: "center" }); + if (el instanceof HTMLElement) { + el.focus({ preventScroll: true }); + } + } + + if (request.action === "focus") { + return { element: el ? summarize(el) : undefined, ok: true }; + } + + if (request.action === "click") { + if (!(el instanceof HTMLElement)) { + return { error: "Matched element is not clickable.", ok: false }; + } + el.click(); + return { element: summarize(el), ok: true }; + } + + if (request.action === "type") { + const nextText = typeof request.value?.text === "string" ? request.value.text : ""; + const replaceExisting = request.value?.replaceExisting !== false; + if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) { + el.value = replaceExisting ? nextText : `${el.value}${nextText}`; + dispatchValueEvents(el); + return { element: summarize(el), ok: true, value: el.type === "password" ? "" : el.value }; + } + if (el instanceof HTMLElement && el.isContentEditable) { + if (replaceExisting) { + el.textContent = nextText; + } else { + el.textContent = `${el.textContent || ""}${nextText}`; + } + dispatchValueEvents(el); + return { element: summarize(el), ok: true }; + } + return { error: "Matched element cannot receive typed text.", ok: false }; + } + + if (request.action === "select") { + if (!(el instanceof HTMLSelectElement)) { + return { error: "Matched element is not a select.", ok: false }; + } + const requestedValue = typeof request.value?.value === "string" ? request.value.value : ""; + const requestedLabel = typeof request.value?.label === "string" ? request.value.label : ""; + const exact = request.value?.exact === true; + const option = Array.from(el.options).find((candidate) => { + if (requestedValue && candidate.value === requestedValue) return true; + if (!requestedLabel) return false; + return matchesText(candidate.textContent || "", requestedLabel, exact); + }); + if (!option) { + return { error: "No matching select option found.", ok: false }; + } + el.value = option.value; + dispatchValueEvents(el); + return { element: summarize(el), label: option.textContent || "", ok: true, value: option.value }; + } + + if (request.action === "scroll") { + const deltaX = typeof request.value?.deltaX === "number" ? request.value.deltaX : 0; + const deltaY = typeof request.value?.deltaY === "number" ? request.value.deltaY : 700; + if (el instanceof HTMLElement) { + el.scrollBy({ behavior: "auto", left: deltaX, top: deltaY }); + return { element: summarize(el), ok: true }; + } + window.scrollBy({ behavior: "auto", left: deltaX, top: deltaY }); + return { ok: true, scrollX: window.scrollX, scrollY: window.scrollY }; + } + + return { error: "Unsupported browser target action.", ok: false }; +}; + +const waitForConditionScript = function(condition: { + selector?: string; + text?: string; + urlIncludes?: string; + urlMatches?: string; +}) { + const checks: Array<{ matched: boolean; type: string }> = []; + if (condition.urlIncludes) { + checks.push({ matched: location.href.includes(condition.urlIncludes), type: "urlIncludes" }); + } + if (condition.urlMatches) { + let matched = false; + try { + matched = new RegExp(condition.urlMatches).test(location.href); + } catch { + matched = false; + } + checks.push({ matched, type: "urlMatches" }); + } + if (condition.selector) { + let matched = false; + try { + const el = document.querySelector(condition.selector); + if (el) { + const style = window.getComputedStyle(el); + const rect = el.getBoundingClientRect(); + matched = style.display !== "none" && style.visibility !== "hidden" && rect.width > 0 && rect.height > 0; + } + } catch { + matched = false; + } + checks.push({ matched, type: "selector" }); + } + if (condition.text) { + const pageText = (document.body?.innerText || "").toLowerCase(); + checks.push({ matched: pageText.includes(condition.text.toLowerCase()), type: "text" }); + } + return { + checks, + matched: checks.length > 0 && checks.every((check) => check.matched), + title: document.title, + url: location.href + }; +}; + +function readTarget(value: unknown): BrowserTarget { + if (!isRecord(value)) { + throw new Error("A browser target object is required."); + } + return { + axNodeId: readString(value.axNodeId), + backendNodeId: readNumber(value.backendNodeId), + exact: value.exact === true, + index: clampInteger(readNumber(value.index) ?? 0, 0, 10000), + ref: readString(value.ref) || readString(value.axNodeId), + role: readString(value.role), + selector: readString(value.selector), + text: readString(value.text) + }; +} + +function requiredString(value: unknown, message: string): string { + const text = readString(value); + if (!text) { + throw new Error(message); + } + return text; +} + +function readString(value: unknown): string | undefined { + return typeof value === "string" && value.trim() ? value.trim() : undefined; +} + +function readStringArray(value: unknown): string[] { + return Array.isArray(value) ? value.filter((item): item is string => typeof item === "string" && item.trim().length > 0) : []; +} + +function uniqueStrings(values: string[]): string[] { + return [...new Set(values)]; +} + +function readNumber(value: unknown): number | undefined { + return typeof value === "number" && Number.isFinite(value) ? value : undefined; +} + +function clampInteger(value: number, min: number, max: number): number { + return Math.min(max, Math.max(min, Math.trunc(value))); +} + +function objectSchema(properties: Record, required: string[] = []): JsonValue { + return { + additionalProperties: false, + properties, + required, + type: "object" + }; +} + +type CompactResultOptions = { + maxArrayItems: number; + maxDepth: number; + maxObjectKeys: number; + maxStringChars: number; +}; + +function formatToolResult(toolName: string, result: unknown): string { + const compacted = compactToolResult(toolName, result); + const serialized = stringifyToolResult(compacted); + if (serialized.length <= maxToolResultChars) { + return serialized; + } + + const tighter = compactJsonValue(compacted, { + maxArrayItems: 50, + maxDepth: 6, + maxObjectKeys: 80, + maxStringChars: 600 + }); + const tightSerialized = stringifyToolResult({ + guidance: largeResultGuidance(toolName), + maxChars: maxToolResultChars, + originalChars: serialized.length, + result: tighter, + tool: toolName, + truncated: true + }); + if (tightSerialized.length <= maxToolResultChars) { + return tightSerialized; + } + + return stringifyToolResult({ + guidance: largeResultGuidance(toolName), + maxChars: maxToolResultChars, + originalChars: serialized.length, + preview: truncateString(tightSerialized, 12_000), + tool: toolName, + truncated: true + }); +} + +function compactToolResult(toolName: string, result: unknown): unknown { + if (toolName === "browser_snapshot") { + return compactSnapshotResult(result); + } + if (toolName === "browser_ax_snapshot") { + return compactAxSnapshotResult(result); + } + if (toolName === "browser_ax_query") { + return compactArrayResult(result, "matches", 80, 600); + } + if (toolName === "browser_events_read" || toolName === "browser_events_await") { + return compactArrayResult(result, "events", 80, 800); + } + return compactJsonValue(result, defaultCompactResultOptions()); +} + +function compactSnapshotResult(result: unknown): unknown { + if (!isRecord(result)) { + return compactJsonValue(result, defaultCompactResultOptions()); + } + + const elements = Array.isArray(result.elements) ? result.elements : []; + const text = rawString(result.text); + const compacted: Record = {}; + for (const [key, value] of Object.entries(result)) { + if (key === "elements" || key === "text") { + continue; + } + compacted[key] = compactJsonValue(value, { + maxArrayItems: 40, + maxDepth: 5, + maxObjectKeys: 60, + maxStringChars: 600 + }); + } + + if (text !== undefined) { + compacted.text = truncateString(text, maxBrowserResultTextChars); + } + compacted.elements = elements + .slice(0, maxSnapshotResultElements) + .map((element) => compactJsonValue(element, { + maxArrayItems: 20, + maxDepth: 4, + maxObjectKeys: 40, + maxStringChars: 500 + })); + compacted.elementCount = elements.length; + + const truncated: Record = {}; + if (text !== undefined && text.length > maxBrowserResultTextChars) { + truncated.textChars = text.length - maxBrowserResultTextChars; + } + if (elements.length > maxSnapshotResultElements) { + truncated.elements = elements.length - maxSnapshotResultElements; + } + if (Object.keys(truncated).length > 0) { + compacted.truncated = truncated; + } + return compacted; +} + +function compactAxSnapshotResult(result: unknown): unknown { + if (!isRecord(result)) { + return compactJsonValue(result, defaultCompactResultOptions()); + } + + const nodes = Array.isArray(result.nodes) ? result.nodes : []; + const compacted: Record = {}; + for (const [key, value] of Object.entries(result)) { + if (key === "nodes") { + continue; + } + compacted[key] = compactJsonValue(value, { + maxArrayItems: 40, + maxDepth: 5, + maxObjectKeys: 60, + maxStringChars: 400 + }); + } + compacted.nodes = nodes + .slice(0, maxAxSnapshotResultNodes) + .map((node) => compactJsonValue(node, { + maxArrayItems: 80, + maxDepth: 5, + maxObjectKeys: 40, + maxStringChars: 260 + })); + compacted.nodeCount = nodes.length; + if (nodes.length > maxAxSnapshotResultNodes) { + compacted.truncated = { + nodes: nodes.length - maxAxSnapshotResultNodes + }; + } + return compacted; +} + +function compactArrayResult(result: unknown, key: string, maxItems: number, maxStringChars: number): unknown { + if (!isRecord(result)) { + return compactJsonValue(result, defaultCompactResultOptions()); + } + + const items = Array.isArray(result[key]) ? result[key] : []; + const compacted: Record = {}; + for (const [entryKey, value] of Object.entries(result)) { + if (entryKey === key) { + continue; + } + compacted[entryKey] = compactJsonValue(value, { + maxArrayItems: 40, + maxDepth: 5, + maxObjectKeys: 60, + maxStringChars + }); + } + compacted[key] = items + .slice(0, maxItems) + .map((item) => compactJsonValue(item, { + maxArrayItems: 40, + maxDepth: 5, + maxObjectKeys: 60, + maxStringChars + })); + compacted[`${key}Count`] = items.length; + if (items.length > maxItems) { + compacted.truncated = { + [key]: items.length - maxItems + }; + } + return compacted; +} + +function compactJsonValue( + value: unknown, + options: CompactResultOptions, + depth = 0, + seen: WeakSet = new WeakSet() +): unknown { + if (value === null || typeof value === "boolean" || typeof value === "number") { + return value; + } + if (typeof value === "string") { + return truncateString(value, options.maxStringChars); + } + if (typeof value === "bigint") { + return value.toString(); + } + if (typeof value === "undefined" || typeof value === "function" || typeof value === "symbol") { + return undefined; + } + if (typeof value !== "object") { + return String(value); + } + if (seen.has(value)) { + return "[Circular]"; + } + if (depth >= options.maxDepth) { + return { + reason: "Maximum result depth reached.", + truncated: true + }; + } + + seen.add(value); + if (Array.isArray(value)) { + const limit = Math.max(0, options.maxArrayItems); + const items = value + .slice(0, limit) + .map((item) => compactJsonValue(item, options, depth + 1, seen)); + if (value.length > limit) { + items.push({ + omittedItems: value.length - limit, + truncated: true + }); + } + seen.delete(value); + return items; + } + + const entries = Object.entries(value); + const compacted: Record = {}; + for (const [key, entryValue] of entries.slice(0, options.maxObjectKeys)) { + compacted[key] = compactJsonValue(entryValue, options, depth + 1, seen); + } + if (entries.length > options.maxObjectKeys) { + compacted.truncatedKeys = entries.length - options.maxObjectKeys; + } + seen.delete(value); + return compacted; +} + +function defaultCompactResultOptions(): CompactResultOptions { + return { + maxArrayItems: maxToolResultArrayItems, + maxDepth: 8, + maxObjectKeys: maxToolResultObjectKeys, + maxStringChars: maxToolResultStringChars + }; +} + +function stringifyToolResult(value: unknown): string { + return JSON.stringify(value, null, 2) || "null"; +} + +function rawString(value: unknown): string | undefined { + return typeof value === "string" ? value : undefined; +} + +function truncateString(value: string, maxChars: number): string { + if (value.length <= maxChars) { + return value; + } + let suffix = "\n[truncated]"; + for (let index = 0; index < 2; index += 1) { + const keep = Math.max(0, maxChars - suffix.length); + suffix = `\n[truncated ${value.length - keep} chars]`; + } + return `${value.slice(0, Math.max(0, maxChars - suffix.length))}${suffix}`; +} + +function largeResultGuidance(toolName: string): string { + if (toolName === "browser_snapshot") { + return "Result was compacted. Re-run browser_snapshot with lower maxElements/maxText, or use browser_ax_query to fetch targeted elements."; + } + if (toolName === "browser_ax_snapshot") { + return "Result was compacted. Re-run browser_ax_snapshot with lower limit or scope=outline, or use browser_ax_query with role/name/text filters."; + } + if (toolName === "browser_ax_query") { + return "Result was compacted. Re-run browser_ax_query with a lower limit or narrower role/name/text filters."; + } + if (toolName === "browser_events_read" || toolName === "browser_events_await") { + return "Result was compacted. Read events with a lower limit/maxEvents or a more specific cursor/filter."; + } + return "Result was compacted before returning to the MCP client. Use narrower arguments or lower limits for more detail."; +} + +function textResult(text: string): ToolCallResult { + return { + content: [{ text, type: "text" }] + }; +} + +function jsonRpcResult(id: null | number | string, result: JsonValue): JsonRpcResponse { + return { + id, + jsonrpc: "2.0", + result + }; +} + +function jsonRpcError(id: null | number | string, code: number, message: string): JsonRpcResponse { + return { + error: { + code, + message + }, + id, + jsonrpc: "2.0" + }; +} + +function sendJson(response: ServerResponse, status: number, payload: unknown): void { + const body = `${JSON.stringify(payload)}\n`; + response.writeHead(status, { + "content-length": Buffer.byteLength(body), + "content-type": "application/json; charset=utf-8" + }); + response.end(body); +} + +function readRequestBody(request: IncomingMessage, maxBytes: number): Promise { + return new Promise((resolve, reject) => { + const chunks: Buffer[] = []; + let total = 0; + request.on("data", (chunk: Buffer | string) => { + const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk); + total += buffer.byteLength; + if (total > maxBytes) { + reject(new Error("MCP request body is too large.")); + request.destroy(); + return; + } + chunks.push(buffer); + }); + request.on("end", () => resolve(Buffer.concat(chunks))); + request.on("error", reject); + }); +} + +function sleep(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +function isRecord(value: unknown): value is Record { + return typeof value === "object" && value !== null && !Array.isArray(value); +} + +function formatError(error: unknown): string { + return error instanceof Error ? error.message : String(error); +} diff --git a/packages/electron/src/main/browser-preload.ts b/packages/electron/src/main/browser-preload.ts index 2e1930f..9d571ff 100644 --- a/packages/electron/src/main/browser-preload.ts +++ b/packages/electron/src/main/browser-preload.ts @@ -1,16 +1,19 @@ import { contextBridge, ipcRenderer, type IpcRendererEvent } from "electron"; import { IPC_CHANNELS } from "@ccr/core/contracts/ipc-channels"; -import type { BuiltInBrowserState } from "@ccr/core/contracts/app"; +import type { BuiltInBrowserState, ChromeLoginImportJob, ChromeLoginImportRequest } from "@ccr/core/contracts/app"; contextBridge.exposeInMainWorld("ccrBrowser", { back: (tabId?: string) => ipcRenderer.invoke(IPC_CHANNELS.browserBack, tabId) as Promise, closeTab: (tabId: string) => ipcRenderer.invoke(IPC_CHANNELS.browserCloseTab, tabId) as Promise, forward: (tabId?: string) => ipcRenderer.invoke(IPC_CHANNELS.browserForward, tabId) as Promise, + getChromeLoginImport: (id: string) => ipcRenderer.invoke(IPC_CHANNELS.browserGetChromeLoginImport, id) as Promise, getState: () => ipcRenderer.invoke(IPC_CHANNELS.browserGetState) as Promise, navigate: (url: string, tabId?: string) => ipcRenderer.invoke(IPC_CHANNELS.browserNavigate, url, tabId) as Promise, newTab: (url?: string) => ipcRenderer.invoke(IPC_CHANNELS.browserNewTab, url) as Promise, reload: (tabId?: string) => ipcRenderer.invoke(IPC_CHANNELS.browserReload, tabId) as Promise, + resolveAutomationHandoff: (status: "completed" | "dismissed") => ipcRenderer.invoke(IPC_CHANNELS.browserResolveAutomationHandoff, status) as Promise, selectTab: (tabId: string) => ipcRenderer.invoke(IPC_CHANNELS.browserSelectTab, tabId) as Promise, + startChromeLoginImport: (request: ChromeLoginImportRequest) => ipcRenderer.invoke(IPC_CHANNELS.browserStartChromeLoginImport, request) as Promise, onStateChanged: (callback: (state: BuiltInBrowserState) => void) => { const handler = (_event: IpcRendererEvent, state: BuiltInBrowserState) => callback(state); ipcRenderer.on(IPC_CHANNELS.browserStateChanged, handler); diff --git a/packages/electron/src/main/built-in-browser.ts b/packages/electron/src/main/built-in-browser.ts index a8ae1b0..c941016 100644 --- a/packages/electron/src/main/built-in-browser.ts +++ b/packages/electron/src/main/built-in-browser.ts @@ -1,4 +1,5 @@ import { randomUUID } from "node:crypto"; +import { EventEmitter } from "node:events"; import { BrowserWindow, clipboard, @@ -10,28 +11,64 @@ import { WebContentsView, type ContextMenuParams, type IpcMainInvokeEvent, - type MenuItemConstructorOptions + type MenuItemConstructorOptions, + type WebContents } from "electron"; import path from "node:path"; import { pathToFileURL } from "node:url"; -import type { AppConfig, BuiltInBrowserState, BuiltInBrowserTabState, GatewayPluginAppConfig, InstalledBrowserApp } from "@ccr/core/contracts/app"; +import type { + AppConfig, + BuiltInBrowserAutomationHandoff, + BuiltInBrowserAutomationHandoffKind, + BuiltInBrowserState, + BuiltInBrowserTabState, + ChromeLoginImportRequest, + GatewayPluginAppConfig, + InstalledBrowserApp +} from "@ccr/core/contracts/app"; import { IPC_CHANNELS } from "@ccr/core/contracts/ipc-channels"; import { APP_NAME } from "@ccr/core/config/constants"; import { pluginService } from "@ccr/core/plugins/service"; -import { proxyService } from "@ccr/core/proxy/service"; +import { chromeLoginImportService } from "./chrome-login-import"; type BrowserTab = BuiltInBrowserTabState & { view: WebContentsView; }; -const browserChromeHeight = 82; +export type BrowserAutomationEvent = { + errorCode?: number; + errorDescription?: string; + handoffId?: string; + handoffStatus?: "completed" | "dismissed"; + kind: string; + seq: number; + summary?: string; + tabId?: string; + title?: string; + ts: number; + url?: string; + windowId?: string; +}; + +export type BrowserAutomationEventListener = (event: BrowserAutomationEvent) => void; + +const browserChromeBaseHeight = 82; +const browserHandoffToolbarHeight = 44; const browserHomeUrl = "about:blank"; const browserPartition = "persist:ccr-built-in-browser"; +const browserAutomationWindowId = "ccr-built-in-browser"; +const maxAutomationEventHistory = 512; +const maxAutomationEventAgeMs = 60_000; const titleBarHeight = 46; class BuiltInBrowserService { private activeTabId?: string; private apps: InstalledBrowserApp[] = []; + private automationHandoff?: BuiltInBrowserAutomationHandoff; + private automationEventSeq = 0; + private readonly automationEvents = new EventEmitter(); + private readonly automationEventHistory: BrowserAutomationEvent[] = []; + private hideWindowAfterAutomationHandoff = false; private proxyConfigKey = ""; private tabOrder: string[] = []; private tabs = new Map(); @@ -44,10 +81,7 @@ class BuiltInBrowserService { async open(config: AppConfig): Promise { await this.syncProxy(config); - const window = this.window && !this.window.isDestroyed() ? this.window : this.createWindow(); - if (this.tabs.size === 0) { - this.createTab(browserHomeUrl); - } + const window = this.ensureWindow(); if (window.isMinimized()) { window.restore(); } @@ -57,28 +91,18 @@ class BuiltInBrowserService { this.sendState(); } + async openHidden(config: AppConfig): Promise { + await this.syncProxy(config); + this.ensureWindow(); + this.layoutActiveView(); + this.sendState(); + } + async syncProxy(config: AppConfig): Promise { this.syncApps(config); const browserSession = session.fromPartition(browserPartition); - if (config.proxy.enabled) { - await proxyService.refreshUpstreamProxyFromCurrentSystem(); - } - const proxyStatus = proxyService.getStatus(); - const shouldUseProxy = Boolean( - config.proxy.enabled && - proxyStatus.state === "running" && - proxyStatus.endpoint - ); - const proxyConfig = shouldUseProxy - ? { - mode: "fixed_servers" as const, - proxyBypassRules: "<-loopback>", - proxyRules: electronProxyRules(proxyStatus.endpoint) - } - : { - mode: "direct" as const - }; + const proxyConfig = { mode: "system" as const }; const nextKey = JSON.stringify(proxyConfig); if (nextKey === this.proxyConfigKey) { return; @@ -100,9 +124,211 @@ class BuiltInBrowserService { async clearProxy(): Promise { const browserSession = session.fromPartition(browserPartition); - await browserSession.setProxy({ mode: "direct" }); + const proxyConfig = { mode: "system" as const }; + await browserSession.setProxy(proxyConfig); await browserSession.forceReloadProxyConfig(); - this.proxyConfigKey = JSON.stringify({ mode: "direct" }); + this.proxyConfigKey = JSON.stringify(proxyConfig); + } + + getAutomationState(): BuiltInBrowserState { + return this.getState(); + } + + getAutomationWindowId(): string { + return browserAutomationWindowId; + } + + getAutomationEvents(options: { replayRecentMs?: number } = {}): BrowserAutomationEvent[] { + const replayRecentMs = Math.max(0, Math.floor(options.replayRecentMs ?? 0)); + const cutoff = replayRecentMs > 0 ? Date.now() - replayRecentMs : 0; + this.pruneAutomationEventHistory(); + return this.automationEventHistory + .filter((event) => event.ts >= cutoff) + .map((event) => ({ ...event })); + } + + requestAutomationHandoff(request: { + kind?: BuiltInBrowserAutomationHandoffKind; + message?: string; + reason?: string; + sessionId?: string; + tabId?: string; + }): BuiltInBrowserAutomationHandoff { + const existingWindow = this.window && !this.window.isDestroyed() ? this.window : undefined; + if (!this.automationHandoff) { + this.hideWindowAfterAutomationHandoff = !existingWindow || !existingWindow.isVisible() || existingWindow.isMinimized(); + } + const tab = this.getTab(request.tabId); + if (request.tabId && tab?.id) { + this.selectTab(request.tabId); + } + const targetTab = tab || this.getTab(); + const handoff: BuiltInBrowserAutomationHandoff = { + id: randomUUID(), + kind: request.kind || "other", + message: request.message?.trim() || defaultAutomationHandoffMessage(request.kind), + ...(request.reason?.trim() ? { reason: request.reason.trim() } : {}), + requestedAt: Date.now(), + ...(request.sessionId?.trim() ? { sessionId: request.sessionId.trim() } : {}), + status: "pending", + tabId: targetTab?.id || request.tabId || this.activeTabId + }; + this.automationHandoff = handoff; + this.layoutActiveView(); + this.showAutomationWindow(); + this.sendState(); + this.emitAutomationEvent({ + handoffId: handoff.id, + kind: "handoff.requested", + summary: handoff.message, + tabId: handoff.tabId, + title: targetTab?.title, + url: targetTab?.url + }); + return handoff; + } + + resolveAutomationHandoff(status: "completed" | "dismissed" = "completed"): BuiltInBrowserState { + const handoff = this.automationHandoff; + const shouldHideWindow = this.hideWindowAfterAutomationHandoff; + this.automationHandoff = undefined; + this.hideWindowAfterAutomationHandoff = false; + this.layoutActiveView(); + this.sendState(); + if (handoff) { + const tab = this.getTab(handoff.tabId); + if (shouldHideWindow) { + this.hideAutomationWindow(); + } + this.emitAutomationEvent({ + handoffId: handoff.id, + handoffStatus: status, + kind: status === "completed" ? "handoff.completed" : "handoff.dismissed", + summary: status === "completed" ? "User completed the requested browser handoff." : "User dismissed the requested browser handoff.", + tabId: handoff.tabId, + title: tab?.title, + url: tab?.url + }); + } + return this.getState(); + } + + subscribeAutomationEvents( + listener: BrowserAutomationEventListener, + options: { replayRecentMs?: number } = {} + ): () => void { + this.automationEvents.on("event", listener); + const replayRecentMs = Math.max(0, Math.floor(options.replayRecentMs ?? 0)); + if (replayRecentMs > 0) { + const cutoff = Date.now() - replayRecentMs; + this.pruneAutomationEventHistory(); + for (const event of this.automationEventHistory) { + if (event.ts >= cutoff) { + listener(event); + } + } + } + return () => this.automationEvents.off("event", listener); + } + + createAutomationTab(url = browserHomeUrl): BuiltInBrowserTabState { + const tab = this.createTab(url); + const { view: _view, ...state } = tab; + return state; + } + + selectAutomationTab(tabId: string): BuiltInBrowserState { + this.selectTab(tabId); + return this.getState(); + } + + closeAutomationTab(tabId: string): BuiltInBrowserState { + this.closeTab(tabId); + return this.getState(); + } + + async navigateAutomationTab(url: string, tabId?: string): Promise { + const tab = this.getTab(tabId); + if (!tab) { + throw new Error("Browser tab was not found."); + } + + const nextUrl = normalizeBrowserUrl(url); + tab.url = nextUrl; + if (tab.id === this.activeTabId) { + this.layoutActiveView(); + } + this.sendState(); + void tab.view.webContents.loadURL(nextUrl).catch((error) => { + this.emitAutomationEvent({ + kind: "page.load_failed", + summary: `Navigation request failed: ${formatError(error)}`, + tabId: tab.id, + title: tab.title, + url: nextUrl + }); + }); + this.emitAutomationEvent({ + kind: "page.navigation_requested", + summary: `Navigation requested: ${nextUrl}`, + tabId: tab.id, + title: tab.title, + url: nextUrl + }); + return this.getState(); + } + + goBackAutomationTab(tabId?: string): BuiltInBrowserState { + const tab = this.getTab(tabId); + tab?.view.webContents.navigationHistory.goBack(); + if (tab) { + this.emitAutomationEvent({ + kind: "tab.go_back", + summary: `Go back in tab ${tab.id}.`, + tabId: tab.id, + title: tab.title, + url: tab.url + }); + } + return this.getState(); + } + + goForwardAutomationTab(tabId?: string): BuiltInBrowserState { + const tab = this.getTab(tabId); + tab?.view.webContents.navigationHistory.goForward(); + if (tab) { + this.emitAutomationEvent({ + kind: "tab.go_forward", + summary: `Go forward in tab ${tab.id}.`, + tabId: tab.id, + title: tab.title, + url: tab.url + }); + } + return this.getState(); + } + + reloadAutomationTab(tabId?: string): BuiltInBrowserState { + const tab = this.getTab(tabId); + tab?.view.webContents.reload(); + if (tab) { + this.emitAutomationEvent({ + kind: "tab.reload", + summary: `Reload tab ${tab.id}.`, + tabId: tab.id, + title: tab.title, + url: tab.url + }); + } + return this.getState(); + } + + getAutomationWebContents(tabId?: string): WebContents { + const tab = this.getTab(tabId); + if (!tab || tab.view.webContents.isDestroyed()) { + throw new Error("Browser tab is unavailable."); + } + return tab.view.webContents; } private registerIpcHandlers(): void { @@ -110,6 +336,14 @@ class BuiltInBrowserService { this.assertBrowserSender(event); return this.getState(); }); + ipcMain.handle(IPC_CHANNELS.browserStartChromeLoginImport, async (event, request: ChromeLoginImportRequest) => { + this.assertBrowserSender(event); + return await chromeLoginImportService.createJob(request); + }); + ipcMain.handle(IPC_CHANNELS.browserGetChromeLoginImport, (event, id: string) => { + this.assertBrowserSender(event); + return chromeLoginImportService.getJob(typeof id === "string" ? id : ""); + }); ipcMain.handle(IPC_CHANNELS.browserNewTab, (event, url?: string) => { this.assertBrowserSender(event); this.createTab(url); @@ -145,6 +379,34 @@ class BuiltInBrowserService { this.getTab(tabId)?.view.webContents.reload(); return this.getState(); }); + ipcMain.handle(IPC_CHANNELS.browserResolveAutomationHandoff, (event, status?: string) => { + this.assertBrowserSender(event); + return this.resolveAutomationHandoff(status === "dismissed" ? "dismissed" : "completed"); + }); + } + + private ensureWindow(): BrowserWindow { + const window = this.window && !this.window.isDestroyed() ? this.window : this.createWindow(); + if (this.tabs.size === 0) { + this.createTab(browserHomeUrl); + } + return window; + } + + private showAutomationWindow(): void { + const window = this.ensureWindow(); + if (window.isMinimized()) { + window.restore(); + } + window.show(); + window.focus(); + } + + private hideAutomationWindow(): void { + const window = this.window; + if (window && !window.isDestroyed()) { + window.hide(); + } } private createWindow(): BrowserWindow { @@ -187,15 +449,12 @@ class BuiltInBrowserService { this.window = undefined; } }); - window.once("ready-to-show", () => { - if (!window.isDestroyed()) { - window.show(); - } - }); window.webContents.setWindowOpenHandler(() => ({ action: "deny" })); window.webContents.on("did-finish-load", () => this.sendState()); - void window.loadURL(this.resolveRendererUrl("pages/browser/index.html")); + void window.loadURL(this.resolveRendererUrl("pages/browser/index.html")).catch((error) => { + console.warn(`[browser] Failed to load browser chrome: ${formatError(error)}`); + }); return window; } @@ -225,8 +484,23 @@ class BuiltInBrowserService { this.window?.contentView.addChildView(view); view.setVisible(false); this.selectTab(tab.id); - void view.webContents.loadURL(tab.url); + void view.webContents.loadURL(tab.url).catch((error) => { + this.emitAutomationEvent({ + kind: "page.load_failed", + summary: `Initial tab load failed: ${formatError(error)}`, + tabId: tab.id, + title: tab.title, + url: tab.url + }); + }); this.sendState(); + this.emitAutomationEvent({ + kind: "tab.created", + summary: `Created tab ${tab.id}.`, + tabId: tab.id, + title: tab.title, + url: tab.url + }); return tab; } @@ -234,7 +508,14 @@ class BuiltInBrowserService { const { webContents } = tab.view; webContents.setWindowOpenHandler(({ url }) => { if (isHttpUrl(url)) { - this.createTab(url); + const opened = this.createTab(url); + this.emitAutomationEvent({ + kind: "tab.opened", + summary: `Opened new tab from window.open: ${url}`, + tabId: opened.id, + title: opened.title, + url: opened.url + }); } return { action: "deny" }; }); @@ -244,14 +525,35 @@ class BuiltInBrowserService { webContents.on("page-title-updated", (_event, title) => { tab.title = title || titleFromUrl(tab.url); this.sendState(); + this.emitAutomationEvent({ + kind: "page.title", + summary: `Title updated: ${tab.title}`, + tabId: tab.id, + title: tab.title, + url: tab.url + }); }); webContents.on("did-start-loading", () => { tab.isLoading = true; this.updateTabNavigationState(tab); + this.emitAutomationEvent({ + kind: "page.loading_started", + summary: `Loading started: ${tab.url}`, + tabId: tab.id, + title: tab.title, + url: tab.url + }); }); webContents.on("did-stop-loading", () => { tab.isLoading = false; this.updateTabNavigationState(tab); + this.emitAutomationEvent({ + kind: "page.loading_stopped", + summary: `Loading stopped: ${tab.url}`, + tabId: tab.id, + title: tab.title, + url: tab.url + }); }); webContents.on("did-navigate", (_event, url) => { tab.url = url; @@ -260,6 +562,13 @@ class BuiltInBrowserService { this.layoutActiveView(); } this.updateTabNavigationState(tab); + this.emitAutomationEvent({ + kind: "page.navigation", + summary: `Navigated to ${url}`, + tabId: tab.id, + title: tab.title, + url + }); }); webContents.on("did-navigate-in-page", (_event, url) => { tab.url = url; @@ -267,8 +576,15 @@ class BuiltInBrowserService { this.layoutActiveView(); } this.updateTabNavigationState(tab); + this.emitAutomationEvent({ + kind: "page.navigation_in_page", + summary: `In-page navigation to ${url}`, + tabId: tab.id, + title: tab.title, + url + }); }); - webContents.on("did-fail-load", (_event, errorCode, _errorDescription, validatedUrl) => { + webContents.on("did-fail-load", (_event, errorCode, errorDescription, validatedUrl) => { if (errorCode !== -3) { tab.isLoading = false; tab.url = validatedUrl || tab.url; @@ -276,6 +592,35 @@ class BuiltInBrowserService { this.layoutActiveView(); } this.updateTabNavigationState(tab); + this.emitAutomationEvent({ + errorCode, + errorDescription, + kind: "page.load_failed", + summary: `Load failed (${errorCode}): ${errorDescription}`, + tabId: tab.id, + title: tab.title, + url: tab.url + }); + } + }); + webContents.on("dom-ready", () => { + this.emitAutomationEvent({ + kind: "page.dom_ready", + summary: `DOM ready: ${tab.url}`, + tabId: tab.id, + title: tab.title, + url: tab.url + }); + }); + webContents.on("console-message", (_event, level, message) => { + if (level >= 2) { + this.emitAutomationEvent({ + kind: "runtime.console", + summary: message, + tabId: tab.id, + title: tab.title, + url: tab.url + }); } }); webContents.on("destroyed", () => { @@ -286,6 +631,13 @@ class BuiltInBrowserService { this.activeTabId = this.tabOrder[0]; } this.sendState(); + this.emitAutomationEvent({ + kind: "tab.destroyed", + summary: `Destroyed tab ${tab.id}.`, + tabId: tab.id, + title: tab.title, + url: tab.url + }); } }); } @@ -403,6 +755,13 @@ class BuiltInBrowserService { selected.view.webContents.focus(); } this.sendState(); + this.emitAutomationEvent({ + kind: "tab.activated", + summary: `Activated tab ${tabId}.`, + tabId, + title: selected.title, + url: selected.url + }); } private closeTab(tabId: string): void { @@ -415,6 +774,13 @@ class BuiltInBrowserService { this.tabs.delete(tabId); this.tabOrder = this.tabOrder.filter((id) => id !== tabId); tab.view.webContents.close({ waitForBeforeUnload: false }); + this.emitAutomationEvent({ + kind: "tab.closed", + summary: `Closed tab ${tabId}.`, + tabId, + title: tab.title, + url: tab.url + }); if (this.tabOrder.length === 0) { this.createTab(browserHomeUrl); @@ -440,8 +806,23 @@ class BuiltInBrowserService { if (tab.id === this.activeTabId) { this.layoutActiveView(); } - void tab.view.webContents.loadURL(nextUrl); + void tab.view.webContents.loadURL(nextUrl).catch((error) => { + this.emitAutomationEvent({ + kind: "page.load_failed", + summary: `Navigation request failed: ${formatError(error)}`, + tabId: tab.id, + title: tab.title, + url: nextUrl + }); + }); this.sendState(); + this.emitAutomationEvent({ + kind: "page.navigation_requested", + summary: `Navigation requested: ${nextUrl}`, + tabId: tab.id, + title: tab.title, + url: nextUrl + }); } private getTab(tabId?: string): BrowserTab | undefined { @@ -469,17 +850,22 @@ class BuiltInBrowserService { const { height, width } = window.getContentBounds(); activeTab.view.setVisible(true); activeTab.view.setBounds({ - height: Math.max(0, height - browserChromeHeight), + height: Math.max(0, height - this.browserChromeHeight()), width, x: 0, - y: browserChromeHeight + y: this.browserChromeHeight() }); } + private browserChromeHeight(): number { + return browserChromeBaseHeight + (this.automationHandoff ? browserHandoffToolbarHeight : 0); + } + private getState(): BuiltInBrowserState { return { activeTabId: this.activeTabId, apps: this.apps.map((app) => ({ ...app })), + ...(this.automationHandoff ? { automationHandoff: { ...this.automationHandoff } } : {}), tabs: this.tabOrder .map((id) => this.tabs.get(id)) .filter((tab): tab is BrowserTab => Boolean(tab)) @@ -512,6 +898,27 @@ class BuiltInBrowserService { this.activeTabId = undefined; } + private emitAutomationEvent(event: Omit): void { + const nextEvent: BrowserAutomationEvent = { + ...event, + seq: ++this.automationEventSeq, + ts: Date.now(), + windowId: browserAutomationWindowId + }; + this.automationEventHistory.push(nextEvent); + this.pruneAutomationEventHistory(nextEvent.ts); + this.automationEvents.emit("event", nextEvent); + } + + private pruneAutomationEventHistory(now = Date.now()): void { + while (this.automationEventHistory.length > 0 && now - this.automationEventHistory[0].ts > maxAutomationEventAgeMs) { + this.automationEventHistory.shift(); + } + if (this.automationEventHistory.length > maxAutomationEventHistory) { + this.automationEventHistory.splice(0, this.automationEventHistory.length - maxAutomationEventHistory); + } + } + private resolveRendererUrl(relativeHtmlPath: string): string { return pathToFileURL(path.join(__dirname, "../renderer", relativeHtmlPath)).toString(); } @@ -619,9 +1026,22 @@ function titleFromUrl(value: string): string { } } -function electronProxyRules(endpoint: string): string { - const parsed = new URL(endpoint); - const host = parsed.hostname.includes(":") ? `[${parsed.hostname}]` : parsed.hostname; - const port = parsed.port || (parsed.protocol === "https:" ? "443" : "80"); - return `http=${host}:${port};https=${host}:${port}`; +function defaultAutomationHandoffMessage(kind?: BuiltInBrowserAutomationHandoffKind): string { + if (kind === "login_required") { + return "Please sign in on this page, then click Done."; + } + if (kind === "verification_code") { + return "Please enter the verification code, then click Done."; + } + if (kind === "human_verification") { + return "Please complete the human verification, then click Done."; + } + if (kind === "blocked") { + return "Please resolve the blocker on this page, then click Done."; + } + return "Please complete the requested browser step, then click Done."; +} + +function formatError(error: unknown): string { + return error instanceof Error ? error.message : String(error); } diff --git a/packages/electron/src/main/chrome-login-import.ts b/packages/electron/src/main/chrome-login-import.ts new file mode 100644 index 0000000..850dd17 --- /dev/null +++ b/packages/electron/src/main/chrome-login-import.ts @@ -0,0 +1,593 @@ +import { randomUUID } from "node:crypto"; +import http, { type IncomingMessage, type Server, type ServerResponse } from "node:http"; +import { BrowserWindow, session, shell } from "electron"; +import type { + ChromeLoginImportCookie, + ChromeLoginImportJob, + ChromeLoginImportLocalStorage, + ChromeLoginImportRequest, + ChromeLoginImportResult, + ChromeLoginImportTarget +} from "@ccr/core/contracts/app"; + +type ServerInfo = { + server: Server; + url: string; +}; + +type StoredChromeLoginImportJob = ChromeLoginImportJob; + +type CookieSetDetails = Parameters[0]; + +const browserPartition = "persist:ccr-built-in-browser"; +const webSearchPartition = "persist:ccr-browser-web-search-mcp"; +const importJobTtlMs = 5 * 60_000; +const maxImportRequestBytes = 16 * 1024 * 1024; +const maxStoredErrors = 20; +const localStorageWriteTimeoutMs = 15_000; + +class ChromeLoginImportService { + private readonly jobs = new Map(); + private serverInfo?: ServerInfo; + private serverStartPromise?: Promise; + + async createJob(request: ChromeLoginImportRequest): Promise { + const domains = uniqueStrings(request.domains.map(normalizeImportDomain).filter((item): item is string => Boolean(item))); + if (domains.length === 0) { + throw new Error("At least one Chrome import domain is required."); + } + + const target = normalizeImportTarget(request.target); + const serverInfo = await this.ensureServer(); + const id = randomUUID(); + const now = Date.now(); + const job: StoredChromeLoginImportJob = { + confirmUrl: `${serverInfo.url}/chrome-import/jobs/${id}/confirm`, + createdAt: now, + domains, + endpointUrl: serverInfo.url, + expiresAt: now + importJobTtlMs, + id, + importUrl: `${serverInfo.url}/chrome-import/jobs/${id}`, + status: "pending", + target + }; + this.jobs.set(id, job); + this.pruneJobs(); + if (request.openConfirmationPage) { + await this.openConfirmationPage(job.id); + } + return cloneJob(job); + } + + getJob(id: string): ChromeLoginImportJob | undefined { + const job = this.jobs.get(id); + if (!job) { + return undefined; + } + this.refreshExpiredJob(job); + return cloneJob(job); + } + + async openConfirmationPage(id: string): Promise { + const job = this.jobs.get(id); + if (!job) { + throw new Error("Chrome login import job was not found."); + } + this.refreshExpiredJob(job); + if (job.status !== "pending") { + throw new Error(`Chrome login import job is ${job.status}.`); + } + await shell.openExternal(job.confirmUrl); + return cloneJob(job); + } + + private async ensureServer(): Promise { + if (this.serverInfo) { + return this.serverInfo; + } + if (this.serverStartPromise) { + return this.serverStartPromise; + } + + this.serverStartPromise = new Promise((resolve, reject) => { + const server = http.createServer((request, response) => { + void this.handleRequest(request, response).catch((error) => { + sendJson(response, 500, { error: { message: formatError(error) } }); + }); + }); + server.once("error", reject); + server.listen(0, "127.0.0.1", () => { + server.off("error", reject); + const address = server.address(); + if (!address || typeof address === "string") { + server.close(); + reject(new Error("Chrome login import server failed to start.")); + return; + } + const info = { + server, + url: `http://127.0.0.1:${address.port}` + }; + this.serverInfo = info; + resolve(info); + }); + }).finally(() => { + this.serverStartPromise = undefined; + }); + return this.serverStartPromise; + } + + private async handleRequest(request: IncomingMessage, response: ServerResponse): Promise { + setCorsHeaders(response); + if (request.method === "OPTIONS") { + response.writeHead(204); + response.end(); + return; + } + + const path = request.url ? new URL(request.url, "http://127.0.0.1").pathname : "/"; + const match = path.match(/^\/chrome-import\/jobs\/([^/]+)(?:\/(confirm|cookies))?$/); + if (!match) { + sendJson(response, 404, { error: { message: "Chrome login import endpoint not found." } }); + return; + } + + const id = decodeURIComponent(match[1]); + const job = this.jobs.get(id); + if (!job) { + sendJson(response, 404, { error: { message: "Chrome login import job was not found." } }); + return; + } + this.refreshExpiredJob(job); + if (job.status === "expired") { + sendJson(response, 410, { error: { message: "Chrome login import job expired." }, job: cloneJob(job) }); + return; + } + + const action = match[2]; + if (request.method === "GET" && action === "confirm") { + sendHtml(response, 200, confirmationPageHtml(job)); + return; + } + + if (request.method === "GET" && !action) { + sendJson(response, 200, { job: cloneJob(job) }); + return; + } + + if (request.method === "POST" && action === "cookies") { + if (job.status !== "pending") { + sendJson(response, 409, { + error: { + message: `Chrome login import job is ${job.status}.` + }, + job: cloneJob(job) + }); + return; + } + const payload = await readJsonRequest(request); + const result = await this.importLoginState(job, payload); + sendJson(response, 200, { job: cloneJob(job), result }); + return; + } + + sendJson(response, 405, { error: { message: "Unsupported Chrome login import method." } }); + } + + private async importLoginState(job: StoredChromeLoginImportJob, payload: unknown): Promise { + const importPayload = readImportPayload(payload); + const partitions = targetPartitions(job.target); + const result: ChromeLoginImportResult = { + completedAt: Date.now(), + cookieImported: 0, + cookieSkipped: 0, + domains: [...job.domains], + imported: 0, + localStorageImported: 0, + localStorageSkipped: 0, + partitions, + skipped: 0 + }; + const errors: string[] = []; + + for (const cookie of importPayload.cookies) { + const normalized = normalizeChromeCookie(cookie, job.domains); + if (!normalized) { + result.cookieSkipped += 1; + continue; + } + + try { + await Promise.all(partitions.map((partition) => session.fromPartition(partition).cookies.set(normalized))); + result.cookieImported += 1; + } catch (error) { + result.cookieSkipped += 1; + if (errors.length < maxStoredErrors) { + errors.push(`${normalized.domain || new URL(normalized.url).hostname}:${normalized.name}: ${formatError(error)}`); + } + } + } + + await Promise.all(partitions.map((partition) => session.fromPartition(partition).cookies.flushStore())); + + for (const localStorageEntry of importPayload.localStorage) { + const normalized = normalizeLocalStorageEntry(localStorageEntry, job.domains); + if (!normalized) { + result.localStorageSkipped += 1; + continue; + } + + const itemCount = Object.keys(normalized.items).length; + if (itemCount === 0) { + result.localStorageSkipped += 1; + continue; + } + + try { + await Promise.all(partitions.map((partition) => writeLocalStorage(partition, normalized.origin, normalized.items))); + result.localStorageImported += itemCount; + } catch (error) { + result.localStorageSkipped += itemCount; + if (errors.length < maxStoredErrors) { + errors.push(`${normalized.origin}: localStorage: ${formatError(error)}`); + } + } + } + + result.imported = result.cookieImported + result.localStorageImported; + result.skipped = result.cookieSkipped + result.localStorageSkipped; + if (errors.length > 0) { + result.errors = errors; + } + job.result = result; + job.status = result.imported > 0 || result.skipped > 0 ? "completed" : "failed"; + return result; + } + + private refreshExpiredJob(job: StoredChromeLoginImportJob): void { + if (job.status === "pending" && Date.now() > job.expiresAt) { + job.status = "expired"; + } + } + + private pruneJobs(): void { + const cutoff = Date.now() - importJobTtlMs; + for (const [id, job] of this.jobs) { + this.refreshExpiredJob(job); + if (job.expiresAt < cutoff) { + this.jobs.delete(id); + } + } + } +} + +export const chromeLoginImportService = new ChromeLoginImportService(); + +function targetPartitions(target: ChromeLoginImportTarget): string[] { + return target === "browser-and-web-search" + ? [browserPartition, webSearchPartition] + : [browserPartition]; +} + +function normalizeChromeCookie(cookie: ChromeLoginImportCookie, allowedDomains: string[]): CookieSetDetails | undefined { + if (!isRecord(cookie) || typeof cookie.name !== "string" || typeof cookie.value !== "string") { + return undefined; + } + if (cookie.partitionKey !== undefined) { + return undefined; + } + + const rawDomain = readString(cookie.domain).toLowerCase(); + const cookieHost = normalizeCookieHost(rawDomain); + if (!cookieHost || !allowedDomains.some((domain) => cookieDomainMatches(cookieHost, domain))) { + return undefined; + } + + const path = normalizeCookiePath(cookie.path); + const expirationDate = typeof cookie.expirationDate === "number" && Number.isFinite(cookie.expirationDate) + ? cookie.expirationDate + : undefined; + if (expirationDate !== undefined && expirationDate <= Date.now() / 1000) { + return undefined; + } + + const secure = cookie.secure === true; + const details: CookieSetDetails = { + httpOnly: cookie.httpOnly === true, + name: cookie.name, + path, + secure, + url: cookieUrl(cookieHost, path, secure), + value: cookie.value + }; + if (cookie.hostOnly !== true) { + details.domain = rawDomain || cookieHost; + } + if (expirationDate !== undefined && cookie.session !== true) { + details.expirationDate = expirationDate; + } + const sameSite = normalizeSameSite(cookie.sameSite); + if (sameSite) { + details.sameSite = sameSite; + } + return details; +} + +function normalizeLocalStorageEntry( + entry: ChromeLoginImportLocalStorage, + allowedDomains: string[] +): ChromeLoginImportLocalStorage | undefined { + if (!isRecord(entry) || !isRecord(entry.items)) { + return undefined; + } + + let origin: URL; + try { + origin = new URL(readString(entry.origin)); + } catch { + return undefined; + } + + if (!["http:", "https:"].includes(origin.protocol)) { + return undefined; + } + if (!allowedDomains.some((domain) => cookieDomainMatches(origin.hostname.toLowerCase(), domain))) { + return undefined; + } + + const items: Record = {}; + for (const [key, value] of Object.entries(entry.items)) { + if (typeof key === "string" && typeof value === "string") { + items[key] = value; + } + } + return { + items, + origin: origin.origin + }; +} + +async function writeLocalStorage(partition: string, origin: string, items: Record): Promise { + const window = new BrowserWindow({ + height: 480, + paintWhenInitiallyHidden: true, + show: false, + skipTaskbar: true, + title: "CCR Chrome Login Import", + webPreferences: { + contextIsolation: true, + nodeIntegration: false, + partition, + sandbox: true, + webSecurity: true + }, + width: 640 + }); + try { + await withTimeout(window.webContents.loadURL(`${origin}/`), localStorageWriteTimeoutMs, "Timed out loading localStorage origin."); + const loadedOrigin = new URL(window.webContents.getURL()).origin; + if (loadedOrigin !== origin) { + throw new Error(`Origin redirected to ${loadedOrigin}.`); + } + const entries = Object.entries(items); + await window.webContents.executeJavaScript( + `(() => { + const entries = ${JSON.stringify(entries)}; + for (const [key, value] of entries) { + window.localStorage.setItem(key, value); + } + return entries.length; + })()`, + true + ); + } finally { + if (!window.isDestroyed()) { + window.destroy(); + } + } +} + +function normalizeImportTarget(value: unknown): ChromeLoginImportTarget { + return value === "browser-and-web-search" ? "browser-and-web-search" : "browser"; +} + +function normalizeImportDomain(value: unknown): string | undefined { + const raw = readString(value).toLowerCase(); + if (!raw) { + return undefined; + } + try { + const parsed = new URL(raw.includes("://") ? raw : `https://${raw}`); + return normalizeCookieHost(parsed.hostname); + } catch { + return normalizeCookieHost(raw.replace(/^\*\./, "").split("/")[0] || ""); + } +} + +function normalizeCookieHost(value: string): string | undefined { + const host = value.trim().replace(/^\./, "").toLowerCase(); + if (!host || host.includes("*") || host.includes("/") || host.includes(" ")) { + return undefined; + } + return host; +} + +function normalizeCookiePath(value: unknown): string { + const path = readString(value); + return path.startsWith("/") ? path : "/"; +} + +function cookieUrl(host: string, path: string, secure: boolean): string { + const normalizedPath = path.startsWith("/") ? path : "/"; + return `${secure ? "https" : "http"}://${host}${normalizedPath}`; +} + +function cookieDomainMatches(cookieHost: string, allowedDomain: string): boolean { + return cookieHost === allowedDomain || cookieHost.endsWith(`.${allowedDomain}`); +} + +function normalizeSameSite(value: unknown): CookieSetDetails["sameSite"] | undefined { + return value === "lax" || value === "strict" || value === "no_restriction" || value === "unspecified" + ? value + : undefined; +} + +function readImportPayload(payload: unknown): { + cookies: ChromeLoginImportCookie[]; + localStorage: ChromeLoginImportLocalStorage[]; +} { + if (!isRecord(payload)) { + throw new Error("Chrome login import payload must be an object."); + } + const cookies = Array.isArray(payload.cookies) + ? payload.cookies.filter(isRecord) as ChromeLoginImportCookie[] + : []; + const localStorage = Array.isArray(payload.localStorage) + ? payload.localStorage.filter(isRecord) as ChromeLoginImportLocalStorage[] + : []; + if (cookies.length === 0 && localStorage.length === 0) { + throw new Error("Chrome login import payload must include cookies or localStorage."); + } + return { cookies, localStorage }; +} + +function cloneJob(job: StoredChromeLoginImportJob): ChromeLoginImportJob { + return { + ...job, + domains: [...job.domains], + ...(job.result + ? { + result: { + ...job.result, + domains: [...job.result.domains], + ...(job.result.errors ? { errors: [...job.result.errors] } : {}), + partitions: [...job.result.partitions] + } + } + : {}) + }; +} + +async function readJsonRequest(request: IncomingMessage): Promise { + const chunks: Buffer[] = []; + let total = 0; + for await (const chunk of request) { + const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk); + total += buffer.byteLength; + if (total > maxImportRequestBytes) { + throw new Error("Chrome login import payload is too large."); + } + chunks.push(buffer); + } + return JSON.parse(Buffer.concat(chunks).toString("utf8")) as unknown; +} + +function sendJson(response: ServerResponse, statusCode: number, body: unknown): void { + if (!response.headersSent) { + response.writeHead(statusCode, { "content-type": "application/json" }); + } + response.end(`${JSON.stringify(body)}\n`); +} + +function sendHtml(response: ServerResponse, statusCode: number, body: string): void { + if (!response.headersSent) { + response.writeHead(statusCode, { + "content-security-policy": "default-src 'none'; style-src 'unsafe-inline'; script-src 'none'; connect-src http://127.0.0.1:* http://localhost:*", + "content-type": "text/html; charset=utf-8" + }); + } + response.end(body); +} + +function confirmationPageHtml(job: StoredChromeLoginImportJob): string { + const domains = job.domains.map((domain) => `
  • ${escapeHtml(domain)}
  • `).join(""); + return ` + + + + + CCR Chrome Login Import + + + +
    +

    Import Chrome Login State into CCR

    +

    CCR is requesting permission to import cookies and localStorage for these domains into the in-app browser.

    +
      ${domains}
    + +
    Install or enable the CCR Login Import extension in Chrome to continue.
    +
    + +`; +} + +function setCorsHeaders(response: ServerResponse): void { + response.setHeader("access-control-allow-headers", "content-type, x-ccr-login-import"); + response.setHeader("access-control-allow-methods", "GET, POST, OPTIONS"); + response.setHeader("access-control-allow-origin", "*"); +} + +function uniqueStrings(values: string[]): string[] { + return [...new Set(values)]; +} + +function readString(value: unknown): string { + return typeof value === "string" ? value.trim() : ""; +} + +function isRecord(value: unknown): value is Record { + return Boolean(value) && typeof value === "object" && !Array.isArray(value); +} + +function escapeHtml(value: string): string { + return value.replace(/[&<>"']/g, (char) => { + switch (char) { + case "&": + return "&"; + case "<": + return "<"; + case ">": + return ">"; + case "\"": + return """; + default: + return "'"; + } + }); +} + +function withTimeout(promise: Promise, timeoutMs: number, message: string): Promise { + let timeout: NodeJS.Timeout | undefined; + const timeoutPromise = new Promise((_resolve, reject) => { + timeout = setTimeout(() => reject(new Error(message)), timeoutMs); + }); + return Promise.race([promise, timeoutPromise]).finally(() => { + if (timeout) { + clearTimeout(timeout); + } + }); +} + +function formatError(error: unknown): string { + return error instanceof Error ? error.message : String(error); +} diff --git a/packages/electron/src/main/electron-web-search-mcp.ts b/packages/electron/src/main/electron-web-search-mcp.ts index 0badf3b..e59ec3a 100644 --- a/packages/electron/src/main/electron-web-search-mcp.ts +++ b/packages/electron/src/main/electron-web-search-mcp.ts @@ -70,6 +70,7 @@ type BrowserSearchRequest = BrowserSearchInput & { type BrowserSearchResult = { content?: string; + diagnostics?: string[]; snippet?: string; title: string; url: string; @@ -597,11 +598,15 @@ class HiddenBrowserSearchPool { return { ...result, ...(content ? { content } : {}), + ...(!content ? { diagnostics: appendSearchResultDiagnostic(result.diagnostics, "No extractable page content found.") } : {}), ...(snippet ? { snippet } : {}), ...(page.title && page.title.length > result.title.length ? { title: page.title.slice(0, 240) } : {}) }; - } catch { - return result; + } catch (error) { + return { + ...result, + diagnostics: appendSearchResultDiagnostic(result.diagnostics, `Page extraction failed: ${formatError(error)}`) + }; } finally { if (worker) { this.release(worker); @@ -635,7 +640,16 @@ async function pollSearchResults(webContents: WebContents, request: BrowserSearc const deadline = Date.now() + request.timeoutMs; let last: BrowserSearchPageResult = { results: [] }; while (Date.now() < deadline) { - last = await webContents.executeJavaScript(searchResultExtractionScript(request.engine, request.count), true) as BrowserSearchPageResult; + try { + last = await withTimeout( + webContents.executeJavaScript(searchResultExtractionScript(request.engine, request.count), true) as Promise, + Math.max(100, Math.min(1000, deadline - Date.now())), + "Browser search result extraction timed out." + ); + } catch { + await sleep(150); + continue; + } if (last.blocked || (last.results?.length ?? 0) >= Math.min(request.count, 3)) { return last; } @@ -823,18 +837,46 @@ function searchResultExtractionScript(engine: BrowserSearchEngine, count: number const host = url.hostname.toLowerCase(); const path = url.pathname.toLowerCase(); if (host === location.hostname.toLowerCase() && (path === "/" || path === "/search" || path.startsWith("/search/"))) return true; - if (host.includes("google.") && /\\/(preferences|advanced_search|intl|policies|support|sorry)/.test(path)) return true; - if (host.endsWith("bing.com") && /\\/(search|account|profile|images|videos|maps|news)/.test(path)) return true; - if (host.endsWith("duckduckgo.com") && path === "/") return true; + if (host.includes("google.") && /\\/(preferences|advanced_search|intl|policies|support|sorry|search|aclk|imgres)/.test(path)) return true; + if (host.endsWith("bing.com") && /\\/(search|account|profile|images|videos|maps|news|aclick)/.test(path)) return true; + if (host.endsWith("bing.com") && path.startsWith("/ck/")) return true; + if (host.endsWith("duckduckgo.com") && (path === "/" || path.startsWith("/settings") || path.startsWith("/y.js"))) return true; return false; } function resultContainer(anchor, title) { + const root = anchor && anchor.closest("li.b_algo, li.b_ad, div.g, div[data-sokoban-container], div[data-testid='result'], article, .result, .result__body, .web-result"); + if (root) return root; let node = anchor; for (let depth = 0; node && depth < 8; depth += 1, node = node.parentElement) { + if (node.matches && node.matches("nav, header, footer, form, aside")) return undefined; + if (node.matches && node.matches("main, #search, #b_results, #rso")) break; const text = compact(node.textContent || ""); if (text.length > title.length + 40) return node; } - return anchor.closest("li.b_algo, article, div.g, div[data-sokoban-container], div[data-testid='result'], .result, li, div"); + return undefined; + } + function isLikelyOrganicResult(anchor, container) { + if (!anchor || !container) return false; + if (anchor.closest("nav, header, footer, form, aside")) return false; + if (anchor.closest("li.b_algo, div.g, div[data-sokoban-container], div[data-testid='result'], article, .result, .result__body, .web-result")) return true; + const heading = anchor.closest("h1, h2, h3, [role='heading']"); + const searchRegion = anchor.closest("main, #search, #b_results, #rso"); + return Boolean(heading && searchRegion); + } + function isAdOrUtilityResult(anchor, container) { + let node = anchor; + for (let depth = 0; node && depth < 6; depth += 1, node = node.parentElement) { + const attributes = [ + node.getAttribute && node.getAttribute("aria-label"), + node.getAttribute && node.getAttribute("data-text-ad"), + node.getAttribute && node.getAttribute("data-testid"), + node.className + ].filter(Boolean).join(" "); + if (/\\b(?:ad|ads|sponsored|promo|shopping)\\b|广告|赞助|推廣/i.test(attributes)) return true; + } + const firstText = compact(container && container.textContent).slice(0, 180); + if (/^(?:ad|ads|sponsored|promo|shopping)\\b|^(?:广告|赞助|推廣)/i.test(firstText)) return true; + return false; } function cleanSnippetCandidate(value, title, url) { const host = (() => { try { return new URL(url).hostname.replace(/^www\\./, ""); } catch (_) { return ""; } })(); @@ -881,6 +923,7 @@ function searchResultExtractionScript(engine: BrowserSearchEngine, count: number const title = titleFor(anchor, titleOverride); if (!title || title.length < 2) return; const container = resultContainer(anchor, title); + if (!isLikelyOrganicResult(anchor, container) || isAdOrUtilityResult(anchor, container)) return; seen.add(url); results.push({ snippet: snippetFor(anchor, container, title, url), @@ -908,19 +951,22 @@ function searchResultExtractionScript(engine: BrowserSearchEngine, count: number collect(".result__a"); collect("article h2 a[href]"); } + function collectGenericHeadings() { + collect("main h1 a[href], main h2 a[href], main h3 a[href], #search h3, #b_results h2 a[href], #rso h3, article h2 a[href], article h3 a[href]"); + } if (engine === "google") collectGoogle(); if (engine === "bing") collectBing(); if (engine === "duckduckgo") collectDuckDuckGo(); - collect("main a[href], #search a[href], #b_results a[href], article a[href], a[href]"); + collectGenericHeadings(); return { results, title: document.title || "" }; function detectBlocked() { const text = compact(document.body && document.body.innerText).toLowerCase(); if (!text) return ""; - if (text.includes("unusual traffic") || text.includes("detected unusual") || text.includes("not a robot") || text.includes("captcha")) { + if (text.includes("unusual traffic") || text.includes("detected unusual") || text.includes("not a robot") || text.includes("captcha") || text.includes("验证码") || text.includes("人机验证")) { return "Search engine returned an anti-bot or CAPTCHA page."; } - if (text.includes("before you continue to google") || text.includes("consent.google")) { + if (text.includes("before you continue to google") || text.includes("consent.google") || text.includes("cookie consent")) { return "Google returned a consent page instead of search results."; } return ""; @@ -996,11 +1042,16 @@ function formatSearchResponse( `${index + 1}. ${result.title}`, `URL: ${result.url}`, result.snippet ? `Snippet: ${result.snippet}` : "", - result.content ? `Extracted content: ${result.content}` : "" + result.content ? `Extracted content: ${result.content}` : "", + result.diagnostics?.length ? `Diagnostics: ${result.diagnostics.join("; ")}` : "" ].filter(Boolean).join("\n")) ].filter(Boolean).join("\n\n"); } +function appendSearchResultDiagnostic(existing: string[] | undefined, message: string): string[] { + return uniqueStrings([...(existing ?? []), message]); +} + function uniqueSearchResults(results: BrowserSearchResult[]): BrowserSearchResult[] { const seen = new Set(); const unique: BrowserSearchResult[] = []; diff --git a/packages/electron/src/main/ipc.ts b/packages/electron/src/main/ipc.ts index 53afe4c..a639fbc 100644 --- a/packages/electron/src/main/ipc.ts +++ b/packages/electron/src/main/ipc.ts @@ -1,7 +1,6 @@ import { app, BrowserWindow, dialog, ipcMain, nativeImage, shell, type OpenDialogOptions, type Rectangle, type SaveDialogOptions } from "electron"; import { randomUUID } from "node:crypto"; import { existsSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs"; -import net from "node:net"; import path from "node:path"; import { deflateSync, inflateSync } from "node:zlib"; import { loadPersistedAppSetting, replacePersistedAppSetting } from "@ccr/core/config/app-config-store"; @@ -133,7 +132,6 @@ ipcMain.handle(IPC_CHANNELS.appListMcpServerTools, async (_event, serverName: st }); ipcMain.handle(IPC_CHANNELS.appOpenBuiltInBrowser, async () => { const config = await loadAppConfig(); - await ensureBuiltInBrowserProxyReady(config); await builtInBrowserService.open(config); }); ipcMain.handle(IPC_CHANNELS.appCloseTray, () => { @@ -361,157 +359,6 @@ function logProfileApplyResult(result: ProfileApplyResult): void { } } -type ProxyConnectProbeResult = { - detail?: string; - ok: boolean; -}; - -const browserProxyConnectProbeTarget = "claude.ai:443"; -const proxyConnectProbeTimeoutMs = 3000; - -async function ensureBuiltInBrowserProxyReady(config: AppConfig): Promise { - if (!config.proxy.enabled) { - return; - } - - let proxyStatus = proxyService.getStatus(); - if (proxyStatus.state === "running" && proxyStatus.endpoint) { - const probe = await probeProxyConnect(proxyStatus.endpoint); - if (probe.ok) { - return; - } - console.warn(`[browser] Proxy CONNECT probe failed at ${proxyStatus.endpoint}; restarting proxy: ${probe.detail || "unknown error"}`); - } - - const status = await gatewayService.start(config); - if (status.state === "error") { - throw new Error(status.lastError || "Failed to start proxy mode."); - } - - proxyStatus = proxyService.getStatus(); - if (proxyStatus.state !== "running" || !proxyStatus.endpoint) { - throw new Error(proxyStatus.lastError || "Proxy mode is not running."); - } - - const probe = await probeProxyConnect(proxyStatus.endpoint); - if (probe.ok) { - return; - } - - console.warn( - `[browser] Shared proxy endpoint ${proxyStatus.endpoint} still does not accept CONNECT after restart; starting dedicated proxy endpoint: ${probe.detail || "unknown error"}` - ); - const dedicatedProxyConfig = await createBuiltInBrowserProxyConfig(config); - const dedicatedProxyStatus = await proxyService.start(dedicatedProxyConfig); - if (dedicatedProxyStatus.state !== "running" || !dedicatedProxyStatus.endpoint) { - throw new Error(dedicatedProxyStatus.lastError || "Failed to start the dedicated proxy endpoint for the built-in browser."); - } - - const dedicatedProbe = await probeProxyConnect(dedicatedProxyStatus.endpoint); - if (!dedicatedProbe.ok) { - const detail = dedicatedProbe.detail ? `: ${dedicatedProbe.detail}` : ""; - throw new Error(`Proxy mode is running at ${dedicatedProxyStatus.endpoint}, but HTTPS CONNECT is not available${detail}.`); - } -} - -async function createBuiltInBrowserProxyConfig(config: AppConfig): Promise { - return { - ...config, - proxy: { - ...config.proxy, - host: "127.0.0.1", - port: await findAvailableLoopbackPort(), - systemProxy: false - } - }; -} - -function findAvailableLoopbackPort(): Promise { - return new Promise((resolve, reject) => { - const server = net.createServer(); - server.once("error", reject); - server.listen(0, "127.0.0.1", () => { - const address = server.address(); - server.close((error) => { - if (error) { - reject(error); - return; - } - if (!address || typeof address === "string") { - reject(new Error("Failed to allocate a local proxy port.")); - return; - } - resolve(address.port); - }); - }); - }); -} - -function probeProxyConnect(endpoint: string): Promise { - return new Promise((resolve) => { - let parsed: URL; - try { - parsed = new URL(endpoint); - } catch { - resolve({ detail: `Invalid proxy endpoint: ${endpoint}`, ok: false }); - return; - } - - const port = Number(parsed.port || (parsed.protocol === "https:" ? 443 : 80)); - if (!Number.isInteger(port) || port <= 0 || port > 65535) { - resolve({ detail: `Invalid proxy port in endpoint: ${endpoint}`, ok: false }); - return; - } - - const host = parsed.hostname.replace(/^\[|\]$/g, ""); - const socket = net.connect({ host, port }); - let response = ""; - let settled = false; - - const finish = (result: ProxyConnectProbeResult) => { - if (settled) { - return; - } - settled = true; - socket.destroy(); - resolve(result); - }; - const parseResponse = (): ProxyConnectProbeResult | undefined => { - const firstLine = response.split(/\r?\n/, 1)[0]?.trim(); - if (!firstLine) { - return undefined; - } - if (/^HTTP\/1\.[01]\s+200\b/i.test(firstLine)) { - return { ok: true }; - } - if (/^HTTP\/1\.[01]\s+\d{3}\b/i.test(firstLine)) { - return { detail: firstLine, ok: false }; - } - return { detail: `Unexpected response: ${firstLine}`, ok: false }; - }; - - socket.setTimeout(proxyConnectProbeTimeoutMs, () => { - finish({ detail: `Timed out after ${proxyConnectProbeTimeoutMs}ms`, ok: false }); - }); - socket.once("error", (error) => { - finish({ detail: formatError(error), ok: false }); - }); - socket.once("connect", () => { - socket.write(`CONNECT ${browserProxyConnectProbeTarget} HTTP/1.1\r\nHost: ${browserProxyConnectProbeTarget}\r\n\r\n`); - }); - socket.on("data", (chunk) => { - response += chunk.toString("latin1"); - const result = parseResponse(); - if (result) { - finish(result); - } - }); - socket.once("close", () => { - finish(parseResponse() ?? { detail: "Connection closed without a CONNECT response", ok: false }); - }); - }); -} - async function exportAppData(window: BrowserWindow | null): Promise { const exportedAt = new Date().toISOString(); const result = window diff --git a/packages/electron/src/main/main-app.ts b/packages/electron/src/main/main-app.ts index f71892a..9f3ab9a 100644 --- a/packages/electron/src/main/main-app.ts +++ b/packages/electron/src/main/main-app.ts @@ -11,6 +11,7 @@ import { syncLaunchAtLogin } from "./launch-at-login"; import { proxyService } from "@ccr/core/proxy/service"; import trayController from "./tray-controller"; import { appUpdateService } from "./update-service"; +import { browserAutomationMcpService } from "./browser-automation-mcp"; import { browserWebSearchMcpService } from "./electron-web-search-mcp"; import windowsManager from "./windows"; @@ -29,6 +30,7 @@ if (!gotTheLock) { } function startPrimaryInstance(): void { + gatewayService.setBrowserAutomationMcpIntegration(browserAutomationMcpService); gatewayService.setBrowserWebSearchMcpIntegration(browserWebSearchMcpService); deepLinkService.register(); deepLinkService.handleArgv(process.argv); diff --git a/packages/ui/src/pages/browser/main.tsx b/packages/ui/src/pages/browser/main.tsx index 578481e..11b5e90 100644 --- a/packages/ui/src/pages/browser/main.tsx +++ b/packages/ui/src/pages/browser/main.tsx @@ -1,7 +1,7 @@ import { useEffect, useMemo, useState, type FormEvent } from "react"; import { createRoot } from "react-dom/client"; -import { ArrowLeft, ArrowRight, Globe2, LoaderCircle, Plus, RotateCw, Search, X } from "lucide-react"; -import type { BuiltInBrowserState } from "@ccr/core/contracts/app"; +import { ArrowLeft, ArrowRight, Check, KeyRound, LoaderCircle, Plus, RotateCw, Search, UserRound, X } from "lucide-react"; +import type { BuiltInBrowserState, ChromeLoginImportJob, ChromeLoginImportRequest } from "@ccr/core/contracts/app"; declare global { interface Window { @@ -9,11 +9,14 @@ declare global { back: (tabId?: string) => Promise; closeTab: (tabId: string) => Promise; forward: (tabId?: string) => Promise; + getChromeLoginImport: (id: string) => Promise; getState: () => Promise; navigate: (url: string, tabId?: string) => Promise; newTab: (url?: string) => Promise; reload: (tabId?: string) => Promise; + resolveAutomationHandoff: (status: "completed" | "dismissed") => Promise; selectTab: (tabId: string) => Promise; + startChromeLoginImport: (request: ChromeLoginImportRequest) => Promise; onStateChanged: (callback: (state: BuiltInBrowserState) => void) => () => void; }; } @@ -29,10 +32,12 @@ function BrowserChrome() { const [state, setState] = useState(emptyState); const [addressDraft, setAddressDraft] = useState(""); const [homeDraft, setHomeDraft] = useState(""); + const [chromeImportJob, setChromeImportJob] = useState(); const activeTab = useMemo( () => state.tabs.find((tab) => tab.id === state.activeTabId), [state.activeTabId, state.tabs] ); + const handoff = state.automationHandoff; const homeVisible = activeTab?.url === browserHomeUrl; useEffect(() => { @@ -57,6 +62,25 @@ function BrowserChrome() { setHomeDraft(""); }, [activeTab?.id]); + useEffect(() => { + if (!chromeImportJob || chromeImportJob.status !== "pending") { + return; + } + const interval = window.setInterval(() => { + void window.ccrBrowser?.getChromeLoginImport(chromeImportJob.id).then((job) => { + if (!job) { + setChromeImportJob(undefined); + return; + } + setChromeImportJob(job); + if (job.status === "completed" && activeTab?.id && activeTabMatchesImport(activeTab.url, job.domains)) { + void run(window.ccrBrowser?.reload(activeTab.id)); + } + }); + }, 2000); + return () => window.clearInterval(interval); + }, [activeTab?.id, activeTab?.url, chromeImportJob]); + async function run(action: Promise | undefined): Promise { if (!action) { return; @@ -79,8 +103,53 @@ function BrowserChrome() { void run(window.ccrBrowser?.navigate(url, activeTab?.id)); } + async function startChromeLoginImport() { + const defaultDomain = activeTabDomain(activeTab?.url); + const rawDomains = window.prompt("Chrome domain(s) to import. Separate multiple domains with commas.", defaultDomain); + if (!rawDomains) { + return; + } + const domains = parseImportDomains(rawDomains); + if (domains.length === 0) { + return; + } + const job = await window.ccrBrowser?.startChromeLoginImport({ domains, openConfirmationPage: true }); + if (!job) { + return; + } + setChromeImportJob(job); + } + + function chromeImportTitle(): string { + if (!chromeImportJob) { + return "Import login from Chrome"; + } + if (chromeImportJob.status === "completed") { + return `Chrome import complete: ${chromeImportJob.result?.imported ?? 0} imported, ${chromeImportJob.result?.skipped ?? 0} skipped`; + } + if (chromeImportJob.status === "expired") { + return "Chrome import expired"; + } + if (chromeImportJob.status === "failed") { + return "Chrome import failed"; + } + return "Chrome import pending. Confirm it in the browser window."; + } + + async function handleChromeImportButton() { + if (chromeImportJob?.status === "pending") { + try { + await navigator.clipboard.writeText(chromeImportJob.confirmUrl); + } catch { + window.prompt("Open this Chrome import confirmation URL.", chromeImportJob.confirmUrl); + } + return; + } + await startChromeLoginImport(); + } + return ( -
    +
    @@ -149,8 +218,45 @@ function BrowserChrome() { spellCheck={false} value={addressDraft} /> + + {handoff ? ( +
    +
    + + {handoff.message} + {handoff.reason ? {handoff.reason} : null} +
    +
    + + +
    +
    + ) : null} + {homeVisible ? (
    @@ -189,6 +295,42 @@ function BrowserChrome() { const root = createRoot(document.getElementById("root") as HTMLElement); root.render(); +function activeTabDomain(url: string | undefined): string { + if (!url || url === browserHomeUrl) { + return ""; + } + try { + return new URL(url).hostname; + } catch { + return ""; + } +} + +function activeTabMatchesImport(url: string | undefined, domains: string[]): boolean { + const host = activeTabDomain(url).toLowerCase(); + return Boolean(host && domains.some((domain) => host === domain || host.endsWith(`.${domain}`))); +} + +function parseImportDomains(value: string): string[] { + return [...new Set(value + .split(/[,\n]/) + .map((item) => normalizeImportDomain(item)) + .filter((item): item is string => Boolean(item)))]; +} + +function normalizeImportDomain(value: string): string | undefined { + const raw = value.trim().toLowerCase(); + if (!raw) { + return undefined; + } + try { + return new URL(raw.includes("://") ? raw : `https://${raw}`).hostname; + } catch { + const domain = raw.replace(/^\*\./, "").split("/")[0]; + return domain && !domain.includes(" ") ? domain : undefined; + } +} + const style = document.createElement("style"); style.textContent = ` :root { @@ -231,6 +373,10 @@ style.textContent = ` width: 100%; } + .browser-shell.has-handoff { + grid-template-rows: 38px 44px 44px minmax(0, 1fr); + } + .tabs-row { -webkit-app-region: drag; align-items: end; @@ -325,7 +471,7 @@ style.textContent = ` border-bottom: 1px solid color-mix(in srgb, CanvasText 12%, transparent); display: grid; gap: 4px; - grid-template-columns: 32px 32px 32px minmax(0, 1fr); + grid-template-columns: 32px 32px 32px minmax(0, 1fr) 32px; padding: 6px 10px; } @@ -339,6 +485,11 @@ style.textContent = ` opacity: 0.4; } + .icon-button.active-import { + background: color-mix(in srgb, #0f766e 16%, transparent); + color: color-mix(in srgb, #0f766e 82%, CanvasText); + } + input { background: color-mix(in srgb, CanvasText 4%, Canvas); border: 1px solid color-mix(in srgb, CanvasText 12%, transparent); @@ -355,6 +506,79 @@ style.textContent = ` border-color: color-mix(in srgb, #2563eb 70%, CanvasText 30%); } + .automation-handoff { + -webkit-app-region: drag; + align-items: center; + background: color-mix(in srgb, #f59e0b 16%, Canvas); + border-bottom: 1px solid color-mix(in srgb, #92400e 24%, transparent); + display: grid; + gap: 10px; + grid-template-columns: minmax(0, 1fr) auto; + min-width: 0; + padding: 6px 10px; + } + + .handoff-copy { + align-items: center; + color: color-mix(in srgb, #78350f 76%, CanvasText); + display: flex; + gap: 8px; + min-width: 0; + } + + .handoff-message, + .handoff-reason { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .handoff-message { + font-size: 13px; + font-weight: 700; + } + + .handoff-reason { + color: color-mix(in srgb, CanvasText 58%, transparent); + font-size: 12px; + } + + .handoff-actions { + -webkit-app-region: no-drag; + align-items: center; + display: flex; + flex: 0 0 auto; + gap: 6px; + } + + .handoff-button { + align-items: center; + background: color-mix(in srgb, CanvasText 5%, Canvas); + border: 1px solid color-mix(in srgb, CanvasText 12%, transparent); + border-radius: 7px; + display: inline-flex; + gap: 5px; + height: 30px; + justify-content: center; + min-width: 0; + padding: 0 9px; + white-space: nowrap; + } + + .handoff-button:hover { + background: color-mix(in srgb, CanvasText 9%, Canvas); + } + + .handoff-button.primary { + background: #166534; + border-color: #166534; + color: white; + } + + .handoff-button.primary:hover { + background: #14532d; + } + .home-page { align-items: flex-start; background: @@ -487,6 +711,24 @@ style.textContent = ` .installed-apps { grid-template-columns: 1fr; } + + .automation-handoff { + gap: 6px; + grid-template-columns: minmax(0, 1fr) auto; + } + + .handoff-reason { + display: none; + } + + .handoff-button span { + display: none; + } + + .handoff-button { + padding: 0 8px; + width: 32px; + } } @keyframes spin { diff --git a/packages/ui/src/pages/home/components/settings.tsx b/packages/ui/src/pages/home/components/settings.tsx index 0b12d40..2b6657f 100644 --- a/packages/ui/src/pages/home/components/settings.tsx +++ b/packages/ui/src/pages/home/components/settings.tsx @@ -4,6 +4,7 @@ import { botGatewaySavedConfigFromDraft, botGatewaySavedConfigLabel, BotGatewayQrLoginStartResult, BotGatewayQrLoginWaitResult, BotGatewayQrWindowOpenResult, BotGatewaySavedConfig, Button, CircleAlert, closestCenter, cn, CSS, Database, Dialog, DialogBody, DialogContent, DialogFooter, DialogHeader, DialogTitle, Field, formatAppError, formatProviderAccountMeterValue, formatSystemOption, Gauge, + Globe, createBotGatewayConfigDraft, createMcpServerDraft, createMcpServerDraftFromConfig, createMcpServerDraftFromUnknown, createRouteModelOptions, DndContext, DragEndEvent, GatewayMcpServerConfig, GatewayProviderConfig, Input, isBotGatewayConfigDraftSubmittable, KeyboardSensor, KeyRound, KeyValueRowsControl, languageDisplayName, Layers3, LoaderCircle, mcpServerConfigFromDraft, mcpServerEndpointSummary, mcpServerTransportOptions, mcpStdioMessageModeOptions, McpServerDraft, normalizeBotGatewayAuthType, normalizeBotGatewayPlatform, normalizeToolHubConfig, Palette, Pencil, Plus, ProfileConfig, profileAgentLabel, PanelLeftOpen, Power, ProviderAccountMeter, ProviderAccountSnapshot, ReactNode, ResolvedLanguage, ResolvedTheme, Select, SelectControl, @@ -512,6 +513,13 @@ function ToolHubSettingsPage({
    {toolHub.enabled ? ( <> + onChange({ browserAutomation })} + />
    {t("MCP servers")}
    -
    {String(toolHub.mcpServers.length)}
    ## Embeddable Button Component diff --git a/docs/src/content/docs/zh/configuration/provider-deeplink.md b/docs/src/content/docs/zh/configuration/provider-deeplink.md index 703ad8e..ac471c7 100644 --- a/docs/src/content/docs/zh/configuration/provider-deeplink.md +++ b/docs/src/content/docs/zh/configuration/provider-deeplink.md @@ -78,6 +78,14 @@ lead: 快速添加常见模型供应商,确认无误后即可保存,减少 TeamoRouterAnthropic / Chat / Responses + + + code0.aiAnthropic / Chat / Responses + + + + claudeapiAnthropic Messages +
    ## 嵌入式按钮组件 diff --git a/docs/src/styles/global.css b/docs/src/styles/global.css index 215d8c2..094438b 100644 --- a/docs/src/styles/global.css +++ b/docs/src/styles/global.css @@ -1318,6 +1318,18 @@ h1 { --provider-brand-3: #f4f4f5; } +.doc-markdown a.provider-import-button.provider-code0 { + --provider-brand: #101214; + --provider-brand-2: #267dff; + --provider-brand-3: #d8e8ff; +} + +.doc-markdown a.provider-import-button.provider-claudeapi { + --provider-brand: #0d1b2a; + --provider-brand-2: #2f8f83; + --provider-brand-3: #d7fff8; +} + .doc-markdown a.provider-import-button.provider-deepseek { --provider-brand: #173aa8; --provider-brand-2: #4e69ff; diff --git a/packages/core/src/providers/presets/claudeapi/index.ts b/packages/core/src/providers/presets/claudeapi/index.ts new file mode 100644 index 0000000..85a7b65 --- /dev/null +++ b/packages/core/src/providers/presets/claudeapi/index.ts @@ -0,0 +1,15 @@ +import { defaultProviderAccountConfig, type ProviderPreset } from "@ccr/core/providers/presets/types"; + +export const claudeApiProviderPreset: ProviderPreset = { + account: defaultProviderAccountConfig, + aliases: ["claudeapi", "claudeapi.com", "www.claudeapi.com"], + endpoints: [ + { + baseUrl: "https://gw.claudeapi.com", + protocols: ["anthropic_messages"] + } + ], + id: "claudeapi", + name: "claudeapi", + websiteUrl: "https://www.claudeapi.com?source=claudecoderouter" +}; diff --git a/packages/core/src/providers/presets/code0/index.ts b/packages/core/src/providers/presets/code0/index.ts new file mode 100644 index 0000000..b87be18 --- /dev/null +++ b/packages/core/src/providers/presets/code0/index.ts @@ -0,0 +1,15 @@ +import { defaultProviderAccountConfig, type ProviderPreset } from "@ccr/core/providers/presets/types"; + +export const code0ProviderPreset: ProviderPreset = { + account: defaultProviderAccountConfig, + aliases: ["code0", "code0.ai", "code 0"], + endpoints: [ + { + baseUrl: "https://console.code0.ai", + protocols: ["anthropic_messages", "openai_chat_completions", "openai_responses"] + } + ], + id: "code0", + name: "code0.ai", + websiteUrl: "https://code0.ai?source=claudecoderouter" +}; diff --git a/packages/core/src/providers/presets/index.ts b/packages/core/src/providers/presets/index.ts index 760c2b1..85c1c86 100644 --- a/packages/core/src/providers/presets/index.ts +++ b/packages/core/src/providers/presets/index.ts @@ -1,5 +1,7 @@ import { anthropicProviderPreset } from "@ccr/core/providers/presets/anthropic/index"; import { bailianProviderPreset } from "@ccr/core/providers/presets/bailian/index"; +import { claudeApiProviderPreset } from "@ccr/core/providers/presets/claudeapi/index"; +import { code0ProviderPreset } from "@ccr/core/providers/presets/code0/index"; import { deepSeekProviderPreset } from "@ccr/core/providers/presets/deepseek/index"; import { geminiProviderPreset } from "@ccr/core/providers/presets/gemini/index"; import { kimiCodingProviderPreset } from "@ccr/core/providers/presets/kimi-coding/index"; @@ -42,7 +44,9 @@ export const providerPresets: ProviderPreset[] = [ bailianProviderPreset, siliconFlowProviderPreset, runApiProviderPreset, - teamoRouterProviderPreset + teamoRouterProviderPreset, + code0ProviderPreset, + claudeApiProviderPreset ]; export function getProviderPresets(): ProviderPreset[] { diff --git a/packages/ui/src/assets/provider-icons/claudeapi.png b/packages/ui/src/assets/provider-icons/claudeapi.png new file mode 100644 index 0000000000000000000000000000000000000000..776ced8c7f16c712ffe861794957f6b31bcd56ff GIT binary patch literal 17658 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

    K!}f5Qjf?D;}Dsfk#(;d=GK6jJ3(o(vej z!#1O5^FK+1P9yexdlJ`t)^eyS7lA}%>8RCG`hm{UDZRj~BF%?Y%s*fM0#Yf@RuS=@ zw{IN0Pt`MT-)z#8vj3Zy@0u>|;F8;?qh$xH&KeW$ESUIarC$|qEt7o1*cd#5{fc@g zlFBex?Q9>(MgANPsDfPewvK-CJaEUt#4FM=TH>|En?DYGi!pGp?6?E&0*I!j{gC?> zZ9fQOXP;@)H1HkpZU!J^2$76iO8d|nT!_+j2A~DN{qpa-01dnzmoMS}E6rWBD;*|` zefy+@IPW&oye9l#6TYOM>L6YV`nLFt$8~}Kf!i|?kSkA%i`2S2az@GDdJ4ePmA(dI z!{$$`+X=3G9F&Zbst+BkahL$t$vN=C2dXwQ7{(UF9SE2B3wXEkrLLo#lCNKwbF4|O z^xOBGAwT|g=}!ZhC5#(!cH3K!^o(mUYYA@vcDAPvde8k?xQl0#~Sc@bAx49>nfmkH1}zhK|TTkq;qz&f7dA{iQ5mv_c;?wDw@Mm z`!l}p{RjIf2-otJj*q!;J>b+x8C-Sb{Z<-1{%OwmL=qY%LxbWs_D_&_>m9FwfS6o7 zO?WwQ&lZ==Oie`I3V5Lq&I2hH=cH1#7mc%Ht~cE`zav?zrA=EOGxqe4Q~Icqog@XNlubW&q(@yvB!)gK zI|8oHTqZ;YGxI;0%I&K*4DbYiQ%Bke_b<1%8cZC@>g?j?8KR2arS#mYivKM;c0auj z;M6)NLF(-IEv3gtw!g=zjUXHG2dZKK4F1{lk9M~2madBqGyq42hQ3G-OkG2!U5*!g zJt0VAG)Ce76r7M@dgcGw@l#VYZ;n?XrdJVj_%2ZuTK3P`O#R_jvVwG;7@p#|(f|-J zK|;{2wl%QEx%eedG~nz3fHI46HuY9EDQ`DDoJmRah`WW9Fkykg_u(xSh^K4(XyOpj zShaxGlx~jx`x!1j8z{3~*W^-95BNL%TnYMJ30E=2*oXD86>!$)?Gj50hiW&$_reeC zsP2EbkVqOFegx7UsRdj`(jq&naFW|(#k7uDTlkj<#IOp(4^$Itcxh&gqGkdk%jIyD zvB*K#Sd(q{^`^j=F@WUr|GU48@&Ef(5UmlBd**D`UHfb%rgq?CCRl39+Dc_FUWNTX DcEeu; literal 0 HcmV?d00001 diff --git a/packages/ui/src/assets/provider-icons/code0.png b/packages/ui/src/assets/provider-icons/code0.png new file mode 100644 index 0000000000000000000000000000000000000000..32d81c1e5b44077d6fcb83e675350cbfee6f84a2 GIT binary patch literal 12992 zcmeHNdr(tX8b1MSgeBrYmGBs#+uc&zQ3)sz2|=nh_^8|6I!IhZAdsLbO_)?(K}pet zN>g#mOCAMB*5d98f;=J;LI5E?LV#8why-XB0tFHvJQou7+?3A#xlU(0vv=oa=FWHT zot%5m_xpa|>zsS#!{ao9m7^5^fN(hI;2!|k)C7;Z&9A{bJ0>sOhr*H@MCSp(E=LYr ztK}PJ0Jwp}2Ln$e7Eisz#g@^ChkADB;w)M{t8gJtcHzz6zU(=^>(YJ?z>9guVZYpM zYhKHl+`ydJn~F~I*0H3hi5G>9u>;5}0I<4P^J~+|wwpdC>3~TL2Z#X11I!-~N5FW1 z@c`oi<_B0Dz~aCw#R0#M%z$)q>TdbntC^ephpy(ArkexmuX>yS2-EFmwWmos<=G=Z z(ABDc3wa=j`do!cG~qCH%o2dRi=Dr(zQHQPfzro=^|7zv5gWR-pv|U;E;*2_vH~(O zx4oDnd)gnvQLAd+UUC7dE8_(4Y|TmHa_328iS^>qm;B&=sLZ8)vtkGU{9!Q>Wic^f zpq_4rSWaNEK5uG(-438cGFV4INS`rzpIqK#NtZvJPD~;>ikl4!^U+L+@44-IeY}|B zL=cE%&mzOa~sa^7uWw@E(5+59b0cuo7i-~^ed73Ej zB8gdY>``P2b3}cgb7#E$99YYQt2xeYh)IDZ+H8yHWYjJ8eV6^`kZxCzdbFQAV}zG# zxwG@$FUYWbsObU2&F;zORcZKlt8ehjS~AGXmEnOGpWy`w-zS;88F(J#nAFE4N^sED z$JYE<{aG3(2#D?Wr^9562HBy=0acZTg-@>+@a^th&mWkbi>wvJ>F7Cp8<0YTb#CvX zua}K?6e)l?*4U$xH?a++ue zSedj;et_`h)gSt*FaM?fhbRSB&nG3E_wPuIqLWEFRlr5V%WrUi`vVNn0>i*2oqO*# z0ETD8Dhot4fi=<=ViGkt2pf9u6(JS_zghkUWyL?Xv1KwG*0lbQ@z%cHcKB%_kLtvr z)DrN#n%)Ni*$rlE8Dxz%K~ZBD=CBm!`(A#+$u1djsK4maiqW9%>tlJI6s#AG6J|!I zTifhbxgH{hb+v^rt8v`${bh}gxegVhwR>dEcZB?j&vyFW zrtuUNR}-2c9*VQ(K%<@0{?L=&RZ?=+X0l~;@Of}*SxVyi@fou#71AAjvA&$gHJ5Dlxr*?j#^O5u!eTF*8rP_$%qPpI{R``J8 zJ+Npgj3djlQ3mF23fM^%7G!5<*LA&S;CX7q(8qn)6;AfA!;GFnhMub#dz`Kx^t^2jZW!Y0J}#RVd#<4dY`q#rnw zH%^e!>1_WxHN5KH+jpwoe%M}47FplwV+fD(xgJB6b)QUT7xSo&42m))v2QA($(2DF z8voq2_mu8x#8*5;21A(PL`dD+vE24PgO^B)b1mg*wbfbPl!rwG*;rLrMO~OnTj3ei z=_Km7a(-nPW>ea13YcL-t4Q6Ib6JyQ4W2KR#5#DMuE8&jI8$%a*@K*g%o|y*W-V}d z{8H=AQk##7d{OixvAA(Ag&Wf7@pUQ{EZ42k^5Dc#u%#8MJR;bwWeVfiPFu!|$Hp3N z=^8)sJJP$VH$d-OeCA$FJM$$;qT;SK3rQE1+%;Z9R`b3zUU-0Fl-#BJd)qw~SZ@nu z(_Kya+G712*2h97pr((-)*UduVtmE?6^jE{9KhnhzvDn^Ed##?0P!w^~6P8;yV2SWNqNgGUn^n|MEcRe=rvzkn`A86QRSIJ%^M{VRkH OhYuY;SovOf+P?sKzeTD5 literal 0 HcmV?d00001 diff --git a/packages/ui/src/pages/home/shared/options.ts b/packages/ui/src/pages/home/shared/options.ts index 2415bb1..18ad4c8 100644 --- a/packages/ui/src/pages/home/shared/options.ts +++ b/packages/ui/src/pages/home/shared/options.ts @@ -43,6 +43,8 @@ import type { } from "@ccr/core/contracts/app"; import anthropicProviderIconUrl from "@/assets/provider-icons/anthropic.png"; import bailianProviderIconUrl from "@/assets/provider-icons/bailian.ico"; +import claudeapiProviderIconUrl from "@/assets/provider-icons/claudeapi.png"; +import code0ProviderIconUrl from "@/assets/provider-icons/code0.png"; import deepseekProviderIconUrl from "@/assets/provider-icons/deepseek.ico"; import geminiProviderIconUrl from "@/assets/provider-icons/gemini.svg"; import mistralProviderIconUrl from "@/assets/provider-icons/mistral.webp"; @@ -319,6 +321,8 @@ export const mcpStdioMessageModeOptions: Array<{ label: string; value: GatewayMc export const providerPresetIconUrls: Record = { anthropic: anthropicProviderIconUrl, bailian: bailianProviderIconUrl, + claudeapi: claudeapiProviderIconUrl, + code0: code0ProviderIconUrl, deepseek: deepseekProviderIconUrl, gemini: geminiProviderIconUrl, "kimi-coding": moonshotProviderIconUrl, From c8cbc09b96c97e768b6c8ee94d87cd4c553b4d1b Mon Sep 17 00:00:00 2001 From: musistudio Date: Tue, 7 Jul 2026 20:29:32 +0800 Subject: [PATCH 71/94] add new api probe --- packages/core/src/contracts/app.ts | 8 +- packages/core/src/gateway/service.ts | 19 ++- .../core/src/providers/account-service.ts | 134 ++++++++++++++++++ packages/core/src/providers/new-api.ts | 92 ++++++++++++ packages/core/src/providers/probe.ts | 35 ++++- .../src/pages/home/components/providers.tsx | 67 ++++++--- packages/ui/src/pages/home/shared/i18n.tsx | 2 + .../ui/src/pages/home/shared/providers.ts | 53 ++++++- tests/main/gateway-virtual-models.test.mjs | 51 +++++++ tests/main/provider-probe.test.mjs | 67 +++++++++ tests/renderer/providers.test.ts | 58 ++++++++ 11 files changed, 559 insertions(+), 27 deletions(-) create mode 100644 packages/core/src/providers/new-api.ts diff --git a/packages/core/src/contracts/app.ts b/packages/core/src/contracts/app.ts index 685a35e..0dbfa56 100644 --- a/packages/core/src/contracts/app.ts +++ b/packages/core/src/contracts/app.ts @@ -160,6 +160,7 @@ export type ProviderAccountStatus = "ok" | "warning" | "critical" | "error" | "u export type ProviderAccountMeterKind = "balance" | "subscription" | "quota" | "time_window" | "tokens" | "requests"; export type ProviderAccountMeterUnit = "USD" | "CNY" | "hours" | "minutes" | "tokens" | "requests" | string; export type ProviderAccountMeterWindow = "5h" | "daily" | "weekly" | "monthly" | string; +export type ProviderAccountHttpJsonParser = "kimi-code-usages" | "new-api-key-usage" | "new-api-user-self"; export type ProviderAccountConfig = { connectors?: ProviderAccountConnectorConfig[]; @@ -193,7 +194,7 @@ export type ProviderAccountHttpJsonConnectorConfig = ProviderAccountConnectorBas headers?: Record; mapping: ProviderAccountMappingConfig; method?: "GET" | "POST"; - parser?: "kimi-code-usages"; + parser?: ProviderAccountHttpJsonParser; type: "http-json"; }; @@ -405,6 +406,8 @@ export type GatewayProviderCapability = { type: GatewayProviderProtocol; }; +export type GatewayProviderDetectedProvider = "new-api"; + export type GatewayProviderProbeRequest = { apiKey?: string; baseUrl: string; @@ -445,6 +448,7 @@ export type ProviderIconDetectionResult = { export type GatewayProviderProbeProtocolResult = { baseUrl?: string; + detectedProvider?: GatewayProviderDetectedProvider; endpoint: string; message: string; protocol: GatewayProviderProtocol; @@ -453,7 +457,9 @@ export type GatewayProviderProbeProtocolResult = { }; export type GatewayProviderProbeResult = { + account?: ProviderAccountConfig; capabilities?: GatewayProviderCapability[]; + detectedProvider?: GatewayProviderDetectedProvider; detectedProtocol?: GatewayProviderProtocol; modelDisplayNames?: Record; modelSource?: "anthropic" | "gemini" | "openai"; diff --git a/packages/core/src/gateway/service.ts b/packages/core/src/gateway/service.ts index ef274df..5936e7a 100644 --- a/packages/core/src/gateway/service.ts +++ b/packages/core/src/gateway/service.ts @@ -391,7 +391,7 @@ class GatewayService { } if (shouldRunGateway) { - await writeCoreGatewayConfig(config, this.rawTraceSyncToken, this.browserWebSearchMcpIntegration); + await writeCoreGatewayConfig(config, this.rawTraceSyncToken, this.coreAuthToken, this.browserWebSearchMcpIntegration); await stopPreviousManagedCoreGateway(config, this.status.coreEndpoint); if (await isCoreGatewayHealthy(this.status.coreEndpoint)) { throw new Error(`Core gateway endpoint is already in use: ${this.status.coreEndpoint}`); @@ -1150,6 +1150,7 @@ export const gatewayService = new GatewayService(); async function writeCoreGatewayConfig( config: AppConfig, rawTraceSyncToken: string, + coreAuthToken: string, browserWebSearchMcpIntegration?: BrowserWebSearchMcpIntegration ): Promise { assertLoopbackCoreHost(config.gateway.coreHost); @@ -1165,7 +1166,7 @@ async function writeCoreGatewayConfig( ...pluginService.getVirtualModelProfiles() ])), config); const coreEndpoint = endpoint(config.gateway.coreHost, config.gateway.corePort); - const builtinToolArtifacts = await fusionBuiltinToolArtifacts(virtualModelProfiles, coreEndpoint, browserWebSearchMcpIntegration); + const builtinToolArtifacts = await fusionBuiltinToolArtifacts(virtualModelProfiles, coreEndpoint, coreAuthToken, browserWebSearchMcpIntegration); const providers = [ ...config.Providers .flatMap((provider) => toCoreGatewayProviders(withCodexOauthProviderBaseUrl(provider, codexOauthProviderNames))) @@ -1459,6 +1460,7 @@ function hasOwn(value: Record, key: string): boolean { async function fusionBuiltinToolArtifacts( profiles: unknown[], coreEndpoint: string, + coreAuthToken: string, browserWebSearchMcpIntegration?: BrowserWebSearchMcpIntegration ): Promise<{ mcpServers: GatewayMcpServerConfig[]; providers: CoreGatewayProvider[] }> { const providers: CoreGatewayProvider[] = []; @@ -1481,12 +1483,14 @@ async function fusionBuiltinToolArtifacts( const toolServerKey = `vision:${visionConfig.toolName}`; if (!toolServerKeys.has(toolServerKey)) { toolServerKeys.add(toolServerKey); + const useGatewayVisionRuntime = !visionConfig.baseUrl; mcpServers.push(fusionBuiltinMcpServer({ entry, env: { FUSION_BUILTIN_TOOL_KIND: "vision", FUSION_TOOL_NAME: visionConfig.toolName, - ...(visionConfig.baseUrl ? { VISION_BASE_URL: visionConfig.baseUrl } : { VISION_GATEWAY_BASE_URL: `${coreEndpoint}/v1` }), + ...(useGatewayVisionRuntime ? { VISION_GATEWAY_BASE_URL: `${coreEndpoint}/v1` } : { VISION_BASE_URL: visionConfig.baseUrl || "" }), + ...(useGatewayVisionRuntime && coreAuthToken ? { VISION_GATEWAY_API_KEY: coreAuthToken } : {}), ...(resolvedVision.model ? { VISION_MODEL: resolvedVision.model } : {}), ...(visionConfig.baseUrl && visionConfig.apiKey ? { VISION_API_KEY: visionConfig.apiKey } : {}), ...(visionConfig.timeoutMs ? { VISION_TIMEOUT_MS: String(visionConfig.timeoutMs) } : {}) @@ -1534,6 +1538,15 @@ async function fusionBuiltinToolArtifacts( return { mcpServers, providers }; } +export async function fusionBuiltinToolArtifactsForTest( + profiles: unknown[], + coreEndpoint: string, + coreAuthToken: string, + browserWebSearchMcpIntegration?: BrowserWebSearchMcpIntegration +): Promise<{ mcpServers: GatewayMcpServerConfig[]; providers: unknown[] }> { + return fusionBuiltinToolArtifacts(profiles, coreEndpoint, coreAuthToken, browserWebSearchMcpIntegration); +} + function fusionBuiltinMcpServer({ entry, env, diff --git a/packages/core/src/providers/account-service.ts b/packages/core/src/providers/account-service.ts index 31df114..adeb61d 100644 --- a/packages/core/src/providers/account-service.ts +++ b/packages/core/src/providers/account-service.ts @@ -146,6 +146,26 @@ export async function testProviderAccountConnector(request: ProviderAccountTestR status: statusFromMeters(meters, [], 1) }; } + if (connector.parser === "new-api-key-usage") { + const meters = newApiKeyUsageMeters(payload); + return { + meters, + message: meters.length === 0 ? newApiKeyUsageFallbackMessage(payload) : readMappedString(connector.mapping.message, payload), + paths: flattenJsonPaths(payload), + payload, + status: statusFromMeters(meters, [], 1) + }; + } + if (connector.parser === "new-api-user-self") { + const meters = newApiUserSelfMeters(payload); + return { + meters, + message: meters.length === 0 ? readMappedString(connector.mapping.message, payload) ?? "No user balance data available." : readMappedString(connector.mapping.message, payload), + paths: flattenJsonPaths(payload), + payload, + status: statusFromMeters(meters, [], 1) + }; + } const meters = mappedMetersFromPayload(connector, payload); @@ -158,6 +178,18 @@ export async function testProviderAccountConnector(request: ProviderAccountTestR }; } +export function newApiKeyUsageMetersForTest(payload: unknown): ProviderAccountMeter[] { + return newApiKeyUsageMeters(payload); +} + +export function newApiKeyUsageFallbackMessageForTest(payload: unknown): string { + return newApiKeyUsageFallbackMessage(payload); +} + +export function newApiUserSelfMetersForTest(payload: unknown): ProviderAccountMeter[] { + return newApiUserSelfMeters(payload); +} + export async function resetCodexRateLimitCredit(request: ProviderAccountResetRequest): Promise { const providerName = request.provider?.trim(); const creditId = request.creditId?.trim(); @@ -586,6 +618,24 @@ async function resolveHttpJsonConnector( source: "http-json" }; } + if (connector.parser === "new-api-key-usage") { + const meters = newApiKeyUsageMeters(payload); + return { + errors: [], + message: meters.length === 0 ? newApiKeyUsageFallbackMessage(payload) : readMappedString(connector.mapping.message, payload), + meters, + source: "http-json" + }; + } + if (connector.parser === "new-api-user-self") { + const meters = newApiUserSelfMeters(payload); + return { + errors: [], + message: meters.length === 0 ? readMappedString(connector.mapping.message, payload) ?? "No user balance data available." : readMappedString(connector.mapping.message, payload), + meters, + source: "http-json" + }; + } const meters = mappedMetersFromPayload(connector, payload); return { @@ -758,6 +808,90 @@ function normalizeRemoteSnapshot( }; } +function newApiKeyUsageMeters(payload: unknown): ProviderAccountMeter[] { + const meter = newApiKeyUsageMeter(payload); + return meter ? [meter] : []; +} + +function newApiKeyUsageMeter(payload: unknown): ProviderAccountMeter | undefined { + const data = newApiKeyUsageData(payload); + if (!data) { + return undefined; + } + + const unlimited = readBoolean(readJsonRecordValue(data, "unlimited_quota")) === true; + const remaining = normalizeNumber(readJsonRecordValue(data, "total_available")); + const limit = normalizeNumber(readJsonRecordValue(data, "total_granted")); + const used = normalizeNumber(readJsonRecordValue(data, "total_used")); + if (unlimited || (limit === undefined && remaining === undefined && used === undefined)) { + return undefined; + } + + return { + id: "new_api_key_quota", + kind: "quota", + label: "API key quota", + limit, + remaining, + source: "http-json", + unit: "quota", + used + }; +} + +function newApiKeyUsageFallbackMessage(payload: unknown): string { + const data = newApiKeyUsageData(payload); + if (data && readBoolean(readJsonRecordValue(data, "unlimited_quota")) === true) { + return "API key has no dedicated quota limit. Configure a New API user balance connector with an access token and New-Api-User to read account balance."; + } + return readMappedString("$.message", payload) ?? "No API key quota data available."; +} + +function newApiUserSelfMeters(payload: unknown): ProviderAccountMeter[] { + const meter = newApiUserSelfMeter(payload); + return meter ? [meter] : []; +} + +function newApiUserSelfMeter(payload: unknown): ProviderAccountMeter | undefined { + const data = newApiUserSelfData(payload); + if (!data) { + return undefined; + } + + const remaining = normalizeNumber(readJsonRecordValue(data, "quota")); + const used = normalizeNumber(readJsonRecordValue(data, "used_quota")); + if (remaining === undefined && used === undefined) { + return undefined; + } + + return { + id: "new_api_user_balance", + kind: "balance", + label: "User balance", + limit: remaining !== undefined && used !== undefined ? remaining + used : undefined, + remaining, + source: "http-json", + unit: "quota", + used + }; +} + +function newApiKeyUsageData(payload: unknown): Record | undefined { + if (!isRecord(payload)) { + return undefined; + } + const data = readJsonRecordValue(payload, "data"); + return isRecord(data) ? data : payload; +} + +function newApiUserSelfData(payload: unknown): Record | undefined { + if (!isRecord(payload)) { + return undefined; + } + const data = readJsonRecordValue(payload, "data"); + return isRecord(data) ? data : payload; +} + function kimiCodeUsageMeters(payload: unknown): ProviderAccountMeter[] { if (!isRecord(payload)) { return []; diff --git a/packages/core/src/providers/new-api.ts b/packages/core/src/providers/new-api.ts new file mode 100644 index 0000000..44c2609 --- /dev/null +++ b/packages/core/src/providers/new-api.ts @@ -0,0 +1,92 @@ +import type { ProviderAccountConfig, ProviderAccountHttpJsonConnectorConfig } from "@ccr/core/contracts/app"; +import { compactProviderUrl, providerUrlWithDefaultScheme } from "@ccr/core/providers/url"; + +export type DetectedProviderKind = "new-api"; + +const newApiHeaderNames = ["x-new-api-version", "x-oneapi-request-id"]; + +export function detectedProviderFromHeaders(headers: Record): DetectedProviderKind | undefined { + return hasNewApiHeaders(headers) ? "new-api" : undefined; +} + +export function hasNewApiHeaders(headers: Record): boolean { + const normalized = new Set(Object.keys(headers).map((key) => key.toLowerCase())); + return newApiHeaderNames.some((header) => normalized.has(header)); +} + +export function newApiKeyUsageAccountConfig(baseUrl: string): ProviderAccountConfig { + return { + connectors: [ + { + auth: "provider-api-key", + endpoint: newApiKeyUsageEndpoint(baseUrl), + mapping: { + message: "$.message", + meters: [ + { + id: "new_api_key_quota", + kind: "quota", + label: "API key quota", + limit: "$.data.total_granted", + remaining: "$.data.total_available", + unit: "quota", + used: "$.data.total_used", + } + ] + }, + method: "GET", + parser: "new-api-key-usage", + type: "http-json" + } + ], + enabled: true + }; +} + +export function newApiKeyUsageEndpoint(baseUrl: string): string { + const root = newApiRootBaseUrl(baseUrl); + return `${root}/api/usage/token/`; +} + +export function newApiUserSelfConnectorConfig(baseUrl: string): ProviderAccountHttpJsonConnectorConfig { + return { + auth: "none", + endpoint: newApiUserSelfEndpoint(baseUrl), + headers: { + Authorization: "Bearer ", + "New-Api-User": "" + }, + mapping: { + meters: [ + { + id: "new_api_user_balance", + kind: "balance", + label: "User balance", + remaining: "$.data.quota", + unit: "quota", + used: "$.data.used_quota" + } + ] + }, + method: "GET", + parser: "new-api-user-self", + type: "http-json" + }; +} + +export function newApiUserSelfEndpoint(baseUrl: string): string { + const root = newApiRootBaseUrl(baseUrl); + return `${root}/api/user/self`; +} + +export function newApiRootBaseUrl(baseUrl: string): string { + try { + const url = new URL(providerUrlWithDefaultScheme(baseUrl.trim())); + url.pathname = url.pathname.replace(/\/+(v1|api)$/i, "").replace(/\/+$/, "") || "/"; + url.search = ""; + url.hash = ""; + return compactProviderUrl(url); + } catch { + return baseUrl.trim().replace(/[?#].*$/, "").replace(/\/+$/, "").replace(/\/(v1|api)$/i, ""); + } +} diff --git a/packages/core/src/providers/probe.ts b/packages/core/src/providers/probe.ts index e5b24d4..0a82346 100644 --- a/packages/core/src/providers/probe.ts +++ b/packages/core/src/providers/probe.ts @@ -19,6 +19,11 @@ import { providerBaseUrlForProtocol, type ParsedProviderBaseUrl } from "@ccr/core/providers/url"; +import { + detectedProviderFromHeaders, + newApiKeyUsageAccountConfig, + type DetectedProviderKind +} from "@ccr/core/providers/new-api"; type ModelSource = NonNullable; @@ -27,6 +32,8 @@ type ParsedProviderUrl = ParsedProviderBaseUrl & { }; type FetchJsonResult = { + detectedProvider?: DetectedProviderKind; + headers?: Record; payload?: unknown; status?: number; text: string; @@ -226,16 +233,21 @@ async function resolveGatewayProviderProbe(request: GatewayProviderProbeRequest) : typedModels; const protocolResults = await probeProtocols(parsed, request.apiKey, models, protocols, mode); const detectedProtocol = detectProtocol(parsed, protocolResults, modelProbe.source, protocols); + const normalizedBaseUrl = detectedProtocol + ? resolveProbeBaseUrl(parsed, detectedProtocol, protocolResults, modelProbe) + : parsed.normalizedInputBaseUrl; + const detectedProvider = detectProvider(protocolResults); + const account = detectedProvider === "new-api" ? newApiKeyUsageAccountConfig(normalizedBaseUrl) : undefined; return { + ...(account ? { account } : {}), capabilities: capabilitiesFromProtocolResults(protocolResults), + ...(detectedProvider ? { detectedProvider } : {}), detectedProtocol, modelDisplayNames: modelProbe.modelDisplayNames, modelSource: modelProbe.source, models: modelProbe.models, - normalizedBaseUrl: detectedProtocol - ? resolveProbeBaseUrl(parsed, detectedProtocol, protocolResults, modelProbe) - : parsed.normalizedInputBaseUrl, + normalizedBaseUrl, protocols: protocolResults }; } @@ -491,6 +503,7 @@ async function probeProtocolSupport( const supported = isProviderProtocolEndpointSupportedForProbe(result.status, message, protocol, parsed.hints); const probeResult = { baseUrl: candidate.baseUrl, + ...(result.detectedProvider ? { detectedProvider: result.detectedProvider } : {}), endpoint: candidate.endpoint, message, protocol, @@ -539,6 +552,7 @@ async function probeProtocolConnectivity( const supported = isProtocolSupported(result.status, message, protocol); const probeResult = { baseUrl: candidate.baseUrl, + ...(result.detectedProvider ? { detectedProvider: result.detectedProvider } : {}), endpoint: candidate.endpoint, message, protocol, @@ -663,7 +677,10 @@ async function requestJson(url: string, init: RequestInit): Promise item.detectedProvider)?.detectedProvider; +} + +function responseHeadersRecord(headers: Headers): Record { + const record: Record = {}; + headers.forEach((value, key) => { + record[key] = value; + }); + return record; +} + function parseProviderUrl(value: string): ParsedProviderUrl { const parsed = parseProviderBaseUrl(value); const url = new URL(parsed.normalizedInputBaseUrl); diff --git a/packages/ui/src/pages/home/components/providers.tsx b/packages/ui/src/pages/home/components/providers.tsx index 040617c..5f1753a 100644 --- a/packages/ui/src/pages/home/components/providers.tsx +++ b/packages/ui/src/pages/home/components/providers.tsx @@ -9,7 +9,7 @@ import { Layers3, LoaderCircle, localAgentProviderIconUrls, mergeProviderModelLists, modelCatalogItemMatchesQuery, motion, Pencil, Plus, PopoverContent, primaryProviderAccountMeter, primaryProviderPresetEndpoint, providerAccountConnectorApiKeySafetyIssue, providerAccountConnectorExample, ProviderAccountDraftMode, providerAccountModeOptions, ProviderAccountSnapshot, - providerAccountSnapshotCredentialLabel, providerAccountSnapshotLabel, ProviderAccountTestPath, + providerAccountConnectorsTextWithNewApiUserBalanceTemplate, providerAccountSnapshotCredentialLabel, providerAccountSnapshotLabel, ProviderAccountTestPath, ProviderAccountTestResult, providerBaseUrl, providerCapabilitiesSummary, ProviderCredentialDraft, ProviderDeepLinkPayload, ProviderDeepLinkRequest, providerDraftSafetyIssue, providerCredentialDraftPatchFromJson, providerHttpJsonConnectorFromDraft, ProviderConnectivityCheckReport, providerDeepLinkDisplayIcon, providerListItemKey, providerMatchesQuery, ProviderPreset, providerPresetIconUrls, providerProbeHasSupportedProtocol, providerModelDisplayName, providerModelDisplayTitle, providerSelectableProtocolsFromProbe, providerUsageFieldPatch, ProviderUsageFieldTarget, providerUsageMethodOptions, Search, SelectControl, @@ -2085,7 +2085,11 @@ function ProviderUsageSettings({ const [testLoading, setTestLoading] = useState(false); const [testResult, setTestResult] = useState(); const [testError, setTestError] = useState(""); + const [newApiUserId, setNewApiUserId] = useState(""); const modeOptions = translateOptions(providerAccountModeOptions, t); + const showNewApiUserBalanceTemplate = probe?.detectedProvider === "new-api" || + draft.accountConnectorsText.includes("new-api-key-usage") || + draft.accountConnectorsText.includes("new-api-user-self"); useEffect(() => { setTestResult(undefined); @@ -2135,6 +2139,16 @@ function ProviderUsageSettings({ onChange(providerUsageFieldPatch(target, path)); } + function insertNewApiUserBalanceTemplate() { + onChange({ + accountConnectorsText: providerAccountConnectorsTextWithNewApiUserBalanceTemplate( + draft.accountConnectorsText, + probe?.normalizedBaseUrl || draft.baseUrl, + newApiUserId + ) + }); + } + return (
    @@ -2253,23 +2267,40 @@ function ProviderUsageSettings({ ) : null} {draft.accountMode === "raw" ? ( - -

    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 z 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