cartethyia 2.5.1 → 2.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +5 -1
- package/dist/dashboard/assets/{AccountCooldown-ChpdzxRH.js → AccountCooldown-Bjo92Qat.js} +1 -1
- package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js.gz +0 -0
- package/dist/dashboard/assets/{BannedPage-ChqnTRpZ.js → BannedPage-MsQaeQTs.js} +1 -1
- package/dist/dashboard/assets/BannedPage-MsQaeQTs.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-BO9nKg9w.js +1 -0
- package/dist/dashboard/assets/CliToolDetailPage-BO9nKg9w.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-BXUaRCQQ.js → CliToolsPage-DfWHTJ7v.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-DfWHTJ7v.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-BrCBQDAc.js +1 -0
- package/dist/dashboard/assets/CombosPage-BrCBQDAc.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-C6Uh33er.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-C6Uh33er.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-8aS9hyJu.js +1 -0
- package/dist/dashboard/assets/CustomizationPage-8aS9hyJu.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-qomXThxX.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-qomXThxX.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-C8hKyXBe.js +1 -0
- package/dist/dashboard/assets/LoginPage-C8hKyXBe.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-CWkTUtpX.js +1 -0
- package/dist/dashboard/assets/ModelPicker-CWkTUtpX.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-C-ExCy1V.js +1 -0
- package/dist/dashboard/assets/OverviewPage-C-ExCy1V.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-tr811uaF.js +3 -0
- package/dist/dashboard/assets/ProviderDetailPage-tr811uaF.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-BS9IEpvv.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-BS9IEpvv.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-Rn_qXScb.js +7 -0
- package/dist/dashboard/assets/ProxyPage-Rn_qXScb.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-BihkgQSq.js +1 -0
- package/dist/dashboard/assets/QuotaPage-BihkgQSq.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-DhiEjm6T.js +1 -0
- package/dist/dashboard/assets/SettingsPage-DhiEjm6T.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-CXjUlfqr.js +1 -0
- package/dist/dashboard/assets/SetupPage-CXjUlfqr.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-C-_oD3F2.js +1 -0
- package/dist/dashboard/assets/SortableList-C-_oD3F2.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-DqK5E26W.js → StudioPage-u9IxmYOI.js} +3 -3
- package/dist/dashboard/assets/StudioPage-u9IxmYOI.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-BEVLVrwP.js +56 -0
- package/dist/dashboard/assets/UsagePage-BEVLVrwP.js.gz +0 -0
- package/dist/dashboard/assets/{api-keys-BmDbsior.js → api-keys-uNywYlfv.js} +1 -1
- package/dist/dashboard/assets/api-keys-uNywYlfv.js.gz +0 -0
- package/dist/dashboard/assets/{app-C-Y4oZT0.js → app-CafWtVXA.js} +1 -1
- package/dist/dashboard/assets/{app-C-Y4oZT0.js.gz → app-CafWtVXA.js.gz} +0 -0
- package/dist/dashboard/assets/app-DxivpfYG.css +2 -0
- package/dist/dashboard/assets/app-DxivpfYG.css.gz +0 -0
- package/dist/dashboard/assets/{assert-BHfaei-X.js → assert-NHwYq95t.js} +1 -1
- package/dist/dashboard/assets/{assert-BHfaei-X.js.gz → assert-NHwYq95t.js.gz} +0 -0
- package/dist/dashboard/assets/badge-6uzDDvqs.js +1 -0
- package/dist/dashboard/assets/badge-6uzDDvqs.js.gz +0 -0
- package/dist/dashboard/assets/banner-4_WNg2WZ.js +1 -0
- package/dist/dashboard/assets/banner-4_WNg2WZ.js.gz +0 -0
- package/dist/dashboard/assets/card--AtFedAq.js +1 -0
- package/dist/dashboard/assets/card--AtFedAq.js.gz +0 -0
- package/dist/dashboard/assets/{cli-tools-B0wBq-RL.js → cli-tools-B3e7dx3N.js} +1 -1
- package/dist/dashboard/assets/cli-tools-B3e7dx3N.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-NreUx6c8.js +1 -0
- package/dist/dashboard/assets/clipboard-button-NreUx6c8.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BtpsJU98.js +1 -0
- package/dist/dashboard/assets/dialog-BtpsJU98.js.gz +0 -0
- package/dist/dashboard/assets/drawer-WwwtzihF.js +1 -0
- package/dist/dashboard/assets/drawer-WwwtzihF.js.gz +0 -0
- package/dist/dashboard/assets/entry-Dj9AR0gY.js +4 -0
- package/dist/dashboard/assets/entry-Dj9AR0gY.js.gz +0 -0
- package/dist/dashboard/assets/entry-TJIye_jT.js +1 -0
- package/dist/dashboard/assets/entry-TJIye_jT.js.gz +0 -0
- package/dist/dashboard/assets/{entry-D4O2229X.js → entry-jy9_4-Ke.js} +1 -1
- package/dist/dashboard/assets/entry-jy9_4-Ke.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-D_eM3IgG.js +1 -0
- package/dist/dashboard/assets/github-badge-D_eM3IgG.js.gz +0 -0
- package/dist/dashboard/assets/{index-CcD1eSJ6.js → index-DCBtROoK.js} +2 -2
- package/dist/dashboard/assets/index-DCBtROoK.js.gz +0 -0
- package/dist/dashboard/assets/inline-BcNGF8Gr.js +1 -0
- package/dist/dashboard/assets/inline-BcNGF8Gr.js.gz +0 -0
- package/dist/dashboard/assets/input-BG41et9G.js +1 -0
- package/dist/dashboard/assets/input-BG41et9G.js.gz +0 -0
- package/dist/dashboard/assets/layout-C8mer_Ak.js +1 -0
- package/dist/dashboard/assets/layout-C8mer_Ak.js.gz +0 -0
- package/dist/dashboard/assets/list-row-Gh_W4OR_.js +1 -0
- package/dist/dashboard/assets/list-row-Gh_W4OR_.js.gz +0 -0
- package/dist/dashboard/assets/{live-yfEJFF9T.js → live-BJGYRBnR.js} +1 -1
- package/dist/dashboard/assets/live-BJGYRBnR.js.gz +0 -0
- package/dist/dashboard/assets/{model-bans-CT-TxSNX.js → model-bans-DeVLDA7I.js} +1 -1
- package/dist/dashboard/assets/model-bans-DeVLDA7I.js.gz +0 -0
- package/dist/dashboard/assets/numbers-Bs4LOOuf.js +2 -0
- package/dist/dashboard/assets/numbers-Bs4LOOuf.js.gz +0 -0
- package/dist/dashboard/assets/{routing-Cs6jl49L.js → routing-CcusqXA7.js} +1 -1
- package/dist/dashboard/assets/routing-CcusqXA7.js.gz +0 -0
- package/dist/dashboard/assets/select-DsX5QT8K.js +41 -0
- package/dist/dashboard/assets/select-DsX5QT8K.js.gz +0 -0
- package/dist/dashboard/assets/{settings-CfcTcOyU.js → settings-mpmXTgUa.js} +1 -1
- package/dist/dashboard/assets/settings-mpmXTgUa.js.gz +0 -0
- package/dist/dashboard/assets/stack-DwLmjUFK.js +1 -0
- package/dist/dashboard/assets/stack-DwLmjUFK.js.gz +0 -0
- package/dist/dashboard/assets/state-DYdQmdAl.js +1 -0
- package/dist/dashboard/assets/state-DYdQmdAl.js.gz +0 -0
- package/dist/dashboard/assets/switch-CidSfbG7.js +1 -0
- package/dist/dashboard/assets/switch-CidSfbG7.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-BQKMcJ1H.js → useMutation-w4u4CVue.js} +1 -1
- package/dist/dashboard/assets/useMutation-w4u4CVue.js.gz +0 -0
- package/dist/dashboard/index.html +1 -1
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +51 -54
- package/package.json +1 -1
- package/src/boot/app.ts +3 -2
- package/src/console/observability/contracts.ts +11 -1
- package/src/console/observability/store.ts +9 -6
- package/src/providers/discovery/openrouter-free-roster.ts +3 -11
- package/src/providers/discovery/probe-phases.ts +42 -5
- package/src/providers/discovery/probing-service.ts +25 -2
- package/src/providers/integrations/claude/claude.ts +3 -1
- package/src/providers/integrations/cloudflareai/cloudflareai-models.ts +3 -1
- package/src/providers/integrations/inferhub.ts +3 -1
- package/src/providers/integrations/kilo/kilo.ts +0 -1
- package/src/providers/integrations/kiro/kiro-catalog.ts +8 -2
- package/src/providers/integrations/openrouter/provider.ts +5 -15
- package/src/security/api-key-auth.ts +23 -0
- package/src/transport/guards/auth-guard.ts +2 -3
- package/dist/dashboard/assets/AccountCooldown-ChpdzxRH.js.gz +0 -0
- package/dist/dashboard/assets/BannedPage-ChqnTRpZ.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-Bb9CNuoh.js +0 -1
- package/dist/dashboard/assets/CliToolDetailPage-Bb9CNuoh.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-BXUaRCQQ.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-CisNZOUt.js +0 -1
- package/dist/dashboard/assets/CombosPage-CisNZOUt.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-BOpI_LOH.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-BOpI_LOH.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-DAkdvJ4k.js +0 -1
- package/dist/dashboard/assets/CustomizationPage-DAkdvJ4k.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-DeT-VXHd.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-DeT-VXHd.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-CytxSeGP.js +0 -1
- package/dist/dashboard/assets/LoginPage-CytxSeGP.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-oQ-SDXp8.js +0 -1
- package/dist/dashboard/assets/ModelPicker-oQ-SDXp8.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-CU7S1wJs.js +0 -1
- package/dist/dashboard/assets/OverviewPage-CU7S1wJs.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-DLm2baot.js +0 -3
- package/dist/dashboard/assets/ProviderDetailPage-DLm2baot.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-DOXfuzfL.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-DOXfuzfL.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-wvk0ciQN.js +0 -7
- package/dist/dashboard/assets/ProxyPage-wvk0ciQN.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-Cn_fcUHE.js +0 -1
- package/dist/dashboard/assets/QuotaPage-Cn_fcUHE.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-8-eVadhc.js +0 -1
- package/dist/dashboard/assets/SettingsPage-8-eVadhc.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-uEIXPyGO.js +0 -1
- package/dist/dashboard/assets/SetupPage-uEIXPyGO.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-DCY0TPTZ.js +0 -1
- package/dist/dashboard/assets/SortableList-DCY0TPTZ.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-DqK5E26W.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-38JfiZ0H.js +0 -56
- package/dist/dashboard/assets/UsagePage-38JfiZ0H.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-BmDbsior.js.gz +0 -0
- package/dist/dashboard/assets/app-BbpSarVV.css +0 -2
- package/dist/dashboard/assets/app-BbpSarVV.css.gz +0 -0
- package/dist/dashboard/assets/badge-DpXT4Ixt.js +0 -1
- package/dist/dashboard/assets/badge-DpXT4Ixt.js.gz +0 -0
- package/dist/dashboard/assets/banner-CSAAWLJv.js +0 -1
- package/dist/dashboard/assets/banner-CSAAWLJv.js.gz +0 -0
- package/dist/dashboard/assets/card-DEhRXXw4.js +0 -1
- package/dist/dashboard/assets/card-DEhRXXw4.js.gz +0 -0
- package/dist/dashboard/assets/check-D69FzdvC.js +0 -1
- package/dist/dashboard/assets/check-D69FzdvC.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-B0wBq-RL.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-DOo6EU8O.js +0 -1
- package/dist/dashboard/assets/clipboard-button-DOo6EU8O.js.gz +0 -0
- package/dist/dashboard/assets/copy-ngmQ9zcZ.js +0 -1
- package/dist/dashboard/assets/copy-ngmQ9zcZ.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BvQeBMfP.js +0 -1
- package/dist/dashboard/assets/dialog-BvQeBMfP.js.gz +0 -0
- package/dist/dashboard/assets/drawer-CMwPXwQB.js +0 -1
- package/dist/dashboard/assets/drawer-CMwPXwQB.js.gz +0 -0
- package/dist/dashboard/assets/entry--yj84zk3.js +0 -4
- package/dist/dashboard/assets/entry--yj84zk3.js.gz +0 -0
- package/dist/dashboard/assets/entry-D4O2229X.js.gz +0 -0
- package/dist/dashboard/assets/entry-DKvr5Q9K.js +0 -1
- package/dist/dashboard/assets/entry-DKvr5Q9K.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-zliLvBwr.js +0 -1
- package/dist/dashboard/assets/github-badge-zliLvBwr.js.gz +0 -0
- package/dist/dashboard/assets/index-CcD1eSJ6.js.gz +0 -0
- package/dist/dashboard/assets/inline-D1IlZZ8H.js +0 -1
- package/dist/dashboard/assets/inline-D1IlZZ8H.js.gz +0 -0
- package/dist/dashboard/assets/input-BpVZdrbc.js +0 -1
- package/dist/dashboard/assets/input-BpVZdrbc.js.gz +0 -0
- package/dist/dashboard/assets/layout-BioLW7gN.js +0 -1
- package/dist/dashboard/assets/layout-BioLW7gN.js.gz +0 -0
- package/dist/dashboard/assets/list-row-CwVcGAIc.js +0 -1
- package/dist/dashboard/assets/list-row-CwVcGAIc.js.gz +0 -0
- package/dist/dashboard/assets/live-yfEJFF9T.js.gz +0 -0
- package/dist/dashboard/assets/model-bans-CT-TxSNX.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CFL4_Dqc.js +0 -1
- package/dist/dashboard/assets/numbers-CFL4_Dqc.js.gz +0 -0
- package/dist/dashboard/assets/routing-Cs6jl49L.js.gz +0 -0
- package/dist/dashboard/assets/select-XPJ-dIw3.js +0 -41
- package/dist/dashboard/assets/select-XPJ-dIw3.js.gz +0 -0
- package/dist/dashboard/assets/settings-CfcTcOyU.js.gz +0 -0
- package/dist/dashboard/assets/stack-C9_uUjaA.js +0 -1
- package/dist/dashboard/assets/stack-C9_uUjaA.js.gz +0 -0
- package/dist/dashboard/assets/state-DA0B95UQ.js +0 -1
- package/dist/dashboard/assets/state-DA0B95UQ.js.gz +0 -0
- package/dist/dashboard/assets/switch-Cittxy-x.js +0 -1
- package/dist/dashboard/assets/switch-Cittxy-x.js.gz +0 -0
- package/dist/dashboard/assets/toast-CNt_jzO4.js +0 -2
- package/dist/dashboard/assets/toast-CNt_jzO4.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-BQKMcJ1H.js.gz +0 -0
|
@@ -1,75 +1,73 @@
|
|
|
1
1
|
{
|
|
2
|
-
"count":
|
|
2
|
+
"count": 114,
|
|
3
3
|
"assets": [
|
|
4
|
-
"assets\\AccountCooldown-
|
|
5
|
-
"assets\\BannedPage-
|
|
6
|
-
"assets\\CliToolDetailPage-
|
|
7
|
-
"assets\\CliToolsPage-
|
|
8
|
-
"assets\\CombosPage-
|
|
9
|
-
"assets\\ConsoleLogPage-
|
|
10
|
-
"assets\\CustomizationPage-
|
|
11
|
-
"assets\\HealthEventsModal-
|
|
12
|
-
"assets\\LoginPage-
|
|
13
|
-
"assets\\ModelPicker-
|
|
14
|
-
"assets\\OverviewPage-
|
|
15
|
-
"assets\\ProviderDetailPage-
|
|
4
|
+
"assets\\AccountCooldown-Bjo92Qat.js",
|
|
5
|
+
"assets\\BannedPage-MsQaeQTs.js",
|
|
6
|
+
"assets\\CliToolDetailPage-BO9nKg9w.js",
|
|
7
|
+
"assets\\CliToolsPage-DfWHTJ7v.js",
|
|
8
|
+
"assets\\CombosPage-BrCBQDAc.js",
|
|
9
|
+
"assets\\ConsoleLogPage-C6Uh33er.js",
|
|
10
|
+
"assets\\CustomizationPage-8aS9hyJu.js",
|
|
11
|
+
"assets\\HealthEventsModal-qomXThxX.js",
|
|
12
|
+
"assets\\LoginPage-C8hKyXBe.js",
|
|
13
|
+
"assets\\ModelPicker-CWkTUtpX.js",
|
|
14
|
+
"assets\\OverviewPage-C-ExCy1V.js",
|
|
15
|
+
"assets\\ProviderDetailPage-tr811uaF.js",
|
|
16
16
|
"assets\\ProviderIcon-CmfzxpVM.js",
|
|
17
|
-
"assets\\ProvidersPage-
|
|
18
|
-
"assets\\ProxyPage-
|
|
19
|
-
"assets\\QuotaPage-
|
|
20
|
-
"assets\\SettingsPage-
|
|
21
|
-
"assets\\SetupPage-
|
|
22
|
-
"assets\\SortableList-
|
|
23
|
-
"assets\\StudioPage-
|
|
17
|
+
"assets\\ProvidersPage-BS9IEpvv.js",
|
|
18
|
+
"assets\\ProxyPage-Rn_qXScb.js",
|
|
19
|
+
"assets\\QuotaPage-BihkgQSq.js",
|
|
20
|
+
"assets\\SettingsPage-DhiEjm6T.js",
|
|
21
|
+
"assets\\SetupPage-CXjUlfqr.js",
|
|
22
|
+
"assets\\SortableList-C-_oD3F2.js",
|
|
23
|
+
"assets\\StudioPage-u9IxmYOI.js",
|
|
24
24
|
"assets\\ToolIcon-K1ckcDKg.js",
|
|
25
|
-
"assets\\UsagePage-
|
|
26
|
-
"assets\\api-keys-
|
|
27
|
-
"assets\\app-
|
|
28
|
-
"assets\\app-
|
|
25
|
+
"assets\\UsagePage-BEVLVrwP.js",
|
|
26
|
+
"assets\\api-keys-uNywYlfv.js",
|
|
27
|
+
"assets\\app-CafWtVXA.js",
|
|
28
|
+
"assets\\app-DxivpfYG.css",
|
|
29
29
|
"assets\\arrow-left-CpBxkoxI.js",
|
|
30
30
|
"assets\\arrow-right-Dl5yKnp8.js",
|
|
31
|
-
"assets\\assert-
|
|
31
|
+
"assets\\assert-NHwYq95t.js",
|
|
32
32
|
"assets\\backup-file-gdKIlg8U.js",
|
|
33
|
-
"assets\\badge-
|
|
34
|
-
"assets\\banner-
|
|
33
|
+
"assets\\badge-6uzDDvqs.js",
|
|
34
|
+
"assets\\banner-4_WNg2WZ.js",
|
|
35
35
|
"assets\\brain-Cwed_72z.js",
|
|
36
|
-
"assets\\card
|
|
37
|
-
"assets\\check-D69FzdvC.js",
|
|
36
|
+
"assets\\card--AtFedAq.js",
|
|
38
37
|
"assets\\chevron-right-BXIy5l3h.js",
|
|
39
38
|
"assets\\circle-alert-CntYHV5v.js",
|
|
40
39
|
"assets\\circle-check-BU3v_mFW.js",
|
|
41
|
-
"assets\\cli-tools-
|
|
42
|
-
"assets\\clipboard-button-
|
|
40
|
+
"assets\\cli-tools-B3e7dx3N.js",
|
|
41
|
+
"assets\\clipboard-button-NreUx6c8.js",
|
|
43
42
|
"assets\\cog-CdWhsajs.js",
|
|
44
|
-
"assets\\
|
|
45
|
-
"assets\\dialog-BvQeBMfP.js",
|
|
43
|
+
"assets\\dialog-BtpsJU98.js",
|
|
46
44
|
"assets\\dialog-actions-B1axpEg7.js",
|
|
47
45
|
"assets\\download-A0SW9OCW.js",
|
|
48
46
|
"assets\\download-dRFo5_Wu.js",
|
|
49
|
-
"assets\\drawer-
|
|
50
|
-
"assets\\entry
|
|
51
|
-
"assets\\entry-
|
|
52
|
-
"assets\\entry-
|
|
47
|
+
"assets\\drawer-WwwtzihF.js",
|
|
48
|
+
"assets\\entry-Dj9AR0gY.js",
|
|
49
|
+
"assets\\entry-TJIye_jT.js",
|
|
50
|
+
"assets\\entry-jy9_4-Ke.js",
|
|
53
51
|
"assets\\eye-C_hawfH4.js",
|
|
54
52
|
"assets\\eye-off-Bk5g2luF.js",
|
|
55
53
|
"assets\\fingerprint-pattern-BWuHtbW4.js",
|
|
56
54
|
"assets\\gauge-BbIMy7MB.js",
|
|
57
|
-
"assets\\github-badge-
|
|
55
|
+
"assets\\github-badge-D_eM3IgG.js",
|
|
58
56
|
"assets\\image-BPaouViG.js",
|
|
59
|
-
"assets\\index-
|
|
60
|
-
"assets\\inline-
|
|
61
|
-
"assets\\input-
|
|
57
|
+
"assets\\index-DCBtROoK.js",
|
|
58
|
+
"assets\\inline-BcNGF8Gr.js",
|
|
59
|
+
"assets\\input-BG41et9G.js",
|
|
62
60
|
"assets\\jsx-runtime-CXUMngI_.js",
|
|
63
61
|
"assets\\key-round-UkavsGxp.js",
|
|
64
62
|
"assets\\layers-2-glBbjl.js",
|
|
65
|
-
"assets\\layout-
|
|
66
|
-
"assets\\list-row-
|
|
67
|
-
"assets\\live-
|
|
63
|
+
"assets\\layout-C8mer_Ak.js",
|
|
64
|
+
"assets\\list-row-Gh_W4OR_.js",
|
|
65
|
+
"assets\\live-BJGYRBnR.js",
|
|
68
66
|
"assets\\log-in-BmRjy60c.js",
|
|
69
67
|
"assets\\message-circle-CCxGwRvv.js",
|
|
70
|
-
"assets\\model-bans-
|
|
68
|
+
"assets\\model-bans-DeVLDA7I.js",
|
|
71
69
|
"assets\\mutation-BkoLM5Ba.js",
|
|
72
|
-
"assets\\numbers-
|
|
70
|
+
"assets\\numbers-Bs4LOOuf.js",
|
|
73
71
|
"assets\\pencil-768ysRAr.js",
|
|
74
72
|
"assets\\plus-DIdX_g-x.js",
|
|
75
73
|
"assets\\power-off-Yjw9GMJh.js",
|
|
@@ -96,26 +94,25 @@
|
|
|
96
94
|
"assets\\refresh-cw-BqJFk4sU.js",
|
|
97
95
|
"assets\\rotate-ccw-CufcqITi.js",
|
|
98
96
|
"assets\\route-CNSNj9Zm.js",
|
|
99
|
-
"assets\\routing-
|
|
97
|
+
"assets\\routing-CcusqXA7.js",
|
|
100
98
|
"assets\\scaling-nWE4Cx8f.js",
|
|
101
99
|
"assets\\search-JOKus9rb.js",
|
|
102
|
-
"assets\\select-
|
|
103
|
-
"assets\\settings-
|
|
100
|
+
"assets\\select-DsX5QT8K.js",
|
|
101
|
+
"assets\\settings-mpmXTgUa.js",
|
|
104
102
|
"assets\\shield-check-DVS0Foqh.js",
|
|
105
103
|
"assets\\sparkles-BRaRUxAU.js",
|
|
106
|
-
"assets\\stack-
|
|
107
|
-
"assets\\state-
|
|
104
|
+
"assets\\stack-DwLmjUFK.js",
|
|
105
|
+
"assets\\state-DYdQmdAl.js",
|
|
108
106
|
"assets\\stream-retry-5QnVuSSo.js",
|
|
109
|
-
"assets\\switch-
|
|
107
|
+
"assets\\switch-CidSfbG7.js",
|
|
110
108
|
"assets\\terminal-Bd4ee8TA.js",
|
|
111
|
-
"assets\\toast-CNt_jzO4.js",
|
|
112
109
|
"assets\\tokens-DDxMbSRL.js",
|
|
113
110
|
"assets\\trash-Cppn5c3R.js",
|
|
114
111
|
"assets\\use-clipboard-B0Sy03fE.js",
|
|
115
112
|
"assets\\use-presence-BSZEBx7U.js",
|
|
116
113
|
"assets\\use-reduced-motion-C4B0R6Sz.js",
|
|
117
114
|
"assets\\use-timeout-BwcBBRrT.js",
|
|
118
|
-
"assets\\useMutation-
|
|
115
|
+
"assets\\useMutation-w4u4CVue.js",
|
|
119
116
|
"assets\\wrench-B6E1oEuY.js",
|
|
120
117
|
"index.html"
|
|
121
118
|
]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cartethyia",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.2",
|
|
4
4
|
"description": "A powerful, self-hosted reverse proxy for OpenAI- and Anthropic-compatible AI clients, powered by native Bun and Elysia. It serves both protocols from a single server, routes every model across any configured provider, and translates requests and responses in real time — streaming and prompt caching included. Underneath it runs a full delivery engine: per-account health tracking, quota and cooldown enforcement, automatic failover across pools and providers, usage accounting, and telemetry. Point OpenCode, Cline, Claude Code, Codex, or any compatible client at Cartethyia and it keeps its native protocol while the gateway absorbs every provider-specific difference behind it. Runs on embedded SQLite or external PostgreSQL with optional Redis coordination, ships as a global CLI with a system-tray console, and needs only Bun 1.4+ alongside Node.js/npm.",
|
|
5
5
|
"license": "GPL-3.0-only",
|
|
6
6
|
"type": "module",
|
package/src/boot/app.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { GatewayError, publicGatewayErrorBody } from "../protocol/gateway-error"
|
|
|
15
15
|
import { shutdownError, shutdownNotice } from "../transport/shutdown-notice";
|
|
16
16
|
import type { CanonicalAdapter } from "../transport/contracts";
|
|
17
17
|
import type { ApiKeyAuthorizationSnapshot } from "../security/api-key-auth";
|
|
18
|
+
import { unknownApiKeyError } from "../security/api-key-auth";
|
|
18
19
|
import { getDbHandle } from "../persistence/boot";
|
|
19
20
|
import { isPgHandle } from "../persistence/db-pg";
|
|
20
21
|
import { resolveRedisClient } from "../persistence/redis";
|
|
@@ -408,7 +409,7 @@ export function createGatewayApp(deps: GatewayAppDeps) {
|
|
|
408
409
|
const handleModelsList = async ({ request }: { request: Request }): Promise<Response> => {
|
|
409
410
|
const state = requestStateStore.require(request);
|
|
410
411
|
const auth = state.authorization;
|
|
411
|
-
if (!auth) throw
|
|
412
|
+
if (!auth) throw unknownApiKeyError();
|
|
412
413
|
const data = await queryAllowedModels(auth);
|
|
413
414
|
return new Response(JSON.stringify({ object: "list", data }), {
|
|
414
415
|
headers: {
|
|
@@ -431,7 +432,7 @@ export function createGatewayApp(deps: GatewayAppDeps) {
|
|
|
431
432
|
}): Promise<Response> => {
|
|
432
433
|
const state = requestStateStore.require(request);
|
|
433
434
|
const auth = state.authorization;
|
|
434
|
-
if (!auth) throw
|
|
435
|
+
if (!auth) throw unknownApiKeyError();
|
|
435
436
|
const raw = params?.["*"] ?? params?.id ?? "";
|
|
436
437
|
const targetId = new URL(request.url).searchParams.get("id") ?? raw;
|
|
437
438
|
const found = await publicModelStore.getPublicModelDetail(
|
|
@@ -45,7 +45,17 @@ export interface SystemHealthResponse {
|
|
|
45
45
|
* gateway itself grew.
|
|
46
46
|
*/
|
|
47
47
|
database_in_process: boolean;
|
|
48
|
-
|
|
48
|
+
/**
|
|
49
|
+
* `null` when the platform does not report it — there is no `loadavg` on
|
|
50
|
+
* Windows and Node hands back a zeroed tuple rather than failing, so on
|
|
51
|
+
* Windows this is always `null` rather than a confident-looking `0`.
|
|
52
|
+
*
|
|
53
|
+
* Nullable rather than `NaN`: `NaN` is not a JSON value and serializes to
|
|
54
|
+
* `null` anyway, which means a client validating the field as a number
|
|
55
|
+
* rejects the *whole* health payload. One unavailable metric then reads as
|
|
56
|
+
* every metric being unavailable.
|
|
57
|
+
*/
|
|
58
|
+
cpu_percent: number | null;
|
|
49
59
|
cpu_cores: number;
|
|
50
60
|
pid: number;
|
|
51
61
|
platform: string;
|
|
@@ -330,12 +330,15 @@ export class DrizzleObservabilityStore implements ObservabilityStore {
|
|
|
330
330
|
// syscall and Node reports a zeroed tuple rather than failing. Divided by
|
|
331
331
|
// cores that is a confident-looking 0.0% on a busy gateway, which is worse
|
|
332
332
|
// than saying nothing: an operator reads it as "idle" and stops looking.
|
|
333
|
-
// Reported as
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
333
|
+
// Reported as null instead of as a measurement.
|
|
334
|
+
//
|
|
335
|
+
// A real zero load is *not* the same thing and is reported as zero: an
|
|
336
|
+
// idle machine genuinely has nothing queued, and collapsing that into
|
|
337
|
+
// "unavailable" would hide a number the platform did supply.
|
|
338
|
+
const cpuPercent: number | null =
|
|
339
|
+
process.platform === "win32"
|
|
340
|
+
? null
|
|
341
|
+
: Math.round(((loadavg()[0] ?? 0) / Math.max(cpus().length, 1)) * 10000) / 100;
|
|
339
342
|
|
|
340
343
|
let databaseHealthy = true;
|
|
341
344
|
let total = 0;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The free tier shared by the OpenRouter-backed providers.
|
|
3
3
|
*
|
|
4
4
|
* Cline, Kilo Code and OpenRouter serve one model catalog: the same 469 ids,
|
|
5
|
-
* and the same
|
|
5
|
+
* and the same 9 of them carrying a `:free` suffix. Verified against all three
|
|
6
6
|
* live listings — every id below is present under `openrouter`, `kilo` and
|
|
7
7
|
* `cline` — so this roster is a catalog fact, not a per-provider guess, and it
|
|
8
8
|
* lives in one place rather than being restated three times.
|
|
@@ -37,10 +37,10 @@ interface FreeRow {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
|
-
* The
|
|
40
|
+
* The 9 `:free` ids, with the limits the catalog states for each.
|
|
41
41
|
*
|
|
42
42
|
* Every row declares `reasoning`: `supported_parameters` lists it on all
|
|
43
|
-
*
|
|
43
|
+
* nine. `toolCall` is `true` unless stated otherwise, because a row that
|
|
44
44
|
* advertises tools must keep them — a false `toolCall` makes the preflight
|
|
45
45
|
* strip `tools` from the request, which is a wrong capability rather than a
|
|
46
46
|
* conservative one.
|
|
@@ -48,8 +48,6 @@ interface FreeRow {
|
|
|
48
48
|
const FREE_ROWS: readonly FreeRow[] = [
|
|
49
49
|
{ id: "cohere/north-mini-code:free", ctx: 256_000, out: 64_000 },
|
|
50
50
|
{ id: "dots-studio/dots-3-note-preview:free", ctx: 512_000, out: 460_800, vision: true },
|
|
51
|
-
{ id: "google/gemma-4-26b-a4b-it:free", ctx: 262_144, out: 32_768, vision: true },
|
|
52
|
-
{ id: "google/gemma-4-31b-it:free", ctx: 262_144, out: 32_768, vision: true },
|
|
53
51
|
{ id: "liquid/lfm-2.5-2.6b:free", ctx: 65_536, out: 8_192 },
|
|
54
52
|
{ id: "minimax/minimax-m3.1-flash:free", ctx: 1_000_000, out: 524_288, vision: true },
|
|
55
53
|
{
|
|
@@ -58,7 +56,6 @@ const FREE_ROWS: readonly FreeRow[] = [
|
|
|
58
56
|
out: 65_536,
|
|
59
57
|
vision: true,
|
|
60
58
|
},
|
|
61
|
-
{ id: "nvidia/nemotron-3-super-120b-a12b:free", ctx: 262_144, out: 235_929 },
|
|
62
59
|
{ id: "nvidia/nemotron-3-ultra-550b-a55b:free", ctx: 1_000_000, out: 65_536 },
|
|
63
60
|
{
|
|
64
61
|
id: "nvidia/nemotron-3.5-content-safety:free",
|
|
@@ -69,11 +66,6 @@ const FREE_ROWS: readonly FreeRow[] = [
|
|
|
69
66
|
},
|
|
70
67
|
{ id: "nvidia/nemotron-3.5-lightning:free", ctx: 1_000_000, out: 65_536 },
|
|
71
68
|
{ id: "nvidia/nemotron-3.5-super-vl:free", ctx: 1_000_000, out: 131_072, vision: true },
|
|
72
|
-
{ id: "poolside/laguna-s-2.1:free", ctx: 262_144, out: 32_768 },
|
|
73
|
-
{ id: "poolside/laguna-xs-2.1:free", ctx: 262_144, out: 32_768 },
|
|
74
|
-
{ id: "thinkingmachines/inkling-small:free", ctx: 1_048_576, out: 262_144, vision: true },
|
|
75
|
-
{ id: "thinkingmachines/inkling:free", ctx: 1_048_576, out: 262_144, vision: true },
|
|
76
|
-
{ id: "xingchen-agi/xing4.0-29b-a4b:free", ctx: 262_144, out: 235_929 },
|
|
77
69
|
];
|
|
78
70
|
|
|
79
71
|
/** Builds this roster for one provider, since each spells its own endpoint. */
|
|
@@ -37,6 +37,7 @@ import { resolveByokWireProfile } from "../operations/byok-wire-profile";
|
|
|
37
37
|
import { recordAccountFailure, recordAccountSuccess } from "../operations/account-health-service";
|
|
38
38
|
import { buildCapabilityProfile } from "../../transport/routing/route-catalog";
|
|
39
39
|
import { classifyUpstreamFailure } from "../../transport/failure-policy";
|
|
40
|
+
import { terminalFailure } from "../../transport/dispatch/attempt-finalize";
|
|
40
41
|
import { GatewayError } from "../../protocol/gateway-error";
|
|
41
42
|
import type { ModelDefinition } from "../provider-model-definition";
|
|
42
43
|
import type { ProviderAdapter, ProviderId } from "../provider-contracts";
|
|
@@ -579,13 +580,33 @@ export async function recordProbeHealth(args: {
|
|
|
579
580
|
readonly invalidator: { invalidate(): unknown } | undefined;
|
|
580
581
|
readonly accountId: string | undefined;
|
|
581
582
|
readonly dispatchError: unknown;
|
|
583
|
+
/**
|
|
584
|
+
* The probe's canonical events, when there are any.
|
|
585
|
+
*
|
|
586
|
+
* Needed because some adapters deliver a refusal as a terminal event rather
|
|
587
|
+
* than as a thrown error — Cursor's `resource_exhausted` is the case — so
|
|
588
|
+
* `dispatchError` stays `undefined` for a Run the upstream refused. Without
|
|
589
|
+
* the events, that account looked like a success and was recovered.
|
|
590
|
+
*/
|
|
591
|
+
readonly events?: readonly CanonicalEvent[] | undefined;
|
|
582
592
|
readonly modelId: string;
|
|
583
593
|
readonly upstreamStatusCode: number | undefined;
|
|
584
594
|
}): Promise<void> {
|
|
585
595
|
const { db, invalidator, accountId, dispatchError, modelId, upstreamStatusCode } = args;
|
|
586
|
-
|
|
596
|
+
// A terminal `failed` state is written only by the upstream decoders, so it
|
|
597
|
+
// is the upstream refusing — the same thing a thrown error would have meant.
|
|
598
|
+
const terminal = args.events?.find((event) => event.type === "terminal");
|
|
599
|
+
// Reconstructed through the very function the live dispatch path uses, so a
|
|
600
|
+
// refusal carries the same code, status, and health classification in both.
|
|
601
|
+
// Hand-rolling a category here is how the two paths drifted apart.
|
|
602
|
+
const refusedUpstream =
|
|
603
|
+
terminal?.type === "terminal" && terminal.state === "failed"
|
|
604
|
+
? terminalFailure(terminal)
|
|
605
|
+
: undefined;
|
|
606
|
+
const failure = dispatchError ?? refusedUpstream;
|
|
607
|
+
if (failure !== undefined && accountId) {
|
|
587
608
|
const failureEvidence = {
|
|
588
|
-
...classifyUpstreamFailure(
|
|
609
|
+
...classifyUpstreamFailure(failure),
|
|
589
610
|
modelId,
|
|
590
611
|
...(upstreamStatusCode === undefined ? {} : { statusCode: upstreamStatusCode }),
|
|
591
612
|
};
|
|
@@ -593,7 +614,7 @@ export async function recordProbeHealth(args: {
|
|
|
593
614
|
// already classified as `rate_limit_transient` with a real cooldown by
|
|
594
615
|
// `classifyAccountError`, so writing a status here only clobbered that
|
|
595
616
|
// cooldown with a state nothing recovers from.
|
|
596
|
-
await recordAccountFailure(db, accountId,
|
|
617
|
+
await recordAccountFailure(db, accountId, failure, failureEvidence)
|
|
597
618
|
.then((classification) => {
|
|
598
619
|
if (classification?.mutatesAccount) void invalidator?.invalidate();
|
|
599
620
|
})
|
|
@@ -603,7 +624,7 @@ export async function recordProbeHealth(args: {
|
|
|
603
624
|
// (a cooling account flips back to active instead of staying stale).
|
|
604
625
|
// recordAccountSuccess is a no-op write for already-healthy accounts and
|
|
605
626
|
// never throws.
|
|
606
|
-
if (
|
|
627
|
+
if (failure === undefined && accountId) {
|
|
607
628
|
await recordAccountSuccess(db, accountId)
|
|
608
629
|
.then((recovered) => {
|
|
609
630
|
if (recovered) void invalidator?.invalidate();
|
|
@@ -703,7 +724,23 @@ export function computeProbeVerdict(args: {
|
|
|
703
724
|
errorMessage = `${wireFamily} — ${tagged}${upstreamReq ? ` [req ${upstreamReq}]` : ""}`;
|
|
704
725
|
|
|
705
726
|
} else if (terminal?.type === "terminal" && terminal.state !== "complete") {
|
|
706
|
-
|
|
727
|
+
// Read the terminal's own detail before falling back to the stop reason.
|
|
728
|
+
//
|
|
729
|
+
// `stop_reason` is a canonical one-word label — "error" — so reporting it
|
|
730
|
+
// alone turned a Cursor quota refusal into a toast whose entire body was
|
|
731
|
+
// the word "error". The upstream sentence an operator needs is in
|
|
732
|
+
// `stop_details`, which is where the adapter parks it and where the live
|
|
733
|
+
// dispatch path already reads it (see `terminalFailure` in
|
|
734
|
+
// `attempt-finalize.ts`). The probe path disagreed with the dispatch path
|
|
735
|
+
// about the same provider response.
|
|
736
|
+
const detail = terminal.stop_details;
|
|
737
|
+
const message =
|
|
738
|
+
typeof detail?.["message"] === "string" && detail["message"].trim().length > 0
|
|
739
|
+
? detail["message"].trim()
|
|
740
|
+
: undefined;
|
|
741
|
+
const code = typeof detail?.["code"] === "string" ? detail["code"] : undefined;
|
|
742
|
+
const tag = code === undefined ? "" : ` (${code})`;
|
|
743
|
+
errorMessage = `${message ?? terminal.stop_reason ?? "Model probe failed"}${tag}`;
|
|
707
744
|
} else {
|
|
708
745
|
errorMessage = undefined;
|
|
709
746
|
}
|
|
@@ -274,7 +274,13 @@ export class ProviderProbingService {
|
|
|
274
274
|
// The same wrapper a real dispatch uses, so the probe reads the model the
|
|
275
275
|
// upstream named from the raw bytes rather than from a synthesized event.
|
|
276
276
|
const probeFetch = createProbeFetch(
|
|
277
|
-
|
|
277
|
+
// Body capture on, unlike a live dispatch: a probe is one-shot and
|
|
278
|
+
// operator-initiated, so it exists to be inspected. With it off, the
|
|
279
|
+
// provider-response tee resolved to null and every probe row reported
|
|
280
|
+
// the middle two stages as absent — a constant, not a measurement,
|
|
281
|
+
// which made the stage markers useless for the diagnosis they exist
|
|
282
|
+
// for. The 256 KiB cap in the tee bounds the cost.
|
|
283
|
+
captureProviderExchange(outbound.fetch, probeCapture, true),
|
|
278
284
|
);
|
|
279
285
|
try {
|
|
280
286
|
if (serviceKind === "websearch") {
|
|
@@ -435,6 +441,11 @@ export class ProviderProbingService {
|
|
|
435
441
|
invalidator: this.snapshotInvalidator,
|
|
436
442
|
accountId,
|
|
437
443
|
dispatchError,
|
|
444
|
+
// Passed so a refusal delivered as a terminal event — not as a thrown
|
|
445
|
+
// error — still reaches the health machine. Without it, Cursor's
|
|
446
|
+
// `resource_exhausted` left `dispatchError` undefined and the probe
|
|
447
|
+
// recovered an exhausted account.
|
|
448
|
+
...(events === undefined ? {} : { events }),
|
|
438
449
|
modelId,
|
|
439
450
|
upstreamStatusCode,
|
|
440
451
|
});
|
|
@@ -496,12 +507,24 @@ export class ProviderProbingService {
|
|
|
496
507
|
...(tokensPerSec !== undefined ? { tokensPerSec } : {}),
|
|
497
508
|
});
|
|
498
509
|
if (this.telemetryBuffer && payloadCaptureEnabled) {
|
|
510
|
+
// All four surfaces, so the row's stage markers mean the same thing here
|
|
511
|
+
// as they do on a live request: which hop actually produced bytes.
|
|
512
|
+
const providerRequest = probeCapture?.request ?? null;
|
|
513
|
+
const providerResponse = await probeCapture?.response.catch(() => null) ?? null;
|
|
499
514
|
await this.payloadCapture
|
|
500
515
|
.capture({
|
|
501
516
|
tenantId,
|
|
502
517
|
requestId,
|
|
503
518
|
requestBody: capturedRequest,
|
|
504
|
-
|
|
519
|
+
// `events` is what the *client* receives, so it belongs in the client
|
|
520
|
+
// surface. Passing it as `responseBody` lit the client bit on probes
|
|
521
|
+
// whose event list was empty, over a body that was never captured.
|
|
522
|
+
// The legacy alias is explicitly absent here; `null` leaves bit 8 to
|
|
523
|
+
// be decided by the real client body alone.
|
|
524
|
+
responseBody: null,
|
|
525
|
+
...(events === undefined ? {} : { clientResponseBody: events }),
|
|
526
|
+
...(providerRequest === null ? {} : { providerRequestBody: providerRequest }),
|
|
527
|
+
...(providerResponse === null ? {} : { providerResponseBody: providerResponse }),
|
|
505
528
|
scope: "tenant",
|
|
506
529
|
tenantOptIn: true,
|
|
507
530
|
})
|
|
@@ -96,7 +96,9 @@ export const CLAUDE_MODELS: readonly ModelDefinition[] = [
|
|
|
96
96
|
claudeMessagesModel("claude-sonnet-5", 1_000_000, 128_000, true),
|
|
97
97
|
claudeMessagesModel("claude-sonnet-4-6", 1_000_000, 128_000, true),
|
|
98
98
|
claudeMessagesModel("claude-opus-4-5", 200_000, 64_000, true),
|
|
99
|
-
|
|
99
|
+
// 200K: the 4.5 trio predates the million-token window, which arrived with
|
|
100
|
+
// Opus 4.6. Only this row claimed 1M while its own siblings said 200K.
|
|
101
|
+
claudeMessagesModel("claude-sonnet-4-5", 200_000, 64_000, true),
|
|
100
102
|
claudeMessagesModel("claude-haiku-4-5", 200_000, 64_000, true),
|
|
101
103
|
];
|
|
102
104
|
|
|
@@ -59,7 +59,9 @@ export const CLOUDFLAREAI_MODELS: readonly ModelDefinition[] = [
|
|
|
59
59
|
["claude-opus-4.7", 1_000_000, 128_000] as const,
|
|
60
60
|
["claude-opus-4.8", 1_000_000, 128_000] as const,
|
|
61
61
|
["claude-opus-5", 1_000_000, 128_000] as const,
|
|
62
|
-
|
|
62
|
+
// 200K, matching the other two 4.5 rows: the million-token window starts at
|
|
63
|
+
// Opus 4.6, so the 4.5 trio is 200K across the board.
|
|
64
|
+
["claude-sonnet-4.5", 200_000, 64_000] as const,
|
|
63
65
|
["claude-sonnet-4.6", 1_000_000, 128_000] as const,
|
|
64
66
|
["claude-sonnet-5", 1_000_000, 128_000] as const,
|
|
65
67
|
].map(cloudflareaiModel);
|
|
@@ -124,7 +124,9 @@ const INFERHUB_MODELS_RAW: readonly {
|
|
|
124
124
|
{ id: "cc/claude-opus-4-8", contextLimit: 1000000, outputLimit: 128000 },
|
|
125
125
|
{ id: "cc/claude-opus-5", contextLimit: 1000000, outputLimit: 128000 },
|
|
126
126
|
{ id: "cc/claude-sonnet-4-5", contextLimit: 200000, outputLimit: 64000 },
|
|
127
|
-
|
|
127
|
+
// 128K: Sonnet 4.6 is in the 1M/128K generation, the same as the Opus 4.6
|
|
128
|
+
// row directly above it and every other 4.6+ row here.
|
|
129
|
+
{ id: "cc/claude-sonnet-4-6", contextLimit: 1000000, outputLimit: 128000 },
|
|
128
130
|
{ id: "cc/claude-sonnet-5", contextLimit: 1000000, outputLimit: 128000 },
|
|
129
131
|
// cb/ — CodeBuddy (12 models)
|
|
130
132
|
{ id: "cb/claude-opus-4.6", contextLimit: 1000000, outputLimit: 128000 },
|
|
@@ -87,7 +87,6 @@ export const KILO_MODELS: readonly ModelDefinition[] = [
|
|
|
87
87
|
// window the router may select into rather than one model's.
|
|
88
88
|
kiloModel("kilo-auto/free", 256_000, 32_768, { reasoning: true }),
|
|
89
89
|
kiloModel("kilo-auto/small", 262_144, 32_768, { vision: true, reasoning: true }),
|
|
90
|
-
kiloModel("openrouter/auto", 2_000_000, 128_000, { reasoning: true }),
|
|
91
90
|
];
|
|
92
91
|
|
|
93
92
|
/**
|
|
@@ -71,9 +71,15 @@ export const KIRO_MODELS: readonly ModelDefinition[] = [
|
|
|
71
71
|
kiroModel({ id: "claude-opus-4.5", ctx: 200_000, out: 64_000, vision: true, reasoning: true, toolCall: true }),
|
|
72
72
|
kiroModel({ id: "claude-sonnet-5", ctx: 1_000_000, out: 128_000, vision: true, reasoning: true, toolCall: true }),
|
|
73
73
|
kiroModel({ id: "claude-sonnet-5.5", ctx: 1_000_000, out: 128_000, vision: true, reasoning: true, toolCall: true }),
|
|
74
|
-
|
|
74
|
+
// 200K, not the 1M the 1M-window models use: Sonnet 4.5 predates it. The
|
|
75
|
+
// million-token window arrived with Opus 4.6 and the 5.x generation; the 4.5
|
|
76
|
+
// trio is 200K, and Opus and Haiku here already say so.
|
|
77
|
+
kiroModel({ id: "claude-sonnet-4.5", ctx: 200_000, out: 64_000, vision: true, reasoning: true, toolCall: true }),
|
|
75
78
|
kiroModel({ id: "claude-haiku-4.5", ctx: 200_000, out: 64_000, vision: true, reasoning: true, toolCall: true }),
|
|
76
|
-
|
|
79
|
+
// 1M/128K, not the 200K/64K of the 4.5 row above it: Haiku moved to the
|
|
80
|
+
// million-token window and the doubled output ceiling with the rest of the
|
|
81
|
+
// 5.x generation, and this line had been copied from its predecessor.
|
|
82
|
+
kiroModel({ id: "claude-haiku-5.5", ctx: 1_000_000, out: 128_000, vision: true, reasoning: true, toolCall: true }),
|
|
77
83
|
kiroModel({ id: "gpt-5.5", ctx: 272_000, out: 128_000, reasoning: true, toolCall: true }),
|
|
78
84
|
kiroModel({ id: "gpt-5.6-sol", ctx: 272_000, out: 128_000, reasoning: true, toolCall: true }),
|
|
79
85
|
kiroModel({ id: "gpt-5.6-terra", ctx: 272_000, out: 128_000, reasoning: true, toolCall: true }),
|
|
@@ -24,21 +24,11 @@ const OPENROUTER_REQUIRED_HEADERS: Readonly<Record<string, string>> = {
|
|
|
24
24
|
* served by the native System One route, never by the chat surface.
|
|
25
25
|
*/
|
|
26
26
|
export const OPENROUTER_MODELS: readonly ModelDefinition[] = [
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
id: "openrouter/auto",
|
|
33
|
-
providerId: OPENROUTER_PROVIDER_ID,
|
|
34
|
-
wireFamily: "chat",
|
|
35
|
-
endpoint: "/chat/completions",
|
|
36
|
-
// The catalog publishes no limits for a router (pricing is -1, meaning
|
|
37
|
-
// "decided at dispatch"), so this is the largest window it may select into.
|
|
38
|
-
ctx: 2_000_000,
|
|
39
|
-
out: 128_000,
|
|
40
|
-
reasoning: true,
|
|
41
|
-
}),
|
|
27
|
+
// No committed chat rows. OpenRouter adds and pulls models within days, so a
|
|
28
|
+
// hand-kept list of ids drifts out of truth and discovery is the authority;
|
|
29
|
+
// the free roster below stays as the offline floor. `openrouter/auto` was the
|
|
30
|
+
// last committed chat row and went with the rest of the static catalog — it
|
|
31
|
+
// publishes no limits of its own anyway, since a router's pricing is -1.
|
|
42
32
|
// Not a chat row: it answers `{answers}` to a `{state, questions}` body, and
|
|
43
33
|
// is served by the native System One route. Kept because deleting it removes
|
|
44
34
|
// that route, which is a capability, not a stale catalog entry.
|
|
@@ -305,6 +305,29 @@ export interface ResolvedApiKey {
|
|
|
305
305
|
/** Share-template id whose remote CLI mappings are inherited by this child. */
|
|
306
306
|
readonly cliMappingOwnerId?: string;
|
|
307
307
|
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* The 401 a caller gets when a key resolves to nothing.
|
|
311
|
+
*
|
|
312
|
+
* One function, not a message repeated at each boundary: three surfaces refuse
|
|
313
|
+
* an unknown key, and any wording drift between them is a caller guessing which
|
|
314
|
+
* surface it hit. Unknown, revoked and expired stay deliberately
|
|
315
|
+
* indistinguishable — distinguishing them would let a caller confirm that a
|
|
316
|
+
* guessed token once existed — so there is no variant carrying a reason.
|
|
317
|
+
*
|
|
318
|
+
* The rotate instruction is unconditional for the same reason. The key that
|
|
319
|
+
* fails is usually the one seeded at first boot, which cannot be deleted and
|
|
320
|
+
* only rotates, and an operator hitting this on a personal key loses nothing by
|
|
321
|
+
* being told the same thing.
|
|
322
|
+
*/
|
|
323
|
+
export function unknownApiKeyError(): GatewayError {
|
|
324
|
+
return new GatewayError(
|
|
325
|
+
"authentication_failed",
|
|
326
|
+
401,
|
|
327
|
+
"Invalid API key. Rotate the default Cartethyia API key in API Keys and send the new key as: Authorization: Bearer <key>.",
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
308
331
|
/** Extracts exactly one supported credential source, preserving public auth statuses. */
|
|
309
332
|
export function requestToken(headers: Headers | Record<string, string>): string {
|
|
310
333
|
const get = (name: string): string | null =>
|
|
@@ -6,7 +6,7 @@ import { GatewayError } from "../../protocol/gateway-error";
|
|
|
6
6
|
import type { ProxyRequestStateStore } from "../request/state";
|
|
7
7
|
import { fastPathname } from "../pathname";
|
|
8
8
|
import type { CartethyiaDatabase } from "../../persistence/db-handle";
|
|
9
|
-
import { requestToken, resolveApiKeyAuthorization } from "../../security/api-key-auth";
|
|
9
|
+
import { requestToken, resolveApiKeyAuthorization, unknownApiKeyError } from "../../security/api-key-auth";
|
|
10
10
|
import {
|
|
11
11
|
deniedClientRouter,
|
|
12
12
|
detectClientRouter,
|
|
@@ -65,8 +65,7 @@ export function createApiKeyAuthenticationMiddleware(deps: {
|
|
|
65
65
|
"cartethyia",
|
|
66
66
|
);
|
|
67
67
|
});
|
|
68
|
-
if (!authorization)
|
|
69
|
-
throw new GatewayError("invalid_request", 401, "This API key is unknown, revoked, or expired. Issue a new key and send it as: Authorization: Bearer <key>.");
|
|
68
|
+
if (!authorization) throw unknownApiKeyError();
|
|
70
69
|
const decision = createAccessDecision({
|
|
71
70
|
id: authorization.id,
|
|
72
71
|
tenantId: authorization.tenantId,
|
|
Binary file
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{n as e,o as t,r as n,t as r}from"./jsx-runtime-CXUMngI_.js";import{t as i}from"./arrow-left-CpBxkoxI.js";import{t as a}from"./circle-check-BU3v_mFW.js";import{r as o,t as s}from"./toast-CNt_jzO4.js";import{t as ee}from"./download-A0SW9OCW.js";import{n as c,r as l}from"./state-DA0B95UQ.js";import{t as te}from"./route-CNSNj9Zm.js";import{t as u}from"./search-JOKus9rb.js";import{l as d}from"./query-keys-D7wX2vM5.js";import{Q as f,X as p}from"./entry--yj84zk3.js";import{n as m,r as h,t as g}from"./card-DEhRXXw4.js";import{n as _}from"./download-dRFo5_Wu.js";import{t as v}from"./badge-DpXT4Ixt.js";import{n as y}from"./input-BpVZdrbc.js";import{t as b}from"./select-XPJ-dIw3.js";import{t as ne}from"./ModelPicker-oQ-SDXp8.js";import{t as re}from"./switch-Cittxy-x.js";import{t as x}from"./stack-C9_uUjaA.js";import{n as S}from"./inline-D1IlZZ8H.js";import{f as ie,t as C}from"./api-keys-BmDbsior.js";import{a as w,i as ae,n as oe,o as T,r as se,t as E}from"./cli-tools-B0wBq-RL.js";import{t as D}from"./ToolIcon-K1ckcDKg.js";var ce=e(`book-open`,[[`path`,{d:`M12 5v16`,key:`1f6ucr`}],[`path`,{d:`M20.001 19A2 2 0 0022 17V5a2 2 0 00-1.999-2L16 3.002A5 5 0 0012 5a5 5 0 00-4-2H4a2 2 0 00-2 2v12a2 2 0 001.999 2H8a5 5 0 014 2 5 5 0 014-2z`,key:`1fyvmf`}]]),le=e(`circle-x`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`m15 9-6 6`,key:`1uzhvr`}],[`path`,{d:`m9 9 6 6`,key:`z0biqf`}]]),ue=e(`file-code-corner`,[[`path`,{d:`M4 12.15V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2h-3.35`,key:`1wthlu`}],[`path`,{d:`M14 2v5a1 1 0 0 0 1 1h5`,key:`wfsgrz`}],[`path`,{d:`m5 16-3 3 3 3`,key:`331omg`}],[`path`,{d:`m9 22 3-3-3-3`,key:`lsp7cz`}]]),de=e(`settings-2`,[[`path`,{d:`M14 17H5`,key:`gfn3mx`}],[`path`,{d:`M19 7h-9`,key:`6i9tg`}],[`circle`,{cx:`17`,cy:`17`,r:`3`,key:`18b49y`}],[`circle`,{cx:`7`,cy:`7`,r:`3`,key:`dfmy0x`}]]),O=t(n(),1);function k(e,t){return e==null?!1:e instanceof Set?e.has(t):e.includes(t)}function A(e){return e==null?0:e instanceof Set?e.size:e.length}function j(e){let t=e.lastIndexOf(`/`);return t<0?e:e.slice(t+1)}function M(e,t,n,r){let i=n?`${n}/${j(t)}`:void 0,a=[t,j(t),...i?[i]:[]];r&&r!==t&&a.push(r);let o=e.model_list;return e.model_access_mode===`blacklist`?a.some(e=>k(o,e))?`model-denied`:null:o==null||A(o)===0||a.some(e=>k(o,e))?null:`model-not-allowed`}function N(e,t,n,r){return M(e,t,n,r)===null}var P=r();function F(){let{toolId:e}=f(),t=w(),n=T(),r=C(),i=(0,O.useMemo)(()=>t.data?.find(t=>t.id===e)??null,[t.data,e]),a=(0,O.useMemo)(()=>{let t=n.data;if(t)return Array.isArray(t)?t.find(t=>t.toolId===e):t[e]},[n.data,e]);return t.isLoading?(0,P.jsx)(l,{label:`Loading tool details…`}):t.isError?(0,P.jsx)(c,{title:`Unable to load tool`,message:t.error?.message??`Could not load the CLI tool registry.`,onRetry:()=>void t.refetch()}):i?(0,P.jsx)(I,{tool:i,status:a,apiKeys:r.data??[]}):(0,P.jsxs)(x,{gap:`12px`,children:[(0,P.jsx)(p,{to:`/cli-tools`,className:`text-caption text-accent`,children:`← Back to CLI Tools`}),(0,P.jsx)(c,{title:`Tool not found`,message:`Unknown CLI tool id.`})]})}function I({tool:e,status:t,apiKeys:n}){let[r,c]=(0,O.useState)(``),[l,f]=(0,O.useState)(()=>typeof window>`u`?`http://localhost:12800`:window.location.origin),[C,w]=(0,O.useState)({}),[T,k]=(0,O.useState)(!1),[A,j]=(0,O.useState)({}),[M,F]=(0,O.useState)(null),[I,L]=(0,O.useState)(`slot`),R=(0,O.useRef)(null),z=(0,O.useMemo)(()=>n.filter(e=>e.keyMode===`personal`||e.keyMode===`share`&&e.parentKeyId===void 0),[n]),B=ae(e.id,r),V=se(),H=oe(),U=ie(),W=E(),G=z.find(e=>e.id===r),K=G?.scopes?.includes(`routing:cli_mapping`)===!0,q=G?.keyMode===`personal`,J=G?.modelAccessMode??`whitelist`,Y=G?.modelList,X=e=>N({model_access_mode:J,model_list:Y},e),fe=(0,O.useMemo)(()=>{let t=new Set;if(!e.mappingSupported)return t;for(let n of e.defaultModels){let e=A[n.alias]??``;if(e.length===0)continue;let r=C[n.alias]??n.defaultValue??n.id;!X(e)&&!X(r)&&t.add(n.alias)}return t},[e.defaultModels,e.mappingSupported,A,C,G]),pe=e=>{if(!G)return;let t=Y??[],n=J===`blacklist`?t.filter(t=>t!==e):[...new Set([...t,e])];U.mutate({keyId:G.id,request:{modelAccessMode:J,modelList:n}},{onSuccess:()=>s.success(J===`blacklist`?`Model unblocked for this key`:`Model allowed for this key`),onError:e=>s.error(d(e,`Could not update the API key.`))})};(0,O.useEffect)(()=>{let t=Object.fromEntries(e.defaultModels.map(e=>[e.alias,e.defaultValue??e.id]));w(t),j({}),R.current=null},[e,r]),(0,O.useEffect)(()=>{let t=B.data;if(t&&t.apiKeyId===r){if(k(t.enabled),t.mappings.length>0)w(e=>({...e,...Object.fromEntries(t.mappings.map(e=>[e.slotKey,e.sourceModel]))})),j(e=>({...e,...Object.fromEntries(t.mappings.map(e=>[e.slotKey,e.targetModel]))}));else{let t=Object.fromEntries(e.defaultModels.map(e=>[e.alias,e.defaultValue??e.id]));w(t),j({})}R.current=JSON.stringify({enabled:t.enabled,mappings:t.mappings})}},[r,B.data,e.defaultModels]);let me=t?.installed??!1,Z=t?.configured??!1,he=e.configType===`guide`,Q=(0,O.useMemo)(()=>e.defaultModels.map(e=>C[e.alias]??e.defaultValue??e.id),[C,e.defaultModels]),$=(0,O.useCallback)(()=>{if(e.mappingSupported)return{enabled:T,mappings:e.defaultModels.filter(e=>(A[e.alias]??``).length>0).map(e=>({slotKey:e.alias,sourceModel:C[e.alias]??e.defaultValue??e.id,targetModel:A[e.alias]??``,enabled:!0})).filter(e=>e.targetModel.length>0)}},[T,A,C,e.defaultModels,e.mappingSupported]);(0,O.useEffect)(()=>{if(!e.mappingSupported||r.length===0)return;let t=B.data;if(!t||t.apiKeyId!==r)return;let n=$();if(!n)return;let i=JSON.stringify({enabled:n.enabled,mappings:n.mappings});if(i===R.current)return;let a=setTimeout(()=>{V.mutate({toolId:e.id,keyId:r,input:n},{onSuccess:()=>{R.current=i},onError:e=>s.error(d(e,`Could not save mappings.`))})},600);return()=>clearTimeout(a)},[$,r,B.data,V,e.id,e.mappingSupported]);let ge=(0,O.useCallback)(()=>{let t=Object.fromEntries(e.defaultModels.map(e=>[e.alias,C[e.alias]??e.defaultValue??e.id])),n=e.defaultModels.find(e=>e.roleKind===`subagent`);return{endpoint:l,apiKey:``,modelIds:e.id===`claude`?[]:Q,modelSlots:t,...Q[0]?{activeModel:Q[0]}:{},...n?{subagentModel:C[n.alias]??n.defaultValue??n.id}:{},mappingOwnerId:r,...e.mappingSupported?{mapping:$()}:{}}},[Q,$,r,l,C,e]);return(0,P.jsxs)(x,{gap:`16px`,children:[(0,P.jsxs)(p,{to:`/cli-tools`,className:`inline-flex items-center gap-3 text-caption text-accent`,children:[(0,P.jsx)(i,{size:14}),`Back to CLI Tools`]}),(0,P.jsx)(g,{children:(0,P.jsxs)(m,{className:`flex flex-col gap-5`,children:[(0,P.jsxs)(S,{gap:`14px`,align:`flex-start`,className:`flex-wrap`,children:[(0,P.jsx)(D,{toolId:e.id,name:e.name,color:e.color,size:46}),(0,P.jsxs)(S,{gap:`8px`,className:`flex-wrap`,children:[(0,P.jsx)(`h1`,{className:`text-title font-bold m-0`,children:e.name}),me?(0,P.jsx)(v,{tone:`ok`,children:`Installed`}):(0,P.jsx)(v,{tone:`disabled`,children:`Not installed`}),Z?(0,P.jsx)(v,{tone:`accent`,children:`Configured`}):null,e.configType===`guide`?(0,P.jsx)(v,{tone:`info`,children:`Guide`}):null]})]}),(0,P.jsx)(`p`,{className:`m-0 text-caption text-text-secondary`,children:e.description}),e.docsUrl?(0,P.jsx)(`a`,{href:e.docsUrl,target:`_blank`,rel:`noreferrer`,className:`inline-block text-caption text-accent`,children:`Official documentation →`}):null,t?.settingsPath?(0,P.jsxs)(`div`,{className:`font-mono tabular-nums px-6 py-4 rounded-inset bg-surface-2 border border-inner-border text-badge text-text-secondary break-all`,children:[`Config file: `,t.settingsPath]}):null,t?.message?(0,P.jsxs)(`div`,{className:`flex items-center gap-3 text-badge ${Z?`text-green`:`text-orange`}`,children:[Z?(0,P.jsx)(a,{size:13}):(0,P.jsx)(le,{size:13}),t.message]}):null]})}),(0,P.jsxs)(g,{children:[(0,P.jsx)(h,{title:`Setup`,subtitle:`Pick the Apikey Name and endpoint, set the models, then download. The key's secret is read server-side — you never paste it.`,icon:(0,P.jsx)(de,{size:16})}),(0,P.jsx)(m,{children:(0,P.jsx)(x,{gap:`12px`,children:(0,P.jsxs)(`div`,{className:`grid grid-cols-2 gap-4 items-start min-w-0 max-medium:grid-cols-1`,children:[(0,P.jsxs)(`div`,{className:`flex flex-col gap-3 min-w-0`,children:[(0,P.jsx)(b,{label:`Apikey Name`,id:`api-key-select`,value:r,onValueChange:c,options:[{value:``,label:`Select…`},...z.map(e=>({value:e.id,label:`${e.label} — ${e.keyMode===`personal`?`Personal`:`Share template`}`}))]}),(0,P.jsx)(`div`,{className:`flex items-center gap-3 flex-wrap`,children:G===void 0?(0,P.jsx)(`span`,{className:`mt-1 text-badge text-text-tertiary`,children:`Choose the Apikey Name whose routes this CLI session should use.`}):K?(0,P.jsxs)(P.Fragment,{children:[(0,P.jsx)(v,{tone:`ok`,children:`routing:cli_mapping`}),(0,P.jsx)(`span`,{className:`mt-1 text-badge text-text-tertiary`,children:`Remote routes work with this key.`})]}):(0,P.jsxs)(P.Fragment,{children:[(0,P.jsx)(v,{tone:`warn`,title:`This key cannot resolve persisted CLI mappings on /v1/*. Turn on Remote Routing below to enable it.`,children:`no CLI scope`}),(0,P.jsx)(`span`,{className:`mt-1 text-badge text-text-tertiary`,children:`Remote routes stay inert on this key — turning on Remote Routing below will activate it.`})]})}),G?.keyMode===`share`?(0,P.jsx)(`span`,{className:`mt-1 text-badge text-text-tertiary`,children:`Share template: its child keys inherit these routes automatically.`}):null]}),(0,P.jsx)(`div`,{className:`min-w-0`,children:(0,P.jsx)(y,{label:`Endpoint`,value:l,onChange:e=>f(e.target.value)})})]})})})]}),(0,P.jsxs)(g,{children:[(0,P.jsx)(h,{title:`Model routing`,icon:(0,P.jsx)(te,{size:16}),subtitle:e.mappingSupported?`Left is the model name the CLI asks for. Right is the route Cartethyia sends it to. Leave a target empty to send that slot straight to the provider.`:`The model names written into this tool's config.`,action:e.mappingSupported?(0,P.jsxs)(`div`,{className:`flex items-center gap-5`,children:[(0,P.jsx)(re,{checked:T,disabled:!r,onChange:t=>{if(k(t),!r){s.error(`Select an API key first to configure its Remote Routing.`);return}if(t&&G&&!K){let e=[...new Set([...G.scopes??[],`routing:cli_mapping`])];U.mutate({keyId:G.id,request:{scopes:e}},{onSuccess:()=>{s.success(`Activated routing:cli_mapping on "${G.label}".`)},onError:e=>{s.error(d(e,`Could not update key scope.`))}})}let n=$();n&&V.mutate({toolId:e.id,keyId:r,input:{...n,enabled:t}},{onError:e=>s.error(d(e,`Could not save mappings.`))})},label:T?`Remote Routing on`:`Remote Routing off`}),(0,P.jsx)(o,{variant:`secondary`,size:`sm`,disabled:!r||H.isPending,onClick:()=>{if(!r){s.error(`Select an API key first.`);return}H.mutate({toolId:e.id,keyId:r},{onSuccess:()=>{k(!1),j({}),s.success(`Remote routes cleared and Remote Routing turned off.`)},onError:e=>s.error(d(e,`Could not reset remote routes.`))})},children:H.isPending?`Resetting…`:`Reset`})]}):void 0}),(0,P.jsxs)(m,{children:[r?null:(0,P.jsx)(`p`,{className:`text-badge text-text-tertiary mb-5`,children:`Select an API key above to load and save remote routes for that key. You can still set models below to download a config file.`}),e.mappingSupported?(0,P.jsxs)(`div`,{className:`mb-5 flex items-center gap-4 rounded-inset border border-inner-border bg-surface-2 px-5 py-3 text-micro text-text-tertiary`,children:[(0,P.jsx)(`span`,{className:`flex-1`,children:`CLI asks for`}),(0,P.jsx)(`span`,{className:`w-9 text-center`,children:`→`}),(0,P.jsx)(`span`,{className:`flex-1`,children:`Cartethyia routes to`})]}):null,(0,P.jsx)(x,{gap:`6px`,children:e.defaultModels.map(t=>{let n=C[t.alias]??t.defaultValue??t.id,r=A[t.alias]??``,i=r===n;return(0,P.jsxs)(`div`,{className:`grid items-center gap-4 ${e.mappingSupported?`grid-cols-2`:`grid-cols-1`}`,children:[(0,P.jsxs)(`div`,{className:`flex flex-col gap-1 min-w-0`,children:[(0,P.jsxs)(`button`,{type:`button`,onClick:()=>{F(t.alias),L(`slot`)},className:`font-mono tabular-nums flex w-full min-w-0 cursor-pointer items-center justify-between gap-4 rounded-inset border border-inner-border bg-surface-2 px-5 py-3.5 text-left text-badge text-text-primary`,children:[(0,P.jsx)(`span`,{className:`overflow-hidden truncate whitespace-nowrap`,children:n}),(0,P.jsx)(u,{size:12,className:`text-text-tertiary shrink-0`})]}),(0,P.jsx)(`span`,{className:`text-micro text-text-tertiary pl-1`,children:t.roleLabel??t.name})]}),e.mappingSupported?(0,P.jsxs)(`div`,{className:`flex flex-col gap-1 min-w-0`,children:[(0,P.jsxs)(`button`,{type:`button`,onClick:()=>{F(t.alias),L(`target`)},className:`font-mono tabular-nums flex w-full min-w-0 cursor-pointer items-center justify-between gap-4 rounded-inset border px-5 py-3.5 text-left text-badge ${i?`border-inner-border bg-surface-2 text-text-tertiary`:`border-accent bg-accent-soft text-text-primary`}`,children:[(0,P.jsx)(`span`,{className:`overflow-hidden truncate whitespace-nowrap`,children:r.length>0?r:`Not routed`}),(0,P.jsx)(u,{size:12,className:`text-text-tertiary shrink-0`})]}),(0,P.jsxs)(`span`,{className:`pl-1 text-micro ${r.length===0?`text-text-tertiary`:`text-accent`}`,children:[r.length===0?`not routed`:`rerouted`,r.length>0?(0,P.jsx)(`button`,{type:`button`,onClick:()=>j(e=>({...e,[t.alias]:``})),className:`ml-3 border-none bg-none p-0 text-micro text-text-tertiary cursor-pointer underline`,children:`clear`}):null]}),fe.has(t.alias)?(0,P.jsxs)(`span`,{className:`mt-1 flex items-center gap-3 text-micro text-orange`,children:[(0,P.jsx)(`span`,{children:J===`blacklist`?`This key blocks the target model.`:`This key does not allow the target model.`}),(0,P.jsx)(`button`,{type:`button`,disabled:U.isPending,onClick:()=>pe(r),className:`border-none bg-none p-0 text-micro font-semibold text-accent underline ${U.isPending?`cursor-not-allowed`:`cursor-pointer`}`,children:J===`blacklist`?`Unblock`:`Allow this model`})]}):null]}):null]},t.alias)})})]})]}),he&&e.guideSteps?(0,P.jsxs)(g,{children:[(0,P.jsx)(h,{title:`Guide`,subtitle:`Manual setup steps for tools that do not support file injection.`,icon:(0,P.jsx)(ce,{size:16})}),(0,P.jsx)(m,{children:(0,P.jsx)(`ol`,{className:`flex flex-col gap-6 pl-9`,children:e.guideSteps.map(e=>(0,P.jsxs)(`li`,{children:[(0,P.jsx)(`strong`,{className:`text-body`,children:e.title}),e.desc?(0,P.jsx)(`p`,{className:`text-caption text-text-secondary mt-2`,children:e.desc}):null,e.value?(0,P.jsx)(`pre`,{className:`mt-3 overflow-x-auto rounded-control border border-inner-border bg-surface-2 p-5 font-mono text-badge`,children:e.value.replace(/\{\{baseUrl\}\}/g,l)}):null]},e.step))})})]}):null,(0,P.jsxs)(g,{children:[(0,P.jsx)(h,{title:`Config file`,subtitle:`Download the config file for this CLI tool. The selected API key secret is decrypted automatically into the download.`,icon:(0,P.jsx)(ue,{size:16})}),(0,P.jsxs)(m,{className:`flex flex-col gap-6`,children:[(0,P.jsx)(`div`,{className:`flex gap-4 flex-wrap justify-end`,children:(0,P.jsx)(o,{variant:`primary`,icon:(0,P.jsx)(ee,{size:14}),onClick:()=>{if(!r){s.error(`Select an API key first.`);return}W.mutate({toolId:e.id,keyId:r,input:ge()},{onSuccess:e=>{_(e.filename,e.content,e.mimeType),s.success(`Downloaded ${e.filename}.`)},onError:e=>s.error(d(e,`Could not download config.`))})},disabled:W.isPending||!q,children:W.isPending?`Downloading…`:`Download config`})}),q?null:(0,P.jsx)(`p`,{className:`text-badge text-text-tertiary text-right`,children:G?.keyMode===`share`?`A share template has no secret to write into a config. Select a personal key to download the config file.`:`Select an Apikey Name above to enable downloading the config file.`})]})]}),(0,P.jsx)(ne,{open:M!==null,onClose:()=>F(null),selected:M?[I===`target`?A[M]??C[M]??``:C[M]??``]:[],onToggle:()=>{},onSelectOne:e=>{M&&(I===`target`?j(t=>({...t,[M]:e})):w(t=>({...t,[M]:e}))),F(null)},title:M?`Select ${M} model`:`Select model`,multi:!1})]})}export{F as default};
|
|
Binary file
|
|
Binary file
|