cartethyia 2.4.6 → 2.5.0

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.
Files changed (302) hide show
  1. package/README.md +45 -13
  2. package/dist/dashboard/assets/BannedPage-9igfctfZ.js +1 -0
  3. package/dist/dashboard/assets/BannedPage-9igfctfZ.js.gz +0 -0
  4. package/dist/dashboard/assets/{CliToolDetailPage-Bcy150Jo.js → CliToolDetailPage-ZRr2K2sF.js} +1 -1
  5. package/dist/dashboard/assets/CliToolDetailPage-ZRr2K2sF.js.gz +0 -0
  6. package/dist/dashboard/assets/{CliToolsPage-DyNXefph.js → CliToolsPage-D3xqZbN-.js} +1 -1
  7. package/dist/dashboard/assets/CliToolsPage-D3xqZbN-.js.gz +0 -0
  8. package/dist/dashboard/assets/CombosPage-CVnQPBBz.js +1 -0
  9. package/dist/dashboard/assets/CombosPage-CVnQPBBz.js.gz +0 -0
  10. package/dist/dashboard/assets/{ConsoleLogPage-CATpAEOz.js → ConsoleLogPage-u6W1UXSc.js} +1 -1
  11. package/dist/dashboard/assets/ConsoleLogPage-u6W1UXSc.js.gz +0 -0
  12. package/dist/dashboard/assets/{CustomizationPage-D6jEU7nL.js → CustomizationPage-CTTpCb25.js} +1 -1
  13. package/dist/dashboard/assets/CustomizationPage-CTTpCb25.js.gz +0 -0
  14. package/dist/dashboard/assets/{HealthEventsModal-CZkpn0a7.js → HealthEventsModal-C5XpWlvy.js} +1 -1
  15. package/dist/dashboard/assets/HealthEventsModal-C5XpWlvy.js.gz +0 -0
  16. package/dist/dashboard/assets/{LoginPage-BJlMVN4U.js → LoginPage-DHoB3Agn.js} +1 -1
  17. package/dist/dashboard/assets/LoginPage-DHoB3Agn.js.gz +0 -0
  18. package/dist/dashboard/assets/{ModelPicker-Br3CXEau.js → ModelPicker-oQ-SDXp8.js} +1 -1
  19. package/dist/dashboard/assets/ModelPicker-oQ-SDXp8.js.gz +0 -0
  20. package/dist/dashboard/assets/OverviewPage-BrY4SgIt.js +1 -0
  21. package/dist/dashboard/assets/OverviewPage-BrY4SgIt.js.gz +0 -0
  22. package/dist/dashboard/assets/ProviderDetailPage-D9VSt7T_.js +3 -0
  23. package/dist/dashboard/assets/ProviderDetailPage-D9VSt7T_.js.gz +0 -0
  24. package/dist/dashboard/assets/ProviderIcon-CmfzxpVM.js +1 -0
  25. package/dist/dashboard/assets/ProviderIcon-CmfzxpVM.js.gz +0 -0
  26. package/dist/dashboard/assets/ProvidersPage-Df77V5I5.js +1 -0
  27. package/dist/dashboard/assets/ProvidersPage-Df77V5I5.js.gz +0 -0
  28. package/dist/dashboard/assets/ProxyPage-CDfxP7AC.js +7 -0
  29. package/dist/dashboard/assets/ProxyPage-CDfxP7AC.js.gz +0 -0
  30. package/dist/dashboard/assets/QuotaPage-CY4o2t4p.js +1 -0
  31. package/dist/dashboard/assets/QuotaPage-CY4o2t4p.js.gz +0 -0
  32. package/dist/dashboard/assets/{SettingsPage-BHW4Zt06.js → SettingsPage-CvjZxHE4.js} +1 -1
  33. package/dist/dashboard/assets/SettingsPage-CvjZxHE4.js.gz +0 -0
  34. package/dist/dashboard/assets/{SetupPage-VOYC_lIa.js → SetupPage-D3nza7LH.js} +1 -1
  35. package/dist/dashboard/assets/SetupPage-D3nza7LH.js.gz +0 -0
  36. package/dist/dashboard/assets/SortableList-Dtt12WT1.js +1 -0
  37. package/dist/dashboard/assets/SortableList-Dtt12WT1.js.gz +0 -0
  38. package/dist/dashboard/assets/{StudioPage-BNIDLiU6.js → StudioPage-CbK3hXvU.js} +4 -4
  39. package/dist/dashboard/assets/StudioPage-CbK3hXvU.js.gz +0 -0
  40. package/dist/dashboard/assets/UsagePage-BQlWbWg2.js +56 -0
  41. package/dist/dashboard/assets/UsagePage-BQlWbWg2.js.gz +0 -0
  42. package/dist/dashboard/assets/{api-keys-BETfaMYZ.js → api-keys-BmDbsior.js} +1 -1
  43. package/dist/dashboard/assets/api-keys-BmDbsior.js.gz +0 -0
  44. package/dist/dashboard/assets/app-BbpSarVV.css +2 -0
  45. package/dist/dashboard/assets/app-BbpSarVV.css.gz +0 -0
  46. package/dist/dashboard/assets/assert-BHfaei-X.js +1 -0
  47. package/dist/dashboard/assets/assert-BHfaei-X.js.gz +0 -0
  48. package/dist/dashboard/assets/{badge-BTyGk_DL.js → badge-DpXT4Ixt.js} +1 -1
  49. package/dist/dashboard/assets/badge-DpXT4Ixt.js.gz +0 -0
  50. package/dist/dashboard/assets/{banner-bujTWYM4.js → banner-CSAAWLJv.js} +1 -1
  51. package/dist/dashboard/assets/banner-CSAAWLJv.js.gz +0 -0
  52. package/dist/dashboard/assets/{card-B0Bnx5CA.js → card-DEhRXXw4.js} +1 -1
  53. package/dist/dashboard/assets/card-DEhRXXw4.js.gz +0 -0
  54. package/dist/dashboard/assets/{cli-tools-DE2ug812.js → cli-tools-B0wBq-RL.js} +1 -1
  55. package/dist/dashboard/assets/cli-tools-B0wBq-RL.js.gz +0 -0
  56. package/dist/dashboard/assets/{clipboard-button-R_rArH8S.js → clipboard-button-DOo6EU8O.js} +1 -1
  57. package/dist/dashboard/assets/clipboard-button-DOo6EU8O.js.gz +0 -0
  58. package/dist/dashboard/assets/{dialog-BWIP_EvW.js → dialog-BvQeBMfP.js} +1 -1
  59. package/dist/dashboard/assets/dialog-BvQeBMfP.js.gz +0 -0
  60. package/dist/dashboard/assets/{drawer-BKcgVugF.js → drawer-CMwPXwQB.js} +1 -1
  61. package/dist/dashboard/assets/drawer-CMwPXwQB.js.gz +0 -0
  62. package/dist/dashboard/assets/{entry-CcWkGR4a.js → entry-D4O2229X.js} +1 -1
  63. package/dist/dashboard/assets/{entry-CcWkGR4a.js.gz → entry-D4O2229X.js.gz} +0 -0
  64. package/dist/dashboard/assets/{entry-kTiTFrvh.js → entry-DKvr5Q9K.js} +1 -1
  65. package/dist/dashboard/assets/entry-DKvr5Q9K.js.gz +0 -0
  66. package/dist/dashboard/assets/entry-FQCejoDY.js +4 -0
  67. package/dist/dashboard/assets/entry-FQCejoDY.js.gz +0 -0
  68. package/dist/dashboard/assets/{github-badge-Booc5mNe.js → github-badge-zliLvBwr.js} +1 -1
  69. package/dist/dashboard/assets/github-badge-zliLvBwr.js.gz +0 -0
  70. package/dist/dashboard/assets/{index-D70pChfn.js → index-CXm3FIyt.js} +2 -2
  71. package/dist/dashboard/assets/index-CXm3FIyt.js.gz +0 -0
  72. package/dist/dashboard/assets/{inline-D1TCqM2D.js → inline-D1IlZZ8H.js} +1 -1
  73. package/dist/dashboard/assets/inline-D1IlZZ8H.js.gz +0 -0
  74. package/dist/dashboard/assets/{input-B9oyU93y.js → input-BpVZdrbc.js} +1 -1
  75. package/dist/dashboard/assets/input-BpVZdrbc.js.gz +0 -0
  76. package/dist/dashboard/assets/{layout-BArzXirQ.js → layout-BioLW7gN.js} +1 -1
  77. package/dist/dashboard/assets/layout-BioLW7gN.js.gz +0 -0
  78. package/dist/dashboard/assets/{list-row-BVZzaoHF.js → list-row-CwVcGAIc.js} +1 -1
  79. package/dist/dashboard/assets/list-row-CwVcGAIc.js.gz +0 -0
  80. package/dist/dashboard/assets/{live-EmuIayqp.js → live-yfEJFF9T.js} +1 -1
  81. package/dist/dashboard/assets/live-yfEJFF9T.js.gz +0 -0
  82. package/dist/dashboard/assets/{model-bans-CSPQ9xB0.js → model-bans-CT-TxSNX.js} +1 -1
  83. package/dist/dashboard/assets/model-bans-CT-TxSNX.js.gz +0 -0
  84. package/dist/dashboard/assets/providers/cursor.webp +0 -0
  85. package/dist/dashboard/assets/providers/trae.webp +0 -0
  86. package/dist/dashboard/assets/{routing-BExgxxBU.js → routing-Cs6jl49L.js} +1 -1
  87. package/dist/dashboard/assets/routing-Cs6jl49L.js.gz +0 -0
  88. package/dist/dashboard/assets/select-C0Rer3gF.js +41 -0
  89. package/dist/dashboard/assets/select-C0Rer3gF.js.gz +0 -0
  90. package/dist/dashboard/assets/settings-CfcTcOyU.js +1 -0
  91. package/dist/dashboard/assets/settings-CfcTcOyU.js.gz +0 -0
  92. package/dist/dashboard/assets/{stack-Ca5HxI5i.js → stack-C9_uUjaA.js} +1 -1
  93. package/dist/dashboard/assets/stack-C9_uUjaA.js.gz +0 -0
  94. package/dist/dashboard/assets/{state-CzUPfLXo.js → state-DA0B95UQ.js} +1 -1
  95. package/dist/dashboard/assets/state-DA0B95UQ.js.gz +0 -0
  96. package/dist/dashboard/assets/{switch-DOUIky_i.js → switch-Cittxy-x.js} +1 -1
  97. package/dist/dashboard/assets/switch-Cittxy-x.js.gz +0 -0
  98. package/dist/dashboard/assets/{toast-C76WrMhc.js → toast-CNt_jzO4.js} +1 -1
  99. package/dist/dashboard/assets/toast-CNt_jzO4.js.gz +0 -0
  100. package/dist/dashboard/assets/{useMutation-D87FZwH0.js → useMutation-BQKMcJ1H.js} +1 -1
  101. package/dist/dashboard/assets/useMutation-BQKMcJ1H.js.gz +0 -0
  102. package/dist/dashboard/index.html +1 -1
  103. package/dist/dashboard/index.html.gz +0 -0
  104. package/dist/dashboard/precompressed-assets.json +51 -52
  105. package/migrations/full-postgres/0015_add_payload_stages.sql +18 -0
  106. package/migrations/full-postgres/0016_add_share_link_slug.sql +20 -0
  107. package/migrations/full-postgres/0017_add_models_pro_only.sql +18 -0
  108. package/migrations/full-postgres/0018_add_accounts_quota_plan.sql +18 -0
  109. package/migrations/full-postgres/0019_add_models_free_tier.sql +15 -0
  110. package/migrations/lite-sqlite3/0015_add_payload_stages.sql +18 -0
  111. package/migrations/lite-sqlite3/0016_add_share_link_slug.sql +20 -0
  112. package/migrations/lite-sqlite3/0017_add_models_pro_only.sql +18 -0
  113. package/migrations/lite-sqlite3/0018_add_accounts_quota_plan.sql +18 -0
  114. package/migrations/lite-sqlite3/0019_add_models_free_tier.sql +15 -0
  115. package/package.json +3 -2
  116. package/scripts/cli/command/console-menu.ts +30 -11
  117. package/scripts/cli/command/start.ts +77 -13
  118. package/scripts/cli/command/update.ts +1 -1
  119. package/scripts/cli/gateway/instance-lock.ts +105 -0
  120. package/scripts/cli/gateway/spawn.ts +173 -101
  121. package/scripts/cli/ui/background.ts +110 -49
  122. package/src/boot/app.ts +20 -6
  123. package/src/boot/dependencies.ts +19 -0
  124. package/src/boot/elysia-typebox-bridge.ts +37 -0
  125. package/src/boot/lifecycle.ts +2 -0
  126. package/src/config/cooldowns.ts +138 -138
  127. package/src/config/providers.ts +5 -5
  128. package/src/config/runtime.ts +19 -3
  129. package/src/config/spec.ts +9 -0
  130. package/src/console/api-keys/contracts.ts +414 -391
  131. package/src/console/api-keys/routes.ts +880 -870
  132. package/src/console/auth/account-reset-store.ts +14 -0
  133. package/src/console/auth/account-reset.ts +3 -2
  134. package/src/console/auth/setup-service.ts +6 -3
  135. package/src/console/domain-registration.ts +9 -2
  136. package/src/console/observability/contracts.ts +475 -455
  137. package/src/console/observability/store.ts +1015 -952
  138. package/src/console/providers/catalog/contracts.ts +31 -0
  139. package/src/console/providers/catalog/provider-operations.ts +8 -0
  140. package/src/console/providers/catalog/store.ts +1532 -1525
  141. package/src/console/providers/oauth/callback-listener.ts +18 -3
  142. package/src/console/providers/oauth/routes.ts +38 -15
  143. package/src/console/quota/refresh.ts +19 -0
  144. package/src/console/updates/routes.ts +162 -4
  145. package/src/main.ts +3 -0
  146. package/src/network/http2-bidi.ts +85 -0
  147. package/src/network/outbound-fetch.ts +1 -26
  148. package/src/network/pool/agent.ts +478 -460
  149. package/src/network/pool/resolver.ts +1104 -544
  150. package/src/network/pool-health-machine.ts +12 -5
  151. package/src/observability/payload-capture.ts +344 -324
  152. package/src/persistence/db-pg.ts +9 -3
  153. package/src/persistence/db-sqlite.ts +497 -495
  154. package/src/persistence/health-event-flush.ts +137 -0
  155. package/src/persistence/migrate.ts +181 -129
  156. package/src/persistence/schema.ts +1360 -1295
  157. package/src/persistence/settings-revision.ts +25 -0
  158. package/src/persistence/shared-cache.ts +217 -0
  159. package/src/persistence/stores/share-store.ts +556 -528
  160. package/src/persistence/usage-flush.ts +61 -1
  161. package/src/protocol/canonical-model.ts +23 -1
  162. package/src/protocol/response/chat.ts +143 -10
  163. package/src/protocol/response/omp-tool-call-markup.ts +221 -0
  164. package/src/providers/default-registry.ts +173 -1
  165. package/src/providers/discovery/openai-model-discovery.ts +39 -3
  166. package/src/providers/discovery/openrouter-free-roster.ts +98 -0
  167. package/src/providers/discovery/probing-service.ts +1022 -1001
  168. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +39 -0
  169. package/src/providers/integrations/buddy/buddy-chat-shared.ts +913 -914
  170. package/src/providers/integrations/buddy/buddy-conversation-headers.ts +18 -5
  171. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +4 -0
  172. package/src/providers/integrations/buddy/codebuddy/provider.ts +180 -175
  173. package/src/providers/integrations/buddy/codebuddy/shared.ts +15 -6
  174. package/src/providers/integrations/buddy/workbuddy/provider.ts +206 -204
  175. package/src/providers/integrations/buddy/workbuddy/shared.ts +3 -3
  176. package/src/providers/integrations/claude/claude.ts +606 -582
  177. package/src/providers/integrations/cline/cline-quota.ts +18 -16
  178. package/src/providers/integrations/cline/cline.ts +539 -468
  179. package/src/providers/integrations/commandcode/commandcode.ts +675 -541
  180. package/src/providers/integrations/commandcode/discovery.ts +116 -0
  181. package/src/providers/integrations/commandcode/quota.ts +160 -0
  182. package/src/providers/integrations/cursor/cursor-auth.ts +418 -0
  183. package/src/providers/integrations/cursor/cursor-catalog.ts +368 -0
  184. package/src/providers/integrations/cursor/cursor-http2.ts +300 -0
  185. package/src/providers/integrations/cursor/cursor-identity.ts +92 -0
  186. package/src/providers/integrations/cursor/cursor-request.ts +471 -0
  187. package/src/providers/integrations/cursor/cursor-session-manager.ts +281 -0
  188. package/src/providers/integrations/cursor/cursor-shared.ts +108 -0
  189. package/src/providers/integrations/cursor/cursor.ts +960 -0
  190. package/src/providers/integrations/cursor/protocol/framing.ts +129 -0
  191. package/src/providers/integrations/cursor/protocol/messages.ts +2355 -0
  192. package/src/providers/integrations/cursor/protocol/struct.ts +305 -0
  193. package/src/providers/integrations/github/github.ts +4 -4
  194. package/src/providers/integrations/hermes.ts +79 -8
  195. package/src/providers/integrations/kilo/kilo-discovery.ts +10 -1
  196. package/src/providers/integrations/kilo/kilo.ts +153 -146
  197. package/src/providers/integrations/opencode/provider.ts +532 -538
  198. package/src/providers/integrations/openrouter/provider.ts +139 -281
  199. package/src/providers/integrations/qoder/qoder-catalog.ts +450 -0
  200. package/src/providers/integrations/qoder/qoder-oauth.ts +310 -257
  201. package/src/providers/integrations/qoder/qoder-realm.ts +106 -0
  202. package/src/providers/integrations/qoder/qoder-virtual.ts +77 -0
  203. package/src/providers/integrations/qoder/qoder.ts +865 -730
  204. package/src/providers/integrations/trae/trae-catalog.ts +190 -0
  205. package/src/providers/integrations/trae/trae-headers.ts +149 -0
  206. package/src/providers/integrations/trae/trae-oauth.ts +773 -0
  207. package/src/providers/integrations/trae/trae-protocol.ts +400 -0
  208. package/src/providers/integrations/trae/trae-realm.ts +220 -0
  209. package/src/providers/integrations/trae/trae.ts +458 -0
  210. package/src/providers/operations/account-health-service.ts +107 -10
  211. package/src/providers/operations/client-versions.ts +133 -6
  212. package/src/providers/operations/provider-catalog-seeder.ts +95 -86
  213. package/src/providers/operations/provider-credential-service.ts +46 -7
  214. package/src/providers/provider-contracts.ts +76 -0
  215. package/src/providers/provider-metadata.ts +78 -6
  216. package/src/providers/provider-model-definition.ts +373 -349
  217. package/src/security/api-key-auth.ts +126 -8
  218. package/src/transport/contracts.ts +3 -0
  219. package/src/transport/dispatch/attempt-finalize.ts +32 -1
  220. package/src/transport/dispatch/attempt-loop.ts +530 -515
  221. package/src/transport/dispatch/generation.ts +88 -21
  222. package/src/transport/dispatch/leases.ts +99 -2
  223. package/src/transport/dispatch/proxy-request.ts +60 -3
  224. package/src/transport/dispatch/streaming/finalize.ts +16 -1
  225. package/src/transport/dispatch/streaming/stream.ts +22 -2
  226. package/src/transport/dispatch/upstream.ts +19 -1
  227. package/src/transport/guards/auth-guard.ts +26 -1
  228. package/src/transport/guards/body-policy.ts +5 -4
  229. package/src/transport/request/preparer.ts +103 -0
  230. package/src/transport/request/state.ts +14 -0
  231. package/src/transport/routing/plan-gate.ts +45 -0
  232. package/src/transport/routing/route-catalog.ts +799 -777
  233. package/src/transport/routing/route-model.ts +52 -1
  234. package/src/transport/routing/router.ts +51 -0
  235. package/dist/dashboard/assets/BannedPage-QhvbJB9o.js +0 -1
  236. package/dist/dashboard/assets/BannedPage-QhvbJB9o.js.gz +0 -0
  237. package/dist/dashboard/assets/CliToolDetailPage-Bcy150Jo.js.gz +0 -0
  238. package/dist/dashboard/assets/CliToolsPage-DyNXefph.js.gz +0 -0
  239. package/dist/dashboard/assets/CombosPage-Cak0ljnO.js +0 -1
  240. package/dist/dashboard/assets/CombosPage-Cak0ljnO.js.gz +0 -0
  241. package/dist/dashboard/assets/ConsoleLogPage-CATpAEOz.js.gz +0 -0
  242. package/dist/dashboard/assets/CustomizationPage-D6jEU7nL.js.gz +0 -0
  243. package/dist/dashboard/assets/HealthEventsModal-CZkpn0a7.js.gz +0 -0
  244. package/dist/dashboard/assets/LoginPage-BJlMVN4U.js.gz +0 -0
  245. package/dist/dashboard/assets/ModelPicker-Br3CXEau.js.gz +0 -0
  246. package/dist/dashboard/assets/OverviewPage-CQxXbo2g.js +0 -1
  247. package/dist/dashboard/assets/OverviewPage-CQxXbo2g.js.gz +0 -0
  248. package/dist/dashboard/assets/ProviderDetailPage--0RBNh99.js +0 -3
  249. package/dist/dashboard/assets/ProviderDetailPage--0RBNh99.js.gz +0 -0
  250. package/dist/dashboard/assets/ProviderIcon-paOCq8I0.js +0 -1
  251. package/dist/dashboard/assets/ProviderIcon-paOCq8I0.js.gz +0 -0
  252. package/dist/dashboard/assets/ProvidersPage-1M-C3mbu.js +0 -1
  253. package/dist/dashboard/assets/ProvidersPage-1M-C3mbu.js.gz +0 -0
  254. package/dist/dashboard/assets/ProxyPage-CXiumA53.js +0 -7
  255. package/dist/dashboard/assets/ProxyPage-CXiumA53.js.gz +0 -0
  256. package/dist/dashboard/assets/QuotaPage-BGDIfIYP.js +0 -1
  257. package/dist/dashboard/assets/QuotaPage-BGDIfIYP.js.gz +0 -0
  258. package/dist/dashboard/assets/SettingsPage-BHW4Zt06.js.gz +0 -0
  259. package/dist/dashboard/assets/SetupPage-VOYC_lIa.js.gz +0 -0
  260. package/dist/dashboard/assets/SortableList-DpOTnGuH.js +0 -1
  261. package/dist/dashboard/assets/SortableList-DpOTnGuH.js.gz +0 -0
  262. package/dist/dashboard/assets/StudioPage-BNIDLiU6.js.gz +0 -0
  263. package/dist/dashboard/assets/UsagePage-BAFaSDQV.js +0 -56
  264. package/dist/dashboard/assets/UsagePage-BAFaSDQV.js.gz +0 -0
  265. package/dist/dashboard/assets/api-keys-BETfaMYZ.js.gz +0 -0
  266. package/dist/dashboard/assets/app-BLCvGdzb.css +0 -2
  267. package/dist/dashboard/assets/app-BLCvGdzb.css.gz +0 -0
  268. package/dist/dashboard/assets/assert-7J6_LVD3.js +0 -1
  269. package/dist/dashboard/assets/assert-7J6_LVD3.js.gz +0 -0
  270. package/dist/dashboard/assets/badge-BTyGk_DL.js.gz +0 -0
  271. package/dist/dashboard/assets/banner-bujTWYM4.js.gz +0 -0
  272. package/dist/dashboard/assets/card-B0Bnx5CA.js.gz +0 -0
  273. package/dist/dashboard/assets/cli-tools-DE2ug812.js.gz +0 -0
  274. package/dist/dashboard/assets/clipboard-button-R_rArH8S.js.gz +0 -0
  275. package/dist/dashboard/assets/dialog-BWIP_EvW.js.gz +0 -0
  276. package/dist/dashboard/assets/drawer-BKcgVugF.js.gz +0 -0
  277. package/dist/dashboard/assets/entry-7dpgvpJ3.js +0 -4
  278. package/dist/dashboard/assets/entry-7dpgvpJ3.js.gz +0 -0
  279. package/dist/dashboard/assets/entry-kTiTFrvh.js.gz +0 -0
  280. package/dist/dashboard/assets/github-badge-Booc5mNe.js.gz +0 -0
  281. package/dist/dashboard/assets/index-D70pChfn.js.gz +0 -0
  282. package/dist/dashboard/assets/inline-D1TCqM2D.js.gz +0 -0
  283. package/dist/dashboard/assets/input-B9oyU93y.js.gz +0 -0
  284. package/dist/dashboard/assets/layout-BArzXirQ.js.gz +0 -0
  285. package/dist/dashboard/assets/list-row-BVZzaoHF.js.gz +0 -0
  286. package/dist/dashboard/assets/live-EmuIayqp.js.gz +0 -0
  287. package/dist/dashboard/assets/model-bans-CSPQ9xB0.js.gz +0 -0
  288. package/dist/dashboard/assets/routing-BExgxxBU.js.gz +0 -0
  289. package/dist/dashboard/assets/select-C9mMoEpJ.js +0 -41
  290. package/dist/dashboard/assets/select-C9mMoEpJ.js.gz +0 -0
  291. package/dist/dashboard/assets/settings-C4_Gn_wq.js +0 -1
  292. package/dist/dashboard/assets/settings-C4_Gn_wq.js.gz +0 -0
  293. package/dist/dashboard/assets/sliders-horizontal-ClG7dBeJ.js +0 -1
  294. package/dist/dashboard/assets/sliders-horizontal-ClG7dBeJ.js.gz +0 -0
  295. package/dist/dashboard/assets/stack-Ca5HxI5i.js.gz +0 -0
  296. package/dist/dashboard/assets/state-CzUPfLXo.js.gz +0 -0
  297. package/dist/dashboard/assets/switch-DOUIky_i.js.gz +0 -0
  298. package/dist/dashboard/assets/toast-C76WrMhc.js.gz +0 -0
  299. package/dist/dashboard/assets/useMutation-D87FZwH0.js.gz +0 -0
  300. package/src/network/tls/bun-client-hello.ts +0 -112
  301. /package/dist/dashboard/assets/{app-Cjq7nrwu.js → app-C-Y4oZT0.js} +0 -0
  302. /package/dist/dashboard/assets/{app-Cjq7nrwu.js.gz → app-C-Y4oZT0.js.gz} +0 -0
@@ -63,6 +63,23 @@ export interface RouteCandidate {
63
63
  * Absent/`true` preserves the existing invariant: no account configured
64
64
  * means the route is unusable, not silently public. */
65
65
  readonly requires_account?: boolean;
66
+ /**
67
+ * True when the upstream refuses this model on a free plan.
68
+ *
69
+ * Three-valued by absence as well as by value: only `true` excludes a free
70
+ * account. `false` and absent both mean the model is not known to be gated,
71
+ * and routing must not turn "not known" into "free accounts may serve it" —
72
+ * that would send pro-only traffic to an account that is certain to be
73
+ * refused.
74
+ */
75
+ readonly pro_only?: boolean;
76
+ /**
77
+ * The plan the provider last reported for `provider_account_id`.
78
+ *
79
+ * Absent means the plan was never read, which is a different state from
80
+ * "free": guessing free here would defeat the gate it exists to serve.
81
+ */
82
+ readonly account_plan?: string | null;
66
83
  }
67
84
 
68
85
  /**
@@ -133,6 +150,21 @@ export interface PoolRoutingSetting {
133
150
  readonly rotateCount: number;
134
151
  }
135
152
 
153
+ /**
154
+ * Exact account a request must be routed to, because a provider session is
155
+ * already live on it.
156
+ *
157
+ * A tool result for a Cursor Run is only meaningful on the account whose Run
158
+ * asked for the call. Routing it to a sibling account — even a healthy one —
159
+ * writes the result into an unrelated upstream turn, so affinity is a hard
160
+ * constraint on the candidate set rather than a preference: `plan` pins to this
161
+ * account or refuses.
162
+ */
163
+ export interface RouteContinuationAffinity {
164
+ readonly providerId: string;
165
+ readonly providerAccountId: string;
166
+ }
167
+
136
168
  export interface RoutePlan {
137
169
  readonly revision: RoutingRevision;
138
170
  readonly candidates: readonly RouteCandidate[];
@@ -367,6 +399,8 @@ export function accountsRateLimitedError(
367
399
 
368
400
 
369
401
 
402
+ import { publishRoutingInvalidation, subscribeRoutingInvalidation } from "../../persistence/shared-cache";
403
+
370
404
  export type SnapshotBuilder = () => Promise<
371
405
  Omit<RouteSnapshot, "revision" | "created_at"> & { created_at?: number }
372
406
  >;
@@ -398,7 +432,17 @@ export class InMemoryRouteSnapshotService implements RouteSnapshotService {
398
432
  */
399
433
  private building: { revision: RoutingRevision; promise: Promise<RouteSnapshot> } | undefined;
400
434
 
401
- constructor(private readonly builder: SnapshotBuilder) {}
435
+ constructor(private readonly builder: SnapshotBuilder) {
436
+ // Peers' mutations arrive once per process, not per instance: the handler
437
+ // drops this instance's copy, which is the only one there is.
438
+ this.unsubscribe = subscribeRoutingInvalidation(() => {
439
+ this.revision += 1;
440
+ this.snapshot = undefined;
441
+ });
442
+ }
443
+
444
+ /** Ends the peer-invalidation subscription. */
445
+ readonly unsubscribe: () => void;
402
446
 
403
447
  async getSnapshot(): Promise<RouteSnapshot> {
404
448
  if (this.snapshot) return this.snapshot;
@@ -422,6 +466,13 @@ export class InMemoryRouteSnapshotService implements RouteSnapshotService {
422
466
  // Lazy: the next getSnapshot() rebuilds. Dashboard mutations only need
423
467
  // the new revision token — they must not pay seven Postgres round-trips
424
468
  // inside the mutating request while holding no pool pressure budget.
469
+ //
470
+ // Broadcast, because `reusePort` runs several gateway processes and each
471
+ // holds its own snapshot: a console write handled by one of them left the
472
+ // others serving pre-write routing until something else invalidated them.
473
+ // Peers drop their copy on receipt and rebuild lazily, same as here — the
474
+ // notice carries no data, so there is nothing to deserialize.
475
+ publishRoutingInvalidation();
425
476
  return this.revision;
426
477
  }
427
478
 
@@ -1,5 +1,6 @@
1
1
  // Routing model resolution and route planning.
2
2
  import { log } from "../../observability/logger";
3
+ import { GatewayError } from "../../protocol/gateway-error";
3
4
  import {
4
5
  accountsRateLimitedError,
5
6
  accountsUnavailableError,
@@ -14,12 +15,14 @@ import {
14
15
  type RouteCandidate,
15
16
  type RoutePlan,
16
17
  type EligibilityDecision,
18
+ type RouteContinuationAffinity,
17
19
  type RouteSnapshot,
18
20
  } from "./route-model";
19
21
  import {
20
22
  candidateSupportsRequest,
21
23
  type RequiredCapability,
22
24
  } from "../translation/capabilities";
25
+ import { planAllowsCandidate } from "./plan-gate";
23
26
  import { InMemoryAdmissionController, admissionKey } from "./admission";
24
27
  import { ReservationManager } from "./reservations";
25
28
  import { getOrCreateRoundRobin, RoundRobinState } from "./round-robin";
@@ -525,6 +528,7 @@ export class RoutingEngine {
525
528
  requiredCapabilities?: readonly RequiredCapability[],
526
529
  allowCliMappings = false,
527
530
  keyId?: string,
531
+ continuationAffinity?: RouteContinuationAffinity,
528
532
  ): Promise<RoutePlan> {
529
533
  const tid = tenantId ?? null;
530
534
  const { resolved, matching, unmatchedMembers } = this.resolveMatchingCandidates(
@@ -534,6 +538,41 @@ export class RoutingEngine {
534
538
  allowCliMappings,
535
539
  keyId,
536
540
  );
541
+ // Continuation affinity is applied first and absolutely.
542
+ //
543
+ // A request carrying a tool result for a provider session this process is
544
+ // holding MUST reach the exact live account that owns that session. Routing
545
+ // it anywhere else is not a degraded choice — it is a correctness failure:
546
+ // the result would be written onto a different account's upstream turn
547
+ // (corrupting both conversations and leaking the first account's context),
548
+ // or into a fresh turn that never asked for the call. So the candidate set
549
+ // is restricted before any health, cooling, or rotation logic runs, and an
550
+ // empty result is a refusal rather than a fallback to normal routing.
551
+ if (continuationAffinity !== undefined) {
552
+ const pinned = matching.filter(
553
+ (candidate) =>
554
+ candidate.provider_id === continuationAffinity.providerId &&
555
+ candidate.provider_account_id === continuationAffinity.providerAccountId,
556
+ );
557
+ if (pinned.length === 0) {
558
+ throw new GatewayError(
559
+ "invalid_request",
560
+ 400,
561
+ "This tool result belongs to a Cursor account that is no longer routable. Reconnect the account, or resend the complete conversation.",
562
+ {
563
+ providerId: continuationAffinity.providerId,
564
+ accountId: continuationAffinity.providerAccountId,
565
+ },
566
+ );
567
+ }
568
+ return {
569
+ revision: snapshot.revision,
570
+ candidates: pinned,
571
+ requested_model: requestedModel,
572
+ resolved_model: resolved.model,
573
+ provider_id: pinned[0]!.provider_id,
574
+ };
575
+ }
537
576
  // `matching` non-empty but nothing eligible means every account serving
538
577
  // this model is currently unusable — NOT a missing model. Distinct from
539
578
  // the `matching.length === 0` throw in the resolver, which is a genuine 404.
@@ -667,6 +706,18 @@ export class RoutingEngine {
667
706
  if (eligible.length === 0)
668
707
  throw capabilityUnsupportedError(requiredCapabilities.join(", "));
669
708
  }
709
+ // Plan gate: a model the upstream refuses on a free plan is not offered to
710
+ // an account the upstream reports as free. That pair is a certain refusal,
711
+ // and sending it anyway advances the account's failure streak toward a
712
+ // six-hour park for something that is not the account's fault.
713
+ //
714
+ // Applied only when another candidate survives. Every account being free is
715
+ // not a routing failure — it is the operator having only free accounts, and
716
+ // the honest outcome is the upstream's own `11158`, which names the model
717
+ // and the plan. Dropping the last candidate would replace that clear refusal
718
+ // with a generic "no candidates" that names neither.
719
+ const planAllowed = eligible.filter((candidate) => planAllowsCandidate(candidate));
720
+ if (planAllowed.length > 0) eligible = planAllowed;
670
721
  eligible = this.applyProviderRouting(eligible, snapshot, tid);
671
722
  // Re-assert the cooling order: `applyProviderRouting` rotates accounts
672
723
  // within one `provider::model` run, and that rotation is by design blind to
@@ -1 +0,0 @@
1
- import{t as e}from"./jsx-runtime-CXUMngI_.js";import{$ as t}from"./entry-7dpgvpJ3.js";var n=e();function r(){return(0,n.jsx)(`main`,{className:`auth-viewport`,children:(0,n.jsxs)(`div`,{className:`card-solid auth-window border border-red-soft`,children:[(0,n.jsxs)(`div`,{className:`auth-header`,children:[(0,n.jsx)(`div`,{"aria-hidden":`true`,className:`auth-logo bg-red bg-none`,children:(0,n.jsx)(t,{size:24})}),(0,n.jsx)(`h1`,{className:`auth-title`,children:`Access Restricted`}),(0,n.jsx)(`p`,{className:`auth-desc`,children:`Security policy lockout or untrusted origin address`})]}),(0,n.jsxs)(`div`,{className:`rounded-panel border border-inner-border bg-surface-2 p-7 text-caption leading-relaxed text-text-secondary`,children:[(0,n.jsx)(`strong`,{className:`block text-text-primary mb-2`,children:`HTTP 403 Forbidden`}),`This client IP has been temporarily locked out due to repeated invalid credential attempts or untrusted proxy headers.`]})]})})}export{r as default};
@@ -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-right-Dl5yKnp8.js";import{t as a}from"./select-C9mMoEpJ.js";import{t as o}from"./SortableList-DpOTnGuH.js";import{a as s,p as c,r as l,t as u}from"./toast-C76WrMhc.js";import{t as d}from"./copy-ngmQ9zcZ.js";import{n as ee,r as te,t as ne}from"./state-CzUPfLXo.js";import{t as f}from"./layers-2-glBbjl.js";import{t as p}from"./dialog-BWIP_EvW.js";import{t as m}from"./pencil-768ysRAr.js";import{t as h}from"./plus-DIdX_g-x.js";import{t as g}from"./route-CNSNj9Zm.js";import{t as re}from"./search-JOKus9rb.js";import{t as _}from"./trash-Cppn5c3R.js";import{l as v}from"./query-keys-D7wX2vM5.js";import{t as y}from"./dialog-actions-B1axpEg7.js";import{n as ie,r as ae,t as b}from"./card-B0Bnx5CA.js";import{t as x}from"./use-clipboard-B0Sy03fE.js";import{n as S}from"./input-B9oyU93y.js";import{t as C}from"./ModelPicker-Br3CXEau.js";import{t as w}from"./stack-Ca5HxI5i.js";import{n as T,t as E}from"./inline-D1TCqM2D.js";import{t as D}from"./list-row-BVZzaoHF.js";import{a as O,d as k,f as A,i as j,n as M,o as N,p as P,r as F,s as I,t as oe,u as L}from"./routing-BExgxxBU.js";import{t as R}from"./use-timeout-BwcBBRrT.js";var z=e(`copy-plus`,[[`line`,{x1:`15`,x2:`15`,y1:`12`,y2:`18`,key:`1p7wdc`}],[`line`,{x1:`12`,x2:`18`,y1:`15`,y2:`15`,key:`1nscbv`}],[`rect`,{width:`14`,height:`14`,x:`8`,y:`8`,rx:`2`,ry:`2`,key:`17jyea`}],[`path`,{d:`M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2`,key:`zix9uf`}]]),B=t(n(),1),V=r(),H={plain:`code-chip-plain`,muted:`code-chip-muted text-text-secondary bg-surface-muted border border-inner-border px-4 py-1 rounded-chip`,accent:`code-chip-accent text-accent bg-accent-soft px-4 py-1.5 rounded-chip font-bold`};function U({value:e,tone:t=`muted`,action:n,className:r=``,children:i,...a}){return(0,V.jsxs)(`span`,{className:s(`code-chip inline-flex items-center gap-3 min-w-0 max-w-full font-mono text-caption tabular-nums`,H[t],r),...a,children:[(0,V.jsx)(`code`,{className:`code-chip-value min-w-0 overflow-hidden text-ellipsis whitespace-nowrap`,title:a.title??e,children:e}),n?(0,V.jsx)(`span`,{className:`code-chip-action inline-flex shrink-0`,children:n}):null,i]})}var W=[{value:`fallback`,label:`Fallback (try in order)`},{value:`round_robin`,label:`Round Robin (rotate)`}],G=()=>`m${Date.now().toString(36)}${Math.random().toString(36).slice(2,8)}`;function K(){let e=N(),t=M(),n=A(),r=j(),a=L(),[s,c]=(0,B.useState)(!1),[f,O]=(0,B.useState)(null),[k,P]=(0,B.useState)(``),[F,I]=(0,B.useState)(``),[oe,z]=(0,B.useState)(!1),[H,W]=(0,B.useState)(null),[G,K]=(0,B.useState)(null),{copy:q}=x(),J=R(),Y=(0,B.useMemo)(()=>e.data??[],[e.data]),X=()=>{O(null),P(``),I(``),c(!0)},Z=e=>{O(e),P(e.alias),I(e.targetModel),c(!0)},Q=e=>{e.preventDefault(),k.trim()&&F.trim()&&(f?n.mutate({id:f.id,request:{alias:k.trim(),targetModel:F.trim()}},{onSuccess:()=>{c(!1),O(null),u.success(`Alias updated`)},onError:e=>u.error(v(e,`Could not save the alias.`))}):t.mutate({alias:k.trim(),targetModel:F.trim()},{onSuccess:()=>{c(!1),P(``),I(``),u.success(`Alias created`)},onError:e=>u.error(v(e,`Could not create the alias.`))}))},se=(e,t)=>{q(e).then(e=>{if(!e){u.error(`Copy failed`);return}K(t),J(()=>K(null),1500)})};return(0,V.jsxs)(b,{children:[(0,V.jsx)(ae,{title:`Model Aliases`,subtitle:`Map readable names to real models (e.g. claude-mythos-5 → claude-opus-5)`,icon:(0,V.jsx)(g,{size:16}),action:(0,V.jsx)(E,{children:(0,V.jsx)(l,{variant:`primary`,size:`sm`,icon:(0,V.jsx)(h,{size:13}),onClick:X,children:`New Alias`})})}),(0,V.jsx)(ie,{children:e.isPending?(0,V.jsx)(te,{label:`Loading aliases...`}):e.isError?(0,V.jsx)(ee,{message:`Failed to load aliases`,onRetry:()=>e.refetch()}):Y.length===0?(0,V.jsx)(ne,{title:`No aliases defined`,message:`Create an alias to route short or friendly names to real provider model IDs.`}):(0,V.jsx)(o,{items:Y,label:`Model aliases`,gap:`8px`,disabled:a.isPending,onReorder:e=>a.mutate(e,{onError:e=>u.error(v(e,`Could not save the new order.`))}),renderItem:e=>(0,V.jsx)(D,{className:`items-start flex-col sm:flex-row`,actions:(0,V.jsxs)(T,{gap:`4px`,className:`w-full justify-start sm:w-auto sm:justify-end`,children:[(0,V.jsx)(l,{variant:`ghost`,size:`sm`,icon:(0,V.jsx)(d,{size:13}),onClick:()=>se(e.alias,e.id),title:G===e.id?`Copied!`:`Copy alias`}),(0,V.jsx)(l,{variant:`ghost`,size:`sm`,icon:(0,V.jsx)(m,{size:13}),onClick:()=>Z(e),title:`Edit target model`}),(0,V.jsx)(l,{variant:`ghost`,size:`sm`,icon:(0,V.jsx)(_,{size:13}),onClick:()=>W(e),title:`Delete alias`,className:`text-red`})]}),children:(0,V.jsxs)(`div`,{className:`flex items-center gap-4 flex-wrap min-w-0`,children:[(0,V.jsx)(U,{value:e.alias,tone:`accent`}),(0,V.jsx)(i,{size:13,className:`text-text-tertiary shrink-0`}),(0,V.jsx)(U,{value:e.targetModel,tone:`plain`})]})})})}),(0,V.jsx)(p,{open:s,onClose:()=>c(!1),title:f?`Edit Model Alias`:`New Model Alias`,footer:(0,V.jsxs)(V.Fragment,{children:[(0,V.jsx)(l,{variant:`secondary`,type:`button`,onClick:()=>c(!1),children:`Cancel`}),(0,V.jsx)(l,{variant:`primary`,type:`submit`,form:`alias-form`,disabled:t.isPending||n.isPending||!k.trim()||!F.trim(),children:f?`Save Changes`:`Create Alias`})]}),children:(0,V.jsxs)(`form`,{id:`alias-form`,onSubmit:Q,className:`flex flex-col gap-6`,children:[(0,V.jsx)(S,{label:`Alias name`,id:`alias-name-input`,value:k,onChange:e=>P(e.target.value),placeholder:`e.g. fast, sonnet, smart`,required:!0}),(0,V.jsxs)(T,{gap:`8px`,align:`flex-end`,children:[(0,V.jsx)(`div`,{className:`flex-1`,children:(0,V.jsx)(S,{label:`Target model`,id:`target-model-input`,value:F,onChange:e=>I(e.target.value),placeholder:`e.g. codex/gpt-5.5, claude/claude-sonnet-5`,required:!0})}),(0,V.jsx)(l,{variant:`secondary`,size:`sm`,type:`button`,onClick:()=>z(!0),icon:(0,V.jsx)(re,{size:13}),children:`Browse`})]}),(0,V.jsx)(C,{open:oe,onClose:()=>z(!1),selected:F?[F]:[],onToggle:()=>{},onSelectOne:e=>I(e),title:`Select target model`,multi:!1})]})}),(0,V.jsx)(p,{open:!!H,onClose:()=>W(null),title:`Delete Model Alias`,children:(0,V.jsxs)(w,{gap:`12px`,children:[(0,V.jsxs)(`p`,{className:`text-body text-text-secondary`,children:[`Are you sure you want to delete alias `,(0,V.jsx)(`strong`,{children:H?.alias}),`? Clients requesting this name will need their full model path.`]}),(0,V.jsxs)(y,{children:[(0,V.jsx)(l,{variant:`secondary`,onClick:()=>W(null),children:`Cancel`}),(0,V.jsx)(l,{variant:`danger`,onClick:()=>{H&&r.mutate(H.id,{onSuccess:()=>W(null)})},disabled:r.isPending,children:r.isPending?`Deleting...`:`Delete Alias`})]})]})})]})}function q(){let e=I(),t=F(),n=oe(),r=P(),i=O(),s=k(),[g,A]=(0,B.useState)(!1),[j,M]=(0,B.useState)(null),[N,L]=(0,B.useState)(``),[H,K]=(0,B.useState)([]),[q,J]=(0,B.useState)(`fallback`),[Y,X]=(0,B.useState)(!1),[Z,Q]=(0,B.useState)(null),[se,ce]=(0,B.useState)(null),{copy:le}=x(),ue=R(),$=(0,B.useMemo)(()=>H.map(e=>e.value),[H]),de=e=>{$.includes(e)?K(H.filter(t=>t.value!==e)):K([...H,{id:G(),value:e.trim()}])},fe=e.data??[],pe=()=>{M(null),L(``),K([]),J(`fallback`),A(!0)},me=e=>{M(e),L(e.name),K(e.members.map(e=>({id:G(),value:e}))),A(!0)},he=e=>{n.mutate(e.id,{onSuccess:e=>{let t=e.skippedMembers;t.length>0?u.success(`Combo cloned`,`${e.combo.name} — skipped unresolvable member${t.length>1?`s`:``}: ${t.join(`, `)}`):u.success(`Combo cloned`,e.combo.name)},onError:e=>u.error(v(e,`Could not clone the combo.`))})},ge=e=>{e.preventDefault(),N.trim()&&$.length!==0&&(j?r.mutate({id:j.id,request:{name:N.trim(),members:$,strategy:q}},{onSuccess:()=>{A(!1),M(null),u.success(`Combo saved`)},onError:e=>u.error(v(e,`Could not save the combo.`))}):t.mutate({name:N.trim(),members:$,strategy:q},{onSuccess:()=>{A(!1),L(``),K([]),u.success(`Combo created`)},onError:e=>u.error(v(e,`Could not create the combo.`))}))},_e=(e,t)=>{r.mutate({id:e.id,request:{strategy:t}},{onSuccess:()=>u.success(`Combo strategy updated`),onError:e=>u.error(v(e,`Could not update the combo strategy.`))})},ve=(e,t)=>{le(e).then(e=>{if(!e){u.error(`Copy failed`);return}ce(t),ue(()=>ce(null),1500)})};return(0,V.jsxs)(b,{children:[(0,V.jsx)(ae,{title:`Combos`,subtitle:`Combine multiple models with fallback or round-robin rotation`,icon:(0,V.jsx)(f,{size:16}),action:(0,V.jsx)(E,{children:(0,V.jsx)(l,{variant:`primary`,size:`sm`,icon:(0,V.jsx)(h,{size:13}),onClick:pe,children:`New Combo`})})}),(0,V.jsx)(ie,{children:e.isPending?(0,V.jsx)(te,{label:`Loading combos...`}):e.isError?(0,V.jsx)(ee,{message:`Failed to load combos`,onRetry:()=>e.refetch()}):fe.length===0?(0,V.jsx)(ne,{title:`No combos defined`,message:`Create a model combo to distribute requests across multiple models.`}):(0,V.jsx)(o,{items:fe,label:`Model combos`,disabled:s.isPending,onReorder:e=>s.mutate(e,{onError:e=>u.error(v(e,`Could not save the new order.`))}),renderItem:e=>(0,V.jsxs)(D,{className:`items-start flex-col sm:flex-row`,actions:(0,V.jsxs)(T,{gap:`8px`,className:`w-full justify-between sm:w-auto sm:justify-start`,children:[(0,V.jsx)(a,{value:e.strategy,onValueChange:t=>_e(e,t),options:W}),(0,V.jsxs)(T,{gap:`4px`,className:`flex-wrap`,children:[(0,V.jsx)(l,{variant:`ghost`,size:`sm`,icon:(0,V.jsx)(d,{size:13}),onClick:()=>ve(e.name,e.id),title:se===e.id?`Copied!`:`Copy combo name`}),(0,V.jsx)(l,{variant:`ghost`,size:`sm`,icon:(0,V.jsx)(z,{size:13}),onClick:()=>he(e),disabled:n.isPending,title:`Clone combo (same members and strategy)`}),(0,V.jsx)(l,{variant:`ghost`,size:`sm`,icon:(0,V.jsx)(m,{size:13}),onClick:()=>me(e),title:`Edit combo`}),(0,V.jsx)(l,{variant:`ghost`,size:`sm`,icon:(0,V.jsx)(_,{size:13}),onClick:()=>Q(e),title:`Delete combo`,className:`text-red`})]})]}),children:[(0,V.jsx)(`div`,{className:`flex items-center justify-between gap-6 flex-wrap`,children:(0,V.jsx)(`strong`,{className:`text-body`,children:e.name})}),(0,V.jsx)(T,{gap:`6px`,className:`w-full flex-wrap`,children:e.members.map(e=>(0,V.jsx)(U,{value:e},e))})]})})}),(0,V.jsx)(p,{open:g,onClose:()=>A(!1),title:j?`Edit Model Combo`:`New Model Combo`,footer:(0,V.jsxs)(V.Fragment,{children:[(0,V.jsx)(l,{variant:`secondary`,type:`button`,onClick:()=>A(!1),children:`Cancel`}),(0,V.jsx)(l,{variant:`primary`,type:`submit`,form:`combo-form`,disabled:t.isPending||r.isPending||!N.trim()||$.length===0,children:j?`Save Changes`:`Create Combo`})]}),children:(0,V.jsxs)(`form`,{id:`combo-form`,onSubmit:ge,className:`flex flex-col gap-6`,children:[(0,V.jsx)(S,{label:`Combo name`,id:`combo-name-input`,value:N,onChange:e=>L(e.target.value),placeholder:`e.g. smart-combo, fast-pool`,required:!0}),(0,V.jsx)(a,{label:`Strategy`,id:`combo-strategy-select`,value:q,onValueChange:e=>J(e),options:W}),(0,V.jsxs)(w,{gap:`6px`,children:[(0,V.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,V.jsx)(`label`,{className:`text-caption font-semibold text-text-secondary`,children:`Member models — use arrows or drag to reorder`}),(0,V.jsx)(l,{variant:`secondary`,size:`sm`,type:`button`,icon:(0,V.jsx)(re,{size:12}),onClick:()=>X(!0),children:`Browse models`})]}),(0,V.jsx)(`div`,{role:`list`,"aria-label":`Combo members`,"data-testid":`combo-members-list`,className:`flex min-h-22 flex-col gap-2 rounded-control border border-dashed border-inner-border ${H.length===0?`p-0`:`p-3`}`,children:H.length===0?(0,V.jsx)(`button`,{type:`button`,onClick:()=>X(!0),className:`flex-1 cursor-pointer font-inherit text-caption text-text-tertiary`,children:`Pick models to add`}):(0,V.jsx)(o,{items:H,label:`Combo members`,gap:`4px`,onReorder:e=>{let t=new Map(H.map(e=>[e.id,e]));K(e.map(e=>t.get(e)).filter(Boolean))},renderItem:e=>(0,V.jsx)(D,{density:`compact`,actions:(0,V.jsx)(`button`,{type:`button`,onClick:()=>de(e.value),"aria-label":`Remove ${e.value}`,className:`flex shrink-0 cursor-pointer text-text-tertiary`,children:(0,V.jsx)(c,{size:12})}),children:(0,V.jsx)(U,{value:e.value,tone:`accent`})})})})]}),(0,V.jsx)(C,{open:Y,onClose:()=>X(!1),selected:$,onToggle:de,title:`Select combo members`,multi:!0})]})}),(0,V.jsx)(p,{open:!!Z,onClose:()=>Q(null),title:`Delete Model Combo`,children:(0,V.jsxs)(w,{gap:`12px`,children:[(0,V.jsxs)(`p`,{className:`text-body text-text-secondary`,children:[`Are you sure you want to delete combo `,(0,V.jsx)(`strong`,{children:Z?.name}),`? Any aliases pointing to it will fail to resolve.`]}),(0,V.jsxs)(y,{children:[(0,V.jsx)(l,{variant:`secondary`,onClick:()=>Q(null),children:`Cancel`}),(0,V.jsx)(l,{variant:`danger`,onClick:()=>{Z&&i.mutate(Z.id,{onSuccess:()=>Q(null)})},disabled:i.isPending,children:i.isPending?`Deleting...`:`Delete Combo`})]})]})})]})}function J(){return(0,V.jsxs)(w,{gap:`16px`,children:[(0,V.jsx)(q,{}),(0,V.jsx)(K,{})]})}export{J as default};
@@ -1 +0,0 @@
1
- import{n as e,o as t,r as n,t as r}from"./jsx-runtime-CXUMngI_.js";import{a as i,i as a,n as o,o as s,r as c}from"./model-bans-CSPQ9xB0.js";import{n as l,t as u}from"./fingerprint-pattern-BWuHtbW4.js";import{t as d}from"./check-D69FzdvC.js";import{n as f,t as p}from"./select-C9mMoEpJ.js";import{t as m}from"./chevron-right-BXIy5l3h.js";import{t as h}from"./SortableList-DpOTnGuH.js";import{_ as g,r as _,t as v}from"./toast-C76WrMhc.js";import{t as y}from"./copy-ngmQ9zcZ.js";import{t as b}from"./download-A0SW9OCW.js";import{t as x}from"./eye-off-Bk5g2luF.js";import{t as S}from"./eye-C_hawfH4.js";import{t as C}from"./gauge-BbIMy7MB.js";import{a as w,n as T,r as E,t as ee}from"./state-CzUPfLXo.js";import{t as te}from"./key-round-UkavsGxp.js";import{n as ne,r as D,t as O}from"./clipboard-button-R_rArH8S.js";import{t as k}from"./dialog-BWIP_EvW.js";import{t as A}from"./pencil-768ysRAr.js";import{t as re}from"./plus-DIdX_g-x.js";import{t as j}from"./refresh-cw-BqJFk4sU.js";import{t as M}from"./route-CNSNj9Zm.js";import{t as ie}from"./search-JOKus9rb.js";import{t as ae}from"./shield-check-DVS0Foqh.js";import{n as N,r as oe,t as se}from"./settings-C4_Gn_wq.js";import{t as P}from"./sparkles-BRaRUxAU.js";import{t as ce}from"./trash-Cppn5c3R.js";import{b as le,y as ue}from"./assert-7J6_LVD3.js";import{l as F}from"./query-keys-D7wX2vM5.js";import{F as I,O as de,et as fe,nt as pe}from"./entry-7dpgvpJ3.js";import{n as L,r as R,t as z}from"./card-B0Bnx5CA.js";import{t as B}from"./use-clipboard-B0Sy03fE.js";import{n as me}from"./download-dRFo5_Wu.js";import{c as V,d as H}from"./tokens-DDxMbSRL.js";import{t as U}from"./badge-BTyGk_DL.js";import{n as W,t as he}from"./input-B9oyU93y.js";import{t as ge}from"./ModelPicker-Br3CXEau.js";import{t as G}from"./switch-DOUIky_i.js";import{t as K}from"./stack-Ca5HxI5i.js";import{n as q}from"./inline-D1TCqM2D.js";import{a as _e,c as ve,d as J,f as ye,i as be,l as xe,n as Se,o as Ce,r as we,s as Te,t as Y,u as Ee}from"./api-keys-BETfaMYZ.js";import{a as X,i as Z,n as De,r as Oe}from"./cli-tools-DE2ug812.js";import{t as ke}from"./layout-BArzXirQ.js";import{s as Ae}from"./quota-DZGyp0N5.js";var je=e(`lock-keyhole`,[[`circle`,{cx:`12`,cy:`16`,r:`1`,key:`1au0dj`}],[`rect`,{x:`3`,y:`10`,width:`18`,height:`12`,rx:`2`,key:`6s8ecr`}],[`path`,{d:`M7 10V7a5 5 0 0 1 10 0v3`,key:`1pqi11`}]]),Me=e(`repeat-2`,[[`path`,{d:`m2 9 3-3 3 3`,key:`1ltn5i`}],[`path`,{d:`M13 18H7a2 2 0 0 1-2-2V6`,key:`1r6tfw`}],[`path`,{d:`m22 15-3 3-3-3`,key:`4rnwn2`}],[`path`,{d:`M11 6h6a2 2 0 0 1 2 2v10`,key:`2f72bc`}]]),Ne=e(`rotate-cw`,[[`path`,{d:`M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8`,key:`1p45f6`}],[`path`,{d:`M21 3v5h-5`,key:`1q7to0`}]]),Pe=e(`scissors`,[[`circle`,{cx:`6`,cy:`6`,r:`3`,key:`1lh9wr`}],[`path`,{d:`M8.12 8.12 12 12`,key:`1alkpv`}],[`path`,{d:`M20 4 8.12 15.88`,key:`xgtan2`}],[`circle`,{cx:`6`,cy:`18`,r:`3`,key:`fqmcym`}],[`path`,{d:`M14.8 14.8 20 20`,key:`ptml3r`}]]),Fe=e(`share-2`,[[`circle`,{cx:`18`,cy:`5`,r:`3`,key:`gq8acd`}],[`circle`,{cx:`6`,cy:`12`,r:`3`,key:`w7nqdw`}],[`circle`,{cx:`18`,cy:`19`,r:`3`,key:`1xt0gg`}],[`line`,{x1:`8.59`,x2:`15.42`,y1:`13.51`,y2:`17.49`,key:`47mynk`}],[`line`,{x1:`15.41`,x2:`8.59`,y1:`6.51`,y2:`10.49`,key:`1n3mei`}]]),Ie=e(`shield-off`,[[`path`,{d:`m2 2 20 20`,key:`1ooewy`}],[`path`,{d:`M5 5a1 1 0 0 0-1 1v7c0 5 3.5 7.5 7.67 8.94a1 1 0 0 0 .67.01c2.35-.82 4.48-1.97 5.9-3.71`,key:`1jlk70`}],[`path`,{d:`M9.309 3.652A12.252 12.252 0 0 0 11.24 2.28a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1v7a9.784 9.784 0 0 1-.08 1.264`,key:`18rp1v`}]]),Le=e(`shield-x`,[[`path`,{d:`M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z`,key:`oel41y`}],[`path`,{d:`m14.5 9.5-5 5`,key:`17q4r4`}],[`path`,{d:`m9.5 9.5 5 5`,key:`18nt4w`}]]),Re=e(`undo-2`,[[`path`,{d:`M9 14 4 9l5-5`,key:`102s5s`}],[`path`,{d:`M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5a5.5 5.5 0 0 1-5.5 5.5H11`,key:`f3b9sd`}]]),ze=e(`user-round`,[[`circle`,{cx:`12`,cy:`8`,r:`5`,key:`1hypcn`}],[`path`,{d:`M20 21a8 8 0 0 0-16 0`,key:`rfgkzh`}]]),Be=e(`users-round`,[[`path`,{d:`M18 21a8 8 0 0 0-16 0`,key:`3ypg7q`}],[`circle`,{cx:`10`,cy:`8`,r:`5`,key:`o932ke`}],[`path`,{d:`M22 20c0-3.37-2-6.5-4-8a5 5 0 0 0-.45-8.3`,key:`10s06x`}]]),Ve=[`routing:invoke`,`routing:cli_mapping`,`search:invoke`,`dashboard:read`,`dashboard:write`,`providers:read`,`providers:write`,`models:read`,`models:write`],He=[`image/png`,`image/jpeg`,`image/webp`,`image/gif`],Q=t(n(),1),$=r(),Ue={"routing:invoke":`Call /v1/* gateway routes within this tenant.`,"routing:cli_mapping":`Resolve persisted CLI source→target model mappings.`,"search:invoke":`Call POST /v1/search with exa.`,"dashboard:read":`Read this tenant's configuration and usage.`,"dashboard:write":`Modify this tenant's configuration.`,"providers:read":`Read provider rows, including accounts.`,"providers:write":`Add, modify, or delete provider rows and their credentials.`,"models:read":`Read catalog model rows.`,"models:write":`Add, modify, or delete catalog model rows.`},We={"routing:invoke":M,"routing:cli_mapping":M,"search:invoke":ie,"dashboard:read":S,"dashboard:write":A,"providers:read":fe,"providers:write":je,"models:read":l,"models:write":i},Ge={k:1e3,m:1e6,b:1e9,t:0xe8d4a51000},Ke=[{label:`1M`,amount:1e6,description:`1 million`},{label:`100M`,amount:1e8,description:`100 million`},{label:`1B`,amount:1e9,description:`1 billion`},{label:`1T`,amount:0xe8d4a51000,description:`1 trillion`}];function qe(e){let t=e.trim().toLowerCase();if(!t)return;let n=t.match(/^([\d.]+)\s*([kmbt])?$/);if(!n)return;let r=Number(n[1]);if(!Number.isFinite(r)||r<=0)return;let i=n[2]?Ge[n[2]]??1:1;return Math.round(r*i)}function Je(e){if(e==null)return``;let t=Ke.find(t=>t.amount===e);return t?t.label:e>=1e9?`${(e/1e9).toFixed(1).replace(/\.0$/,``)}B`:e>=1e6?`${(e/1e6).toFixed(1).replace(/\.0$/,``)}M`:e>=1e3?`${(e/1e3).toFixed(1).replace(/\.0$/,``)}K`:String(e)}function Ye(e){let t=Number(e.trim());return Number.isFinite(t)&&t>0?Math.round(t):void 0}function Xe(e){let t=Ye(e);return e.trim()===``?null:t}function Ze({label:e,value:t,onChange:n,disabled:r}){let i=qe(t),a=Ke.find(e=>e.amount===i)?.label;return(0,$.jsxs)(`div`,{children:[(0,$.jsx)(W,{label:e,value:t,onChange:e=>n(e.target.value),placeholder:`e.g. 1M, 500K`,disabled:r}),(0,$.jsx)(`div`,{className:`flex flex-wrap gap-3 mt-3`,role:`group`,"aria-label":`${e} presets`,children:Ke.map(e=>(0,$.jsx)(`button`,{type:`button`,"aria-pressed":a===e.label,"aria-label":`${e.label}, ${e.description} tokens`,onClick:()=>n(e.label),disabled:r,className:`py-1 px-4 rounded-chip text-micro font-semibold`,style:{cursor:r?`not-allowed`:`pointer`,border:`1px solid ${a===e.label?`var(--accent)`:`var(--inner-border)`}`,background:a===e.label?`var(--accent-soft)`:`transparent`,color:a===e.label?`var(--accent)`:`var(--text-tertiary)`},children:e.label},e.label))})]})}function Qe({tool:e,keyId:t,busy:n,scopeGranted:r,onScopeChange:i}){let a=Z(e.id,t),o=Oe(),s=De(),c=a.data?.enabled??!1,l=r&&c,u=a.data?.mappings.length??0,d=o.isPending||s.isPending;return(0,$.jsxs)(`div`,{className:`flex items-center justify-between gap-6 py-2 px-5 rounded-inset`,style:{minHeight:`44px`,border:`1px solid var(--inner-border)`,background:l?`var(--accent-soft)`:`var(--surface-1)`},children:[(0,$.jsxs)(`div`,{className:`flex min-w-0 items-start gap-3`,children:[(0,$.jsx)(M,{size:16,className:`mt-1 shrink-0 text-accent`,"aria-hidden":`true`}),(0,$.jsxs)(`div`,{className:`min-w-0`,children:[(0,$.jsx)(`div`,{className:`text-caption font-semibold text-text-primary`,children:e.name}),(0,$.jsx)(`p`,{className:`mt-1 text-badge text-text-tertiary`,children:l?`Remote routing on — ${u} route${u===1?``:`s`} active, routing:cli_mapping granted.`:`Remote routing off — this key's requests go straight to the provider.`})]})]}),(0,$.jsxs)(`div`,{className:`flex items-center gap-4`,children:[(0,$.jsx)(G,{checked:l,disabled:n||d||a.isLoading,"aria-label":`Enable remote routing for ${e.name}`,onChange:n=>{n!==r&&i(n),o.mutate({toolId:e.id,keyId:t,input:{enabled:n,mappings:a.data?.mappings??[]}},{onSuccess:()=>v.success(`Saved remote routing for ${e.name}.`),onError:e=>v.error(F(e,`Could not save remote routing.`))})}}),(0,$.jsx)(_,{variant:`secondary`,size:`sm`,disabled:n||d||!c&&u===0,onClick:()=>s.mutate({toolId:e.id,keyId:t},{onSuccess:()=>v.success(`Cleared remote routes for ${e.name}.`),onError:e=>v.error(F(e,`Could not reset remote routes.`))}),children:s.isPending?`Resetting…`:`Reset`})]})]})}function $e({keyId:e,busy:t,scopeGranted:n,onScopeChange:r,persistScope:i}){let a=(X().data??[]).filter(e=>e.mappingSupported);return e.length===0||a.length===0?null:(0,$.jsxs)(`section`,{"aria-labelledby":`remote-routing-heading`,className:`flex flex-col gap-4`,children:[(0,$.jsxs)(`div`,{children:[(0,$.jsxs)(`h4`,{id:`remote-routing-heading`,className:`inline-flex items-center gap-2 text-caption font-bold text-text-primary`,children:[(0,$.jsx)(M,{size:15,className:`text-accent`,"aria-hidden":`true`}),`Remote routing`]}),(0,$.jsx)(`p`,{className:`mt-1 text-badge text-text-tertiary`,children:`Route this key's CLI model calls through persisted mappings and grant routing:cli_mapping. Applies immediately — no save needed.`})]}),(0,$.jsx)(`div`,{className:`flex flex-col gap-3`,children:a.map(a=>(0,$.jsx)(Qe,{tool:a,keyId:e,busy:t,scopeGranted:n,onScopeChange:e=>{r(e),i(e)}},a.id))})]})}function et(e,t,n){let r=e===`personal`?t.trim():``;return{keyMode:e,keyPrefix:n.trim()||void 0,...r?{key:r}:{}}}function tt(e,t){return e===`personal`?t??null:null}function nt(e){if(e?.isDefault)return[...Ve];let t=e?.scopes;return t&&t.length>0?[...t]:[`routing:invoke`,`search:invoke`]}function rt({mode:e,record:t,busy:n,onDone:r,onClose:i,actionsRef:a}){let[o,c]=(0,Q.useState)(t?.label??``),[d,p]=(0,Q.useState)(t?.keyPrefix??``),[m,h]=(0,Q.useState)(``),[g,y]=(0,Q.useState)(t?.requestsPerMinute?.toString()??``),[b,x]=(0,Q.useState)(Je(t?.dailyTokenLimit??null)),[S,w]=(0,Q.useState)(Je(t?.monthlyTokenLimit??null)),[T,E]=(0,Q.useState)(Je(t?.lifetimeTokenBudget??null)),[ee,D]=(0,Q.useState)(t?.lifetimeTokenBudget==null?`recurring`:`one-time`),[O,k]=(0,Q.useState)(t?.maxConcurrentRequests?.toString()??``),[A,re]=(0,Q.useState)(()=>nt(t)),[j,ie]=(0,Q.useState)(t?.modelAccessMode??`whitelist`),[N,se]=(0,Q.useState)(()=>{let e=t?.modelList;return e?[...e]:[]}),[P,ce]=(0,Q.useState)(!1),[I,de]=(0,Q.useState)(()=>{let e=t?.clientRouterDenylist;return e?[...new Set(e.map(le).filter(e=>e!==void 0))]:[]}),[fe,L]=(0,Q.useState)(()=>t?.notesTitle??``),[R,z]=(0,Q.useState)(()=>t?.notesSubtitle??``),[B,me]=(0,Q.useState)(()=>t?.notesBody??``),[V,H]=(0,Q.useState)(t?.sharePopupEnabled??!1),[U,K]=(0,Q.useState)(null),[q,_e]=(0,Q.useState)(!1),[ve,J]=(0,Q.useState)(null),be=t?.sharePopupImageMime!==void 0,xe=U??(be&&!q&&t!==null?`/console/api/api-keys/${t.id}/share-popup-image`:null),[Se,Ce]=(0,Q.useState)(t?.sharePopupTitle??``),[we,Te]=(0,Q.useState)(t?.sharePopupBody??``),[Y,Ee]=(0,Q.useState)(t?.keyMode??`personal`),X=ee===`one-time`,Z=e===`edit`,De=ye(),Oe=e=>re(t=>t.includes(e)?t.filter(t=>t!==e):[...t,e]),ke=()=>{r({label:o.trim(),...et(Y,m,d),scopes:A,modelAccessMode:j,modelList:N,clientRouterDenylist:I,requestsPerMinute:Xe(g),maxConcurrentRequests:Xe(O),dailyTokenLimit:X?null:qe(b)??null,monthlyTokenLimit:X?null:qe(S)??null,lifetimeTokenBudget:X?qe(T)??null:null,notesTitle:fe.trim(),notesSubtitle:R.trim(),notesBody:B.trim(),sharePopupEnabled:V,...U===null?q?{sharePopupImage:null}:{}:{sharePopupImage:U},sharePopupTitle:Se.trim(),sharePopupBody:we.trim()})};return(0,Q.useEffect)(()=>{a?.((0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(_,{variant:`secondary`,size:`sm`,onClick:i,disabled:n,children:`Cancel`}),(0,$.jsx)(_,{variant:`primary`,size:`sm`,disabled:n||e===`create`&&!o.trim(),onClick:ke,children:n?`Saving…`:e===`create`?`Create API key`:`Save changes`})]}))}),(0,$.jsxs)(`div`,{className:`flex flex-col gap-6 min-w-0 pb-2`,children:[(0,$.jsxs)(`section`,{className:`flex flex-col gap-4 min-w-0 p-6 border border-inner-border rounded-card bg-surface-2 col-span-full`,children:[(0,$.jsxs)(`h3`,{className:`inline-flex items-center gap-2 text-body font-bold text-text-primary`,children:[(0,$.jsx)(te,{size:16,className:`text-accent`,"aria-hidden":`true`}),`Credential mode`]}),(0,$.jsx)(`div`,{role:`group`,"aria-label":`Credential mode`,className:`flex gap-4`,children:[`personal`,`share`].map(e=>(0,$.jsx)(`button`,{type:`button`,"aria-pressed":Y===e,disabled:n||Z,onClick:()=>Ee(e),title:Z?`Credential mode cannot change after creation`:void 0,className:`rounded-inset py-2 px-6`,style:{border:`1px solid var(--inner-border)`,color:Y===e?`var(--accent)`:`var(--text-secondary)`,background:Y===e?`var(--accent-soft)`:`var(--surface-2)`,cursor:Z?`not-allowed`:`pointer`,opacity:Z&&Y!==e?.5:1},children:(0,$.jsxs)(`span`,{className:`inline-flex items-center gap-2`,children:[e===`personal`?(0,$.jsx)(ze,{size:14,"aria-hidden":`true`}):(0,$.jsx)(Be,{size:14,"aria-hidden":`true`}),e===`personal`?`Personal`:`Share template`]})},e))}),(0,$.jsx)(`p`,{className:`mt-1 text-badge text-text-tertiary`,children:Y===`share`?`A share template does not authenticate requests. Recipients generate their own child key from its public enrollment page.`:`A personal key authenticates requests directly; its secret is shown once when created or rotated.`})]}),(0,$.jsxs)(`div`,{className:`columns-2 gap-6 min-w-0 max-medium:columns-1`,children:[e===`create`&&(0,$.jsxs)(`section`,{className:`flex flex-col gap-4 min-w-0 p-6 border border-inner-border rounded-card bg-surface-2 break-inside-avoid mb-6`,children:[(0,$.jsxs)(`div`,{children:[(0,$.jsxs)(`h3`,{className:`inline-flex items-center gap-2 text-body font-bold text-text-primary`,children:[(0,$.jsx)(u,{size:16,className:`text-accent`,"aria-hidden":`true`}),`Identity`]}),(0,$.jsx)(`p`,{className:`mt-1 text-badge text-text-tertiary`,children:`Give this credential a recognizable name.`})]}),(0,$.jsxs)(`div`,{className:`grid grid-cols-2 gap-4 items-start min-w-0 max-medium:grid-cols-1`,children:[(0,$.jsx)(W,{label:`Name`,value:o,onChange:e=>c(e.target.value),placeholder:`ci-key…`,disabled:n,autoFocus:!0}),(0,$.jsx)(W,{label:`Key prefix`,value:d,onChange:e=>p(e.target.value),placeholder:`carte_ (default)…`,disabled:n||m.trim().length>0})]}),Y===`personal`&&(0,$.jsx)(W,{label:`Custom API key value (optional)`,value:m,onChange:e=>h(e.target.value),placeholder:`Leave blank to generate…`,disabled:n})]}),e===`edit`&&(0,$.jsxs)(`section`,{className:`flex flex-col gap-4 min-w-0 p-6 border border-inner-border rounded-card bg-surface-2 break-inside-avoid mb-6`,children:[(0,$.jsxs)(`div`,{children:[(0,$.jsxs)(`h3`,{className:`text-body font-bold text-text-primary`,children:[(0,$.jsx)(u,{size:16,className:`mr-2 inline text-accent`,"aria-hidden":`true`}),`Identity`]}),(0,$.jsx)(`p`,{className:`mt-1 text-badge text-text-tertiary`,children:t?.isDefault?`The default key's name is fixed. Limits, access, and everything else can still change.`:`Update the label for this credential.`})]}),(0,$.jsx)(`div`,{className:`grid grid-cols-2 gap-4 items-start min-w-0 max-medium:grid-cols-1`,children:(0,$.jsx)(W,{label:`Name`,value:o,onChange:e=>c(e.target.value),placeholder:`ci-key…`,disabled:n||t?.isDefault===!0,title:t?.isDefault?`The default gateway key keeps its name`:void 0,autoFocus:!0})})]}),(0,$.jsxs)(`section`,{className:`flex flex-col gap-4 min-w-0 p-6 border border-inner-border rounded-card bg-surface-2 break-inside-avoid mb-6`,children:[(0,$.jsxs)(`div`,{children:[(0,$.jsxs)(`h3`,{className:`text-body font-bold text-text-primary`,children:[(0,$.jsx)(ne,{size:16,className:`mr-2 inline text-accent`,"aria-hidden":`true`}),`Share notes`]}),(0,$.jsx)(`p`,{className:`mt-1 text-badge text-text-tertiary`,children:`Optional copy shown on the public share page.`})]}),(0,$.jsxs)(`div`,{className:`grid grid-cols-2 gap-4 items-start min-w-0 max-medium:grid-cols-1`,children:[(0,$.jsx)(W,{label:`Title`,value:fe,onChange:e=>L(e.target.value),placeholder:`e.g. Bansos Token`,disabled:n}),(0,$.jsx)(W,{label:`Subtitle`,value:R,onChange:e=>z(e.target.value),placeholder:`e.g. Come and save your tokens`,disabled:n}),(0,$.jsx)(W,{label:`Body`,value:B,onChange:e=>me(e.target.value),placeholder:`Free-form notes for the recipient`,disabled:n})]})]}),(0,$.jsxs)(`section`,{className:`flex flex-col gap-4 min-w-0 p-6 border border-inner-border rounded-card bg-surface-2 break-inside-avoid mb-6`,children:[(0,$.jsxs)(`div`,{children:[(0,$.jsxs)(`h3`,{className:`text-body font-bold text-text-primary`,children:[(0,$.jsx)(C,{size:16,className:`mr-2 inline text-accent`,"aria-hidden":`true`}),`Limits`]}),(0,$.jsx)(`p`,{className:`mt-1 text-badge text-text-tertiary`,children:`Keep this credential predictable under load.`})]}),(0,$.jsxs)(`div`,{className:`grid grid-cols-2 gap-4 items-start min-w-0 max-medium:grid-cols-1`,children:[(0,$.jsx)(W,{label:`Requests per minute`,type:`number`,min:`0`,value:g,onChange:e=>y(e.target.value),placeholder:`Unlimited…`,disabled:n}),(0,$.jsx)(W,{label:`Max concurrent requests`,type:`number`,min:`0`,value:O,onChange:e=>k(e.target.value),placeholder:`Unlimited…`,disabled:n})]}),(0,$.jsxs)(`div`,{className:`flex items-center justify-between gap-6 rounded-panel border border-inner-border px-6 py-5 bg-surface-2`,children:[(0,$.jsxs)(`div`,{children:[(0,$.jsxs)(`div`,{className:`text-caption font-bold text-text-primary`,children:[(0,$.jsx)(C,{size:14,className:`mr-2 inline text-accent`,"aria-hidden":`true`}),`Token budget`]}),(0,$.jsx)(`p`,{className:`mt-1 text-text-tertiary text-micro`,children:X?`One-time cap; it does not reset.`:`Daily and monthly limits reset automatically.`})]}),(0,$.jsxs)(`div`,{className:`flex gap-2 rounded-inset border border-inner-border bg-surface-1 p-1`,children:[(0,$.jsx)(`button`,{type:`button`,onClick:()=>D(`recurring`),disabled:n,className:`py-2 px-5 rounded-chip font-semibold border-0 cursor-pointer`,style:{fontSize:`10.5px`,background:X?`transparent`:`var(--accent-soft)`,color:X?`var(--text-tertiary)`:`var(--accent)`},children:(0,$.jsxs)(`span`,{className:`inline-flex items-center gap-2`,children:[(0,$.jsx)(Me,{size:13,"aria-hidden":`true`}),`Recurring`]})}),(0,$.jsx)(`button`,{type:`button`,onClick:()=>D(`one-time`),disabled:n,className:`py-2 px-5 rounded-chip font-semibold border-0 cursor-pointer`,style:{fontSize:`10.5px`,background:X?`var(--accent-soft)`:`transparent`,color:X?`var(--accent)`:`var(--text-tertiary)`},children:(0,$.jsxs)(`span`,{className:`inline-flex items-center gap-2`,children:[(0,$.jsx)(je,{size:13,"aria-hidden":`true`}),`One-time`]})})]})]}),X?(0,$.jsx)(Ze,{label:`One-time token limit`,value:T,onChange:E,disabled:n}):(0,$.jsxs)(`div`,{className:`grid grid-cols-2 gap-4 items-start min-w-0 max-medium:grid-cols-1`,children:[(0,$.jsx)(Ze,{label:`Daily token limit`,value:b,onChange:x,disabled:n}),(0,$.jsx)(Ze,{label:`Monthly token limit`,value:S,onChange:w,disabled:n})]})]})]}),(0,$.jsxs)(`section`,{className:`flex flex-col gap-4 min-w-0 p-6 border border-inner-border rounded-card bg-surface-2 col-span-full`,"aria-labelledby":`share-popup-heading`,children:[(0,$.jsxs)(`div`,{className:`flex items-start justify-between gap-6`,children:[(0,$.jsxs)(`div`,{children:[(0,$.jsxs)(`h3`,{id:`share-popup-heading`,className:`text-body font-bold text-text-primary`,children:[(0,$.jsx)(Fe,{size:16,className:`mr-2 inline text-accent`,"aria-hidden":`true`}),`Share page popup`]}),(0,$.jsx)(`p`,{className:`mt-1 text-badge text-text-tertiary`,children:`Optional card visitors open from the Base URL card on your share page.`})]}),(0,$.jsx)(G,{checked:V,onChange:e=>H(e),disabled:n,label:`Enable popup`})]}),V?(0,$.jsxs)(`div`,{className:`flex gap-7 items-start mt-2 min-w-0 max-medium:flex-col max-medium:items-stretch`,children:[(0,$.jsxs)(`div`,{className:`flex flex-col gap-4 min-w-0 w-100 shrink-0 max-medium:w-auto`,children:[xe?(0,$.jsx)(`img`,{className:`block w-full h-66 object-cover border border-inner-border rounded-control bg-surface-1 max-medium:h-80`,src:xe,alt:`Popup image preview`}):(0,$.jsx)(`div`,{className:`flex items-center justify-center w-full h-66 object-cover border border-dashed border-inner-border rounded-control bg-surface-1 text-text-tertiary text-badge max-medium:h-80`,"aria-hidden":`true`,children:(0,$.jsx)(`span`,{children:`No image`})}),(0,$.jsx)(he,{id:`share-popup-image`,accept:He.join(`,`),"aria-label":`Popup image`,disabled:n,onChange:e=>{let t=e.target.files?.[0];if(e.target.value=``,!t)return;if(J(null),t.size>2097152){K(null),J(`Image must be 2 MB or smaller.`);return}let n=new FileReader;n.onload=()=>{K(String(n.result)),_e(!1)},n.onerror=()=>J(`Could not read that file.`),n.readAsDataURL(t)}}),xe?(0,$.jsx)(_,{variant:`secondary`,size:`sm`,type:`button`,disabled:n,className:`h-14 px-5 text-badge self-start`,onClick:()=>{K(null),_e(!0),J(null)},children:`Remove image`}):null,ve?(0,$.jsx)(`p`,{className:`form-error`,children:ve}):null,(0,$.jsx)(`p`,{className:`m-0 text-text-tertiary text-badge leading-normal`,children:`PNG, JPEG, WebP, or GIF up to 2 MB.`})]}),(0,$.jsxs)(`div`,{className:`flex flex-col gap-4 min-w-0 flex-1 [&_.form-group]:gap-2`,children:[(0,$.jsx)(W,{label:`Popup title`,value:Se,onChange:e=>Ce(e.target.value),maxLength:120,placeholder:`Support this gateway`,disabled:n}),(0,$.jsxs)(`div`,{className:`form-group`,children:[(0,$.jsx)(`label`,{className:`form-label`,htmlFor:`share-popup-copy`,children:`Message`}),(0,$.jsx)(`textarea`,{className:`form-textarea`,id:`share-popup-copy`,value:we,onChange:e=>Te(e.target.value),maxLength:1200,rows:3,placeholder:`If this service helps you, consider supporting its upkeep…`,disabled:n})]})]})]}):null]}),(0,$.jsxs)(`details`,{className:`group min-w-0 col-span-full rounded-card border border-inner-border bg-surface-2`,children:[(0,$.jsxs)(`summary`,{className:`flex cursor-pointer list-none items-center justify-between gap-4 p-6 [&::-webkit-details-marker]:hidden`,children:[(0,$.jsxs)(`span`,{className:`flex min-w-0 items-start gap-3`,children:[(0,$.jsx)(`span`,{className:`grid size-10 shrink-0 place-items-center rounded-inset bg-accent-soft text-accent`,children:(0,$.jsx)(oe,{size:18,"aria-hidden":`true`})}),(0,$.jsxs)(`span`,{className:`min-w-0`,children:[(0,$.jsx)(`span`,{className:`block text-body font-bold text-text-primary`,children:`Access`}),(0,$.jsx)(`span`,{className:`mt-1 block text-badge text-text-tertiary`,children:t?.isDefault?`Default gateway key · all tenant permissions, including gateway routing and remote debugging.`:`Choose model, router, and dashboard permissions for this key.`})]})]}),(0,$.jsxs)(`span`,{className:`inline-flex shrink-0 items-center gap-2 rounded-control border border-inner-border bg-surface-1 px-3 py-2 text-micro font-semibold text-text-secondary`,children:[A.length,`/`,Ve.length,` grants`,(0,$.jsx)(f,{size:14,className:`transition-transform group-open:rotate-180`,"aria-hidden":`true`})]})]}),(0,$.jsxs)(`div`,{className:`flex flex-col gap-5 border-t border-inner-border p-6`,children:[t?.isDefault?(0,$.jsxs)(`div`,{className:`flex items-start gap-3 rounded-control border border-accent/30 bg-accent-soft p-4 text-badge text-text-secondary`,children:[(0,$.jsx)(ae,{size:16,className:`mt-0.5 shrink-0 text-accent`,"aria-hidden":`true`}),(0,$.jsxs)(`p`,{className:`m-0`,children:[`This default API key is provisioned with every tenant permission, including `,(0,$.jsx)(`code`,{children:`routing:invoke`}),` and`,` `,(0,$.jsx)(`code`,{children:`routing:cli_mapping`}),`. Those grant gateway access and authorization to use remote CLI mappings; each client's mapping toggle below still controls whether those routes are active. You can remove individual grants here.`]})]}):null,(0,$.jsxs)(`div`,{className:`columns-2 gap-6 min-w-0 max-medium:columns-1`,children:[(0,$.jsxs)(`div`,{children:[(0,$.jsxs)(`label`,{className:`inline-flex items-center gap-2 text-badge font-semibold text-text-secondary block mb-3`,children:[(0,$.jsx)(l,{size:14,className:`text-accent`,"aria-hidden":`true`}),`Model access`]}),(0,$.jsx)(`div`,{role:`group`,"aria-label":`Model access mode`,className:`flex gap-3 mb-4`,children:[`whitelist`,`blacklist`].map(e=>(0,$.jsxs)(`button`,{type:`button`,"aria-pressed":j===e,disabled:n,onClick:()=>ie(e),title:e===`whitelist`?`Only the listed models may be used. An empty list allows every model.`:`The listed models are refused. Everything else is allowed.`,className:`flex-1 py-2.5 px-4 text-badge font-semibold`,style:{borderRadius:`7px`,border:`1px solid ${j===e?`var(--accent)`:`var(--inner-border)`}`,background:j===e?`var(--accent-soft)`:`var(--surface-2)`,color:j===e?`var(--accent)`:`var(--text-secondary)`,cursor:n?`not-allowed`:`pointer`,opacity:n?.6:1},children:[e===`whitelist`?`Whitelist`:`Blacklist`,e===`whitelist`?(0,$.jsx)(ae,{size:13,className:`ml-2 inline`,"aria-hidden":`true`}):(0,$.jsx)(Le,{size:13,className:`ml-2 inline`,"aria-hidden":`true`})]},e))}),(0,$.jsx)(`label`,{className:`text-badge font-semibold text-text-secondary block mb-3`,children:j===`whitelist`?`Allowed models`:`Blocked models`}),(0,$.jsx)(`div`,{className:`flex gap-3 flex-wrap mb-4`,children:N.length===0?(0,$.jsx)(`span`,{className:`mt-1 text-badge text-text-tertiary`,children:j===`whitelist`?`All models allowed`:`No models blocked`}):N.map(e=>(0,$.jsxs)(`span`,{className:`font-mono tabular-nums inline-flex items-center gap-2 text-badge bg-accent-soft text-accent py-1 px-4 rounded-chip border border-accent`,children:[e,(0,$.jsx)(`button`,{type:`button`,"aria-label":`Remove ${e} from the model list`,onClick:()=>se(t=>t.filter(t=>t!==e)),disabled:n,className:`bg-transparent border-0 cursor-pointer p-0 flex`,children:`×`})]},e))}),(0,$.jsx)(_,{variant:`secondary`,size:`sm`,type:`button`,onClick:()=>ce(!0),disabled:n,children:`Browse models`}),(0,$.jsx)(ge,{open:P,onClose:()=>ce(!1),selected:N,onToggle:e=>se(t=>t.includes(e)?t.filter(t=>t!==e):[...t,e]),title:j===`whitelist`?`Select allowed models`:`Select blocked models`,multi:!0})]}),(0,$.jsxs)(`section`,{"aria-labelledby":`blocked-routers-heading`,className:`flex flex-col gap-4`,children:[(0,$.jsxs)(`div`,{children:[(0,$.jsxs)(`h4`,{id:`blocked-routers-heading`,className:`inline-flex items-center gap-2 text-caption font-bold text-text-primary`,children:[(0,$.jsx)(s,{size:15,className:`text-accent`,"aria-hidden":`true`}),`Blocked client routers`]}),(0,$.jsx)(`p`,{className:`mt-1 text-badge text-text-tertiary`,children:`Matching fingerprints are rejected with 403 before routing. Detection is best-effort; clients without a fingerprint are not matched. This may also block bazaar probe links that share the same fingerprint.`})]}),(0,$.jsx)(`div`,{className:`flex flex-col gap-3`,children:ue.map(e=>(0,$.jsxs)(`div`,{className:`flex items-center justify-between gap-6 py-2 px-5 rounded-inset`,style:{minHeight:`44px`,border:`1px solid var(--inner-border)`,background:I.includes(e.id)?`var(--accent-soft)`:`var(--surface-1)`},children:[(0,$.jsxs)(`div`,{className:`flex min-w-0 items-start gap-3`,children:[(0,$.jsx)(s,{size:15,className:`mt-1 shrink-0 text-orange`,"aria-hidden":`true`}),(0,$.jsxs)(`div`,{className:`min-w-0`,children:[(0,$.jsxs)(`div`,{className:`text-caption text-text-primary`,children:[`Block `,e.label,` Client`]}),(0,$.jsxs)(`p`,{className:`mt-1 text-badge text-text-tertiary`,children:[`Reject requests fingerprinted as `,e.label,`.`]})]})]}),(0,$.jsx)(G,{checked:I.includes(e.id),onChange:t=>de(n=>t?[...n,e.id]:n.filter(t=>t!==e.id)),disabled:n,"aria-label":`Block ${e.label} client`})]},e.id))})]}),(0,$.jsxs)(`section`,{"aria-labelledby":`routing-scopes-heading`,className:`flex flex-col gap-4`,children:[(0,$.jsxs)(`h4`,{id:`routing-scopes-heading`,className:`inline-flex items-center gap-2 text-caption font-bold text-text-primary`,children:[(0,$.jsx)(M,{size:15,className:`text-accent`,"aria-hidden":`true`}),`Routing`]}),(0,$.jsx)(`div`,{className:`flex flex-col gap-3`,children:Ve.filter(e=>e===`routing:invoke`).map(e=>(0,$.jsxs)(`div`,{className:`flex items-center justify-between gap-6 py-2 px-5 rounded-inset`,style:{minHeight:`44px`,border:`1px solid var(--inner-border)`,background:A.includes(e)?`var(--accent-soft)`:`var(--surface-1)`},children:[(0,$.jsxs)(`div`,{className:`flex items-start gap-3`,children:[(0,$.jsx)(M,{size:16,className:`mt-1 shrink-0 text-accent`,"aria-hidden":`true`}),(0,$.jsxs)(`div`,{className:`min-w-0`,children:[(0,$.jsx)(`div`,{className:`text-caption font-semibold text-text-primary`,children:e}),(0,$.jsx)(`p`,{className:`mt-1 text-badge text-text-tertiary`,children:Ue[e]})]})]}),(0,$.jsx)(G,{checked:A.includes(e),onChange:()=>Oe(e),disabled:n,"aria-label":`Grant ${e}`})]},e))})]}),(0,$.jsx)($e,{keyId:e===`edit`&&t?t.id:``,busy:n,scopeGranted:A.includes(`routing:cli_mapping`),onScopeChange:e=>{e!==A.includes(`routing:cli_mapping`)&&Oe(`routing:cli_mapping`)},persistScope:n=>{if(e!==`edit`||!t)return;let r=n?[...new Set([...A,`routing:cli_mapping`])]:A.filter(e=>e!==`routing:cli_mapping`);De.mutate({keyId:t.id,request:{scopes:r}},{onError:e=>v.error(F(e,`Could not update the key's routing scope.`))})}}),(0,$.jsxs)(`section`,{"aria-labelledby":`dashboard-scopes-heading`,className:`flex flex-col gap-4`,children:[(0,$.jsxs)(`h4`,{id:`dashboard-scopes-heading`,className:`inline-flex items-center gap-2 text-caption font-bold text-text-primary`,children:[(0,$.jsx)(pe,{size:15,className:`text-accent`,"aria-hidden":`true`}),`Dashboard / Resources`]}),(0,$.jsx)(`div`,{className:`flex flex-col gap-3`,children:Ve.filter(e=>!e.startsWith(`routing:`)).map(e=>(0,$.jsxs)(`div`,{className:`flex items-center justify-between gap-6 py-2 px-5 rounded-inset`,style:{minHeight:`44px`,border:`1px solid var(--inner-border)`,background:A.includes(e)?`var(--accent-soft)`:`var(--surface-1)`},children:[(0,$.jsxs)(`div`,{className:`flex items-start gap-3`,children:[(()=>{let t=We[e];return(0,$.jsx)(t,{size:16,className:`mt-1 shrink-0 text-accent`,"aria-hidden":`true`})})(),(0,$.jsxs)(`div`,{className:`min-w-0`,children:[(0,$.jsx)(`div`,{className:`text-caption font-semibold text-text-primary`,children:e}),(0,$.jsx)(`p`,{className:`mt-1 text-badge text-text-tertiary`,children:Ue[e]})]})]}),(0,$.jsx)(G,{checked:A.includes(e),onChange:()=>Oe(e),disabled:n,"aria-label":`Grant ${e}`})]},e))})]})]})]})]})]})}function it({secret:e,onClose:t}){return(0,$.jsx)(k,{open:e!==null,onClose:t,title:`New API key`,description:`Copy it now, or reveal it again later from the key's Share button.`,size:`md`,children:(0,$.jsxs)(`div`,{className:`flex flex-col gap-7`,children:[(0,$.jsx)(`p`,{className:`text-body text-text-secondary m-0 leading-normal`,children:`A personal key stays recoverable. Open the key's Share button and choose Reveal to see it again and copy it. Rotating the key replaces this secret, and any client still using it starts getting 401s.`}),(0,$.jsx)(`code`,{className:`block rounded-inset border border-inner-border bg-surface-1 py-5 px-6 text-caption text-text-primary break-all`,children:e}),(0,$.jsxs)(`div`,{className:`flex gap-4 flex-wrap`,children:[(0,$.jsx)(O,{value:e??``,size:`sm`,variant:`primary`,label:`Copy key`,copiedLabel:`Copied`}),(0,$.jsx)(_,{variant:`secondary`,size:`sm`,icon:(0,$.jsx)(b,{size:13}),onClick:()=>{e!==null&&(me(`cartethyia-api-key.txt`,`Cartethyia API key\n\n${e}\n\nKeep this secret. Reveal it again from the key's Share button.\n`,`text/plain;charset=utf-8`),v.success(`Key saved to your downloads.`))},children:`Save this key`})]})]})})}function at(e){let t=new Date(e);if(!Number.isFinite(t.getTime()))return`—`;let n=e-Date.now(),r=t.toLocaleString();if(n<=0)return`${r} (lapsed)`;let i=Math.ceil(n/6e4);if(i<60)return`${r} (in ${i}m)`;let a=Math.floor(i/60);return a<24?`${r} (in ${a}h)`:`${r} (in ${Math.floor(a/24)}d)`}function ot({onClose:e}){let t=o(!0),n=c(),[r,i]=(0,Q.useState)(null),a=t.data??[];return(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(k,{open:!0,onClose:e,title:`Banned Users`,description:`A client address is banned after repeated invalid-model requests. The ban applies to every API key used from that address, not just one credential.`,size:`lg`,children:(0,$.jsxs)(`div`,{className:`flex flex-col gap-7 min-w-0`,children:[(0,$.jsxs)(`div`,{className:`min-w-0`,children:[(0,$.jsxs)(`h4`,{className:`text-caption font-semibold text-text-secondary mb-4`,children:[`Active bans (`,a.length,`)`]}),t.isPending?(0,$.jsx)(E,{label:`Loading banned users…`}):t.isError?(0,$.jsx)(T,{message:F(t.error,`Could not load banned users.`),onRetry:()=>void t.refetch()}):a.length===0?(0,$.jsx)(ee,{title:`No banned users`,message:`No client address is currently banned for model abuse.`,icon:(0,$.jsx)(Ie,{size:20})}):(0,$.jsx)(`div`,{className:`rounded-inset border border-inner-border`,children:(0,$.jsx)(ke,{maxHeight:480,scrollRegion:!0,headers:[`Client IP`,`Reason`,`Applies to`,`Lapses`,``],children:a.map(e=>(0,$.jsxs)(`tr`,{children:[(0,$.jsx)(`td`,{className:`font-mono tabular-nums text-badge text-text-primary whitespace-nowrap`,children:e.ip}),(0,$.jsx)(`td`,{className:`text-badge text-text-secondary`,children:e.reason===`repeated_invalid_model_requests`?`Repeated invalid-model requests`:e.reason}),(0,$.jsx)(`td`,{className:`text-badge text-text-secondary whitespace-nowrap`,children:`All API keys from this IP`}),(0,$.jsx)(`td`,{className:`text-badge text-text-secondary whitespace-nowrap`,children:at(e.expiresAt)}),(0,$.jsx)(`td`,{className:`text-right whitespace-nowrap`,children:(0,$.jsx)(_,{variant:`secondary`,size:`sm`,icon:(0,$.jsx)(Re,{size:13}),disabled:n.isPending,onClick:()=>i(e),children:`Unban`})})]},e.ip))})})]}),(0,$.jsx)(q,{justify:`flex-end`,gap:`0px`,children:(0,$.jsx)(_,{variant:`secondary`,size:`sm`,onClick:e,children:`Close`})})]})}),(0,$.jsx)(I,{open:r!==null,onClose:()=>i(null),onConfirm:async()=>{r&&(await n.mutateAsync(r.ip),v.success(`Unbanned ${r.ip}.`),i(null))},title:`Lift this ban?`,message:r?`Lift the ban on ${r.ip}? The address can reach the gateway again immediately.`:`Lift this ban?`,confirmLabel:`Unban`,danger:!0})]})}var st=e=>(e??0).toLocaleString(),ct=e=>e?new Date(e).toLocaleString():`—`;function lt({keyId:e}){let t=_e(),[n,r]=(0,Q.useState)(null),[i,a]=(0,Q.useState)(!1);return(0,$.jsxs)(`div`,{className:`flex min-w-0 items-center gap-4 rounded-control border border-inner-border bg-surface-2 px-5 py-4 text-text-tertiary`,children:[(0,$.jsx)(`code`,{title:i&&n?n:void 0,className:`min-w-0 flex-auto overflow-hidden font-mono text-caption text-text-primary text-ellipsis whitespace-nowrap`,children:i&&n?n:`••••••••••••••••`}),(0,$.jsx)(_,{variant:`secondary`,size:`sm`,icon:i?(0,$.jsx)(x,{size:13}):(0,$.jsx)(S,{size:13}),loading:t.isPending,disabled:t.isPending,onClick:()=>{if(n!==null){a(e=>!e);return}t.mutate({keyId:e},{onSuccess:e=>{r(e.secret),a(!0)},onError:e=>v.error(F(e,`Could not reveal the key.`))})},children:i?`Hide`:`Reveal`}),i&&n?(0,$.jsx)(O,{value:n,size:`sm`,variant:`secondary`,label:`Copy`,copiedLabel:`Copied`}):null]})}function ut({label:e,value:t}){return(0,$.jsxs)(`div`,{className:`min-w-0 flex-1 basis-60 rounded-control border border-inner-border bg-surface-2 px-5 py-3`,children:[(0,$.jsx)(`dt`,{className:`text-badge font-normal tracking-label text-text-tertiary uppercase`,children:e}),(0,$.jsx)(`dd`,{className:`mt-1 text-caption font-normal text-text-primary tabular-nums`,children:t==null?`Unlimited`:st(t)})]})}function dt(e){return e?{title:`Regenerate this key?`,message:`This rotates the key credential. Any client still using the current secret starts getting 401s immediately, and the old secret cannot be recovered. The handoff link is re-pointed to reveal the new key.`,confirmLabel:`Regenerate key`}:{title:`Regenerate this link?`,message:`This replaces the link URL; the current URL stops resolving immediately. Recipients keep the keys they already generated and keep working — but no one can enroll through the old link again.`,confirmLabel:`Regenerate link`}}function ft({consumed:e,budget:t}){let n=t==null||t<=0,r=n?100:Math.min(100,e/t*100),i=!n&&e>=t;return(0,$.jsxs)(`div`,{className:`grid flex-auto basis-full gap-2.5 rounded-control border border-inner-border bg-surface-2 px-5 py-3.5`,children:[(0,$.jsxs)(`div`,{className:`flex items-baseline justify-between gap-4 text-badge tracking-label text-text-tertiary uppercase`,children:[(0,$.jsx)(`span`,{children:`Lifetime budget`}),(0,$.jsx)(`span`,{className:`text-caption font-normal text-text-primary tabular-nums normal-case tracking-normal`,children:n?`${V(e)} · Unlimited`:`${V(e)} / ${V(t)}`})]}),(0,$.jsx)(`div`,{className:`h-3 overflow-hidden rounded bg-inner-border`,role:`progressbar`,"aria-label":`Lifetime token budget consumed`,"aria-valuemin":0,"aria-valuemax":100,"aria-valuenow":Math.round(r),"aria-valuetext":n?`${V(e)} tokens consumed, no lifetime budget set`:`${V(e)} of ${V(t)} tokens consumed`,children:(0,$.jsx)(`div`,{className:i?`h-full rounded bg-status-danger`:`h-full rounded bg-status-success`,style:{width:`${r}%`}})})]})}function pt(e){return e.filter(e=>e.revokedAt===null).length}function mt(e){return e.reduce((e,t)=>e+t.allTime.totalTokens,0)}function ht({consumed:e,budget:t}){if(t==null)return(0,$.jsx)(`div`,{className:`grid flex-auto basis-full gap-2.5 rounded-control border border-inner-border bg-surface-2 px-5 py-3.5`,children:(0,$.jsxs)(`div`,{className:`flex items-baseline justify-between gap-4 text-badge tracking-label text-text-tertiary uppercase`,children:[(0,$.jsx)(`span`,{children:`Total quota used`}),(0,$.jsx)(`span`,{className:`text-caption font-normal text-text-primary tabular-nums normal-case tracking-normal`,children:V(e)})]})});let n=t>0?Math.min(100,e/t*100):0,r=t>0?Math.max(0,100-e/t*100):100,i=Math.max(0,t-e),a=Ae(e>=t?0:r),o=e>=t;return(0,$.jsxs)(`div`,{className:`grid flex-auto basis-full gap-2.5 rounded-control border border-inner-border bg-surface-2 px-5 py-3.5`,children:[(0,$.jsxs)(`div`,{className:`flex items-baseline justify-between gap-4 text-badge tracking-label text-text-tertiary uppercase`,children:[(0,$.jsx)(`span`,{children:`Total quota`}),(0,$.jsxs)(`span`,{className:`text-caption font-normal text-text-primary tabular-nums normal-case tracking-normal`,children:[V(e),` / `,V(t)]}),(0,$.jsx)(`span`,{className:`border rounded-chip px-4 py-0.5 text-caption font-bold whitespace-nowrap tabular-nums`,style:{color:a.text,borderColor:a.text},children:o?`Exhausted`:`${V(i)} left`})]}),(0,$.jsx)(`div`,{className:`h-3 overflow-hidden rounded bg-inner-border`,role:`progressbar`,"aria-label":`Total quota consumed`,"aria-valuemin":0,"aria-valuemax":100,"aria-valuenow":Math.round(n),"aria-valuetext":`${V(e)} of ${V(t)} tokens consumed`,children:(0,$.jsx)(`div`,{className:o?`h-full rounded bg-status-danger`:`h-full rounded`,style:{width:`${n}%`,background:o?void 0:a.bar}})})]})}function gt({parentId:e,childId:t}){let n=Ee(e,t);if(n.isPending)return(0,$.jsx)(E,{label:`Loading activity…`,compact:!0});if(n.isError)return(0,$.jsx)(T,{compact:!0,message:F(n.error,`Could not load activity.`),onRetry:()=>void n.refetch()});let r=n.data;return r?(0,$.jsxs)(`div`,{className:`grid min-w-0 gap-6 border-t border-inner-border bg-surface-1 px-6 py-5`,children:[(0,$.jsxs)(`section`,{children:[(0,$.jsx)(`h4`,{className:`mb-3 text-caption font-bold text-text-primary`,children:`Top models`}),r.models.length?(0,$.jsx)(`div`,{className:`data-table-container`,children:(0,$.jsxs)(`table`,{className:`data-table text-caption`,children:[(0,$.jsx)(`thead`,{children:(0,$.jsxs)(`tr`,{children:[(0,$.jsx)(`th`,{scope:`col`,children:`Model`}),(0,$.jsx)(`th`,{scope:`col`,children:`Today`}),(0,$.jsx)(`th`,{scope:`col`,children:`Errors`}),(0,$.jsx)(`th`,{scope:`col`,children:`Tokens`})]})}),(0,$.jsx)(`tbody`,{children:r.models.map((e,t)=>(0,$.jsxs)(`tr`,{children:[(0,$.jsx)(`td`,{children:e.modelId}),(0,$.jsx)(`td`,{children:st(e.todayRequests)}),(0,$.jsx)(`td`,{children:st(e.todayErrors)}),(0,$.jsx)(`td`,{children:V(e.todayTokens)})]},`${e.providerId??``}-${e.modelId}-${t}`))})]})}):(0,$.jsx)(`p`,{className:`py-3 text-caption text-text-tertiary`,children:`No model activity yet.`})]}),(0,$.jsxs)(`section`,{children:[(0,$.jsx)(`h4`,{className:`mb-3 text-caption font-bold text-text-primary`,children:`Recent requests`}),r.requests.length?(0,$.jsx)(`div`,{className:`data-table-container`,children:(0,$.jsxs)(`table`,{className:`data-table text-caption`,children:[(0,$.jsx)(`thead`,{children:(0,$.jsxs)(`tr`,{children:[(0,$.jsx)(`th`,{scope:`col`,children:`Model`}),(0,$.jsx)(`th`,{scope:`col`,children:`Status`}),(0,$.jsx)(`th`,{scope:`col`,children:`Tokens`}),(0,$.jsx)(`th`,{scope:`col`,children:`Time`})]})}),(0,$.jsx)(`tbody`,{children:r.requests.map(e=>(0,$.jsxs)(`tr`,{children:[(0,$.jsxs)(`td`,{children:[e.providerId??`Provider`,`/`,e.modelId??`Model`]}),(0,$.jsxs)(`td`,{children:[e.status,e.httpStatus?` · ${e.httpStatus}`:``]}),(0,$.jsx)(`td`,{children:V(e.totalTokens)}),(0,$.jsx)(`td`,{children:ct(e.startedAt)})]},e.requestId))})]})}):(0,$.jsx)(`p`,{className:`py-3 text-caption text-text-tertiary`,children:`No recent requests.`})]})]}):null}function _t({parent:e,onSecretRevealed:t}){let[n,r]=(0,Q.useState)(null),i=ve(),a=we(),o=Te(),s=xe(e.id),c=e.keyMode!==`share`,l=J(c?null:e.id),u=l.data??[];(0,Q.useEffect)(()=>{r(null)},[e.id]);let d=i.isPending||a.isPending,[p,h]=(0,Q.useState)(!1),g=async()=>{if(c){let n=await a.mutateAsync({keyId:e.id});t?.(n.secret);return}await i.mutateAsync({keyId:e.id,regenerate:!0}),v.success(`Link regenerated; the previous URL no longer works.`)},y=()=>{i.mutate({keyId:e.id},{onError:e=>v.error(F(e,`Could not create link.`))})},b=s.data?.url??null,x=dt(c);return(0,$.jsxs)(`div`,{className:`grid min-w-0 gap-6`,children:[(0,$.jsx)(`section`,{"aria-label":`Link`,children:s.isPending?(0,$.jsx)(E,{label:`Loading link…`,compact:!0}):s.isError?(0,$.jsx)(T,{compact:!0,message:F(s.error,`Could not load the link.`),onRetry:()=>void s.refetch()}):b?(0,$.jsxs)(`div`,{className:`flex min-w-0 flex-wrap items-center gap-4 rounded-control border border-inner-border bg-surface-2 px-5 py-4 text-text-tertiary`,children:[(0,$.jsx)(D,{size:14,"aria-hidden":`true`,className:`shrink-0`}),(0,$.jsx)(`code`,{title:b,className:`min-w-0 flex-auto overflow-hidden font-mono text-caption text-text-primary text-ellipsis whitespace-nowrap`,children:b}),(0,$.jsx)(O,{value:b,size:`sm`,variant:`secondary`,label:`Copy`,copiedLabel:`Copied`}),(0,$.jsx)(_,{variant:`secondary`,size:`sm`,icon:(0,$.jsx)(Ne,{size:13}),loading:d,disabled:d,onClick:()=>h(!0),children:`Regenerate`})]}):(0,$.jsxs)(`div`,{className:`flex min-w-0 flex-wrap items-center gap-4 rounded-control border border-inner-border bg-surface-2 px-5 py-4 text-text-tertiary`,children:[(0,$.jsx)(D,{size:14,"aria-hidden":`true`,className:`shrink-0`}),(0,$.jsx)(`span`,{className:`flex-auto text-caption text-text-tertiary`,children:`No link yet.`}),(0,$.jsx)(_,{variant:`primary`,size:`sm`,loading:d,disabled:d,onClick:y,children:`Create link`})]})}),c?(0,$.jsxs)(`section`,{"aria-label":`API key`,className:`grid gap-3`,children:[(0,$.jsx)(`div`,{className:`flex items-center justify-between gap-4`,children:(0,$.jsx)(`strong`,{className:`text-caption`,children:`API key`})}),(0,$.jsx)(lt,{keyId:e.id})]}):null,c?(0,$.jsxs)(`section`,{"aria-label":`Usage`,className:`grid gap-3`,children:[(0,$.jsx)(`div`,{className:`flex items-center justify-between gap-4`,children:(0,$.jsx)(`strong`,{className:`text-caption`,children:`Usage`})}),(0,$.jsxs)(`dl`,{className:`m-0 flex flex-wrap gap-3`,children:[(0,$.jsxs)(`div`,{className:`min-w-0 flex-1 basis-60 rounded-control border border-inner-border bg-surface-2 px-5 py-3`,children:[(0,$.jsx)(`dt`,{className:`text-badge font-normal tracking-label text-text-tertiary uppercase`,children:`Lifetime tokens`}),(0,$.jsx)(`dd`,{className:`mt-1 text-caption font-normal text-text-primary tabular-nums`,children:V(e.tokensConsumed)})]}),(0,$.jsx)(ut,{label:`Requests / min`,value:e.requestsPerMinute}),(0,$.jsx)(ut,{label:`Daily tokens`,value:e.dailyTokenLimit}),(0,$.jsx)(ut,{label:`Monthly tokens`,value:e.monthlyTokenLimit}),(0,$.jsx)(ft,{consumed:e.tokensConsumed,budget:e.lifetimeTokenBudget})]})]}):(0,$.jsxs)($.Fragment,{children:[(0,$.jsxs)(`section`,{"aria-label":`Total quota`,className:`grid gap-3`,children:[(0,$.jsx)(`div`,{className:`flex items-center justify-between gap-4`,children:(0,$.jsx)(`strong`,{className:`text-caption`,children:`Total quota`})}),(0,$.jsx)(ht,{consumed:mt(u),budget:e.lifetimeTokenBudget})]}),(0,$.jsxs)(`section`,{"aria-label":`Recipients`,className:`grid gap-3`,children:[(0,$.jsxs)(`div`,{className:`flex items-center justify-between gap-4`,children:[(0,$.jsx)(`strong`,{className:`text-caption`,children:`Recipients`}),(0,$.jsxs)(`span`,{className:`mr-2 ml-auto text-caption text-text-tertiary tabular-nums`,children:[`Active users: `,pt(u)]}),(0,$.jsx)(_,{variant:`ghost`,size:`sm`,onClick:()=>void l.refetch(),icon:(0,$.jsx)(j,{size:13,className:l.isFetching?`animate-spin`:void 0}),children:`Refresh`})]}),l.isPending&&!l.data?(0,$.jsx)(E,{label:`Loading recipients…`,compact:!0}):l.isError?(0,$.jsx)(T,{compact:!0,message:F(l.error,`Could not load recipients.`),onRetry:()=>void l.refetch()}):u.length===0?(0,$.jsx)(ee,{compact:!0,title:`No recipients yet`,message:`Keys generated through this link appear here with their masked client IP and token usage.`}):(0,$.jsx)(`div`,{className:`data-table-container max-h-[46vh]`,children:(0,$.jsxs)(`table`,{className:`data-table text-caption`,children:[(0,$.jsx)(`thead`,{children:(0,$.jsxs)(`tr`,{children:[(0,$.jsx)(`th`,{scope:`col`,className:`w-12`,"aria-label":`Expand`}),(0,$.jsx)(`th`,{scope:`col`,children:`Key`}),(0,$.jsx)(`th`,{scope:`col`,children:`Status`}),(0,$.jsx)(`th`,{scope:`col`,children:`Today`}),(0,$.jsx)(`th`,{scope:`col`,children:`Lifetime`}),(0,$.jsx)(`th`,{scope:`col`,children:`Last activity`}),(0,$.jsx)(`th`,{scope:`col`,className:`text-right whitespace-nowrap`,children:`Actions`})]})}),(0,$.jsx)(`tbody`,{children:u.map(t=>{let i=n===t.id;return(0,$.jsxs)(Q.Fragment,{children:[(0,$.jsxs)(`tr`,{children:[(0,$.jsx)(`td`,{children:(0,$.jsx)(`button`,{type:`button`,className:`grid h-11 w-11 place-items-center rounded-chip border-0 bg-transparent p-0 text-text-secondary hover:bg-hover hover:text-text-primary`,"aria-expanded":i,"aria-label":i?`Collapse recipient`:`Expand recipient`,onClick:()=>r(i?null:t.id),children:i?(0,$.jsx)(f,{size:14}):(0,$.jsx)(m,{size:14})})}),(0,$.jsxs)(`td`,{children:[(0,$.jsxs)(`code`,{children:[t.label||t.keyPrefix||`key`,`…`]}),(0,$.jsx)(`div`,{className:`mt-1 text-badge text-text-tertiary wrap-anywhere`,children:t.issuedClientIp??`IP hidden`})]}),(0,$.jsx)(`td`,{children:t.revokedAt?`revoked`:`active`}),(0,$.jsx)(`td`,{children:V(t.today.totalTokens)}),(0,$.jsx)(`td`,{children:V(t.allTime.totalTokens)}),(0,$.jsx)(`td`,{children:ct(t.lastUsedAt)}),(0,$.jsx)(`td`,{className:`text-right whitespace-nowrap`,children:(0,$.jsx)(_,{variant:`danger`,size:`sm`,disabled:o.isPending||!!t.revokedAt,onClick:()=>o.mutate({parentKeyId:e.id,childKeyId:t.id},{onError:e=>v.error(F(e,`Could not revoke key.`))}),children:`Revoke`})})]}),i?(0,$.jsx)(`tr`,{children:(0,$.jsx)(`td`,{colSpan:7,className:`p-0`,children:(0,$.jsx)(gt,{parentId:e.id,childId:t.id})})}):null]},t.id)})})]})})]})]}),(0,$.jsx)(I,{open:p,onClose:()=>h(!1),onConfirm:g,title:x.title,message:x.message,confirmLabel:x.confirmLabel,danger:!0})]})}function vt({parent:e,onClose:t,onSecretRevealed:n}){let r=e.keyMode!==`share`;return(0,$.jsx)(k,{open:!0,onClose:t,title:`Sharing — ${e.label||`API key`}`,description:r?`One stable handoff link for this key. Regenerating rotates the key itself.`:`One stable enrollment link. Regenerating replaces the URL; recipients keep the keys they already generated.`,size:`lg`,children:(0,$.jsx)(_t,{parent:e,onSecretRevealed:n})})}function yt(){let e=Y(),t=de(),n=Se(),r=ye(),i=Ce(),a=we(),o=_e(),s=be(),{copy:c}=B(),[l,u]=(0,Q.useState)(!1),[d,f]=(0,Q.useState)(null),[p,m]=(0,Q.useState)(null),[g,b]=(0,Q.useState)(null),[x,S]=(0,Q.useState)(null),[C,w]=(0,Q.useState)(null),[ne,D]=(0,Q.useState)(!1),[O,j]=(0,Q.useState)(null),[M,ie]=(0,Q.useState)(null),ae=e.data??[],N=t.data?.isPlatformAdmin??!1,oe=e=>{S(null),w(e)},se=async e=>{try{let{secret:t}=await o.mutateAsync({keyId:e.id});await c(t)?v.success(`API key copied`):v.error(`Clipboard unavailable on this origin`)}catch(e){v.error(F(e,`Could not reveal API key.`))}},P=dt(!0);return(0,$.jsxs)(z,{children:[(0,$.jsx)(R,{title:`API Credentials`,subtitle:`Manage tenant API keys, model access, budgets, and sharing.`,icon:(0,$.jsx)(te,{size:16}),action:(0,$.jsxs)(`div`,{className:`flex items-center gap-4`,children:[N?(0,$.jsx)(_,{variant:`secondary`,size:`sm`,onClick:()=>D(!0),icon:(0,$.jsx)(Ie,{size:13}),children:`Banned Users`}):null,(0,$.jsx)(_,{variant:`primary`,size:`sm`,onClick:()=>{S(null),u(!0)},icon:(0,$.jsx)(re,{size:13}),children:`Create Key`})]})}),(0,$.jsx)(L,{children:e.isPending&&!e.data?(0,$.jsx)(E,{label:`Loading API keys…`}):e.isError&&!e.data?(0,$.jsx)(T,{message:F(e.error,`Could not load credentials.`),onRetry:()=>void e.refetch()}):ae.length===0?(0,$.jsx)(ee,{title:`No API keys issued`,message:`Generate a secret key to authenticate your SDK or CLI client against the gateway.`,icon:(0,$.jsx)(te,{size:20})}):(0,$.jsx)(`div`,{className:`scroll-region max-h-280 pr-1`,children:(0,$.jsx)(h,{items:ae,label:`API credentials`,disabled:s.isPending,onReorder:e=>s.mutate(e,{onError:e=>v.error(F(e,`Could not save the new order.`))}),renderItem:e=>(0,$.jsxs)(`div`,{className:`api-key-card flex items-start justify-between gap-6 px-6 py-4 rounded-chip border border-inner-border bg-surface-2 max-medium:flex-col max-medium:items-stretch max-medium:gap-4 max-medium:p-5 [&>div:last-child]:flex-wrap`,children:[(0,$.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,$.jsxs)(`div`,{className:`flex items-center gap-4 flex-wrap`,children:[(0,$.jsx)(`strong`,{className:`text-body`,children:e.label||`Unnamed credential`}),(0,$.jsx)(U,{tone:e.revokedAt?`err`:e.enabled?`ok`:`warn`,children:e.revokedAt?`revoked`:e.enabled?`enabled`:`disabled`}),(0,$.jsx)(U,{tone:e.keyMode===`share`?`accent`:`default`,children:e.keyMode===`share`?`share template`:`personal`}),e.isDefault?(0,$.jsx)(U,{tone:`warn`,title:`Required by the gateway. Rotate it; it cannot be deleted.`,children:`default · rotate only`}):null]}),(0,$.jsxs)(`div`,{className:`api-key-facts flex items-center flex-wrap gap-2 mt-3 min-w-0 max-medium:grid max-medium:grid-cols-3 max-medium:gap-2 max-medium:mt-0`,children:[(0,$.jsxs)(`span`,{className:`inline-flex items-center gap-2.5 px-3.5 py-1 border border-inner-border rounded-chip bg-surface-1 text-text-secondary text-badge leading-normal whitespace-nowrap max-medium:justify-between`,children:[(0,$.jsx)(`span`,{className:`text-text-tertiary text-micro font-semibold uppercase tracking-label`,children:`Created`}),new Date(e.createdAt).toLocaleDateString()]}),(0,$.jsxs)(`span`,{className:`inline-flex items-center gap-2.5 px-3.5 py-1 border border-inner-border rounded-chip bg-surface-1 text-text-secondary text-badge leading-normal whitespace-nowrap max-medium:justify-between`,children:[(0,$.jsx)(`span`,{className:`text-text-tertiary text-micro font-semibold uppercase tracking-label`,children:e.keyMode===`share`?`Total usage`:`Usage`}),V(e.tokensConsumed)]}),(0,$.jsxs)(`span`,{className:`inline-flex items-center gap-2.5 px-3.5 py-1 border border-inner-border rounded-chip bg-surface-1 text-text-secondary text-badge leading-normal whitespace-nowrap max-medium:justify-between`,children:[(0,$.jsx)(`span`,{className:`text-text-tertiary text-micro font-semibold uppercase tracking-label`,children:`RPM`}),H(e.requestsPerMinute)]})]}),(0,$.jsxs)(`div`,{className:`api-key-facts flex items-center flex-wrap gap-2 mt-3 min-w-0 max-medium:grid max-medium:grid-cols-3 max-medium:gap-2 max-medium:mt-0`,children:[(0,$.jsxs)(`span`,{className:`inline-flex items-center gap-2.5 px-3.5 py-1 border border-inner-border rounded-chip bg-surface-1 text-text-secondary text-badge leading-normal whitespace-nowrap max-medium:justify-between`,children:[(0,$.jsx)(`span`,{className:`text-text-tertiary text-micro font-semibold uppercase tracking-label`,children:`Daily`}),H(e.dailyTokenLimit)]}),(0,$.jsxs)(`span`,{className:`inline-flex items-center gap-2.5 px-3.5 py-1 border border-inner-border rounded-chip bg-surface-1 text-text-secondary text-badge leading-normal whitespace-nowrap max-medium:justify-between`,children:[(0,$.jsx)(`span`,{className:`text-text-tertiary text-micro font-semibold uppercase tracking-label`,children:`Monthly`}),H(e.monthlyTokenLimit)]}),(0,$.jsxs)(`span`,{className:`inline-flex items-center gap-2.5 px-3.5 py-1 border border-inner-border rounded-chip bg-surface-1 text-text-secondary text-badge leading-normal whitespace-nowrap max-medium:justify-between`,children:[(0,$.jsx)(`span`,{className:`text-text-tertiary text-micro font-semibold uppercase tracking-label`,children:`One-time`}),H(e.lifetimeTokenBudget)]}),(0,$.jsxs)(`span`,{className:`inline-flex items-center gap-2.5 px-3.5 py-1 border border-inner-border rounded-chip bg-surface-1 text-text-secondary text-badge leading-normal whitespace-nowrap max-medium:justify-between`,children:[(0,$.jsx)(`span`,{className:`text-text-tertiary text-micro font-semibold uppercase tracking-label`,children:`Concurrent`}),H(e.maxConcurrentRequests)]}),(0,$.jsxs)(`span`,{className:`inline-flex items-center gap-2.5 px-3.5 py-1 border border-inner-border rounded-chip bg-surface-1 text-text-secondary text-badge leading-normal whitespace-nowrap max-medium:justify-between`,children:[(0,$.jsx)(`span`,{className:`text-text-tertiary text-micro font-semibold uppercase tracking-label`,children:e.modelAccessMode===`blacklist`?`Blocked`:`Models`}),e.modelAccessMode===`blacklist`?e.modelList?.length??0:e.modelList?.length?e.modelList.length:`All`]})]})]}),(0,$.jsxs)(`div`,{className:`flex gap-3 shrink-0 flex-wrap`,children:[e.revokedAt?null:(0,$.jsx)(G,{checked:e.enabled,disabled:r.isPending,id:`api-key-enabled-${e.id}`,label:``,"aria-label":`${e.enabled?`Disable`:`Enable`} ${e.label||`API credential`}`,onChange:t=>r.mutate({keyId:e.id,request:{enabled:t}},{onSuccess:()=>v.success(`${e.label||`API credential`} ${t?`enabled`:`disabled`}`),onError:e=>v.error(F(e,`Could not update credential state.`))})}),(0,$.jsx)(_,{variant:`secondary`,size:`sm`,icon:(0,$.jsx)(A,{size:13}),onClick:()=>{S(null),f(e)},children:`Edit`}),!e.revokedAt&&e.keyMode!==`share`?(0,$.jsx)(_,{variant:`secondary`,size:`sm`,icon:(0,$.jsx)(y,{size:13}),onClick:()=>void se(e),disabled:o.isPending,children:`Copy`}):null,!e.revokedAt&&(0,$.jsx)(_,{variant:`secondary`,size:`sm`,icon:(0,$.jsx)(Fe,{size:13}),onClick:()=>oe(e),children:e.keyMode===`share`?`Recipients`:`Share`}),!e.revokedAt&&e.keyMode!==`share`&&(0,$.jsx)(_,{variant:`secondary`,size:`sm`,icon:(0,$.jsx)(Ne,{size:13}),onClick:()=>{S(null),b(e)},disabled:a.isPending,children:`Rotate`}),!e.revokedAt&&!e.isDefault&&(0,$.jsx)(_,{variant:`danger`,size:`sm`,icon:(0,$.jsx)(ce,{size:13}),onClick:()=>m(e),disabled:i.isPending,children:`Revoke`})]})]})})})}),(0,$.jsx)(k,{open:l,onClose:()=>u(!1),title:`Create API Key`,description:`Create a tenant-scoped credential. Choose model access, permissions, limits, and optional blocked client routers.`,size:`lg`,footer:O,children:(0,$.jsx)(rt,{mode:`create`,record:null,busy:n.isPending,onClose:()=>u(!1),actionsRef:j,onDone:e=>{n.mutate(e,{onSuccess:t=>{u(!1),S(tt(e.keyMode,t.secret)),v.success(`API key created`)},onError:e=>v.error(F(e,`Could not create API key.`))})}})}),(0,$.jsx)(k,{open:d!==null,onClose:()=>f(null),title:`Edit API Key`,description:`Update the key's model access, permissions, limits, share notes, and optional public popup.`,size:`lg`,footer:M,children:(0,$.jsx)(rt,{mode:`edit`,record:d,busy:r.isPending,onClose:()=>f(null),actionsRef:ie,onDone:e=>{d&&r.mutate({keyId:d.id,request:e},{onSuccess:t=>{f(null),S(tt(e.keyMode,t.secret)),v.success(`API key updated`)},onError:e=>v.error(F(e,`Could not update API key.`))})}})}),C?(0,$.jsx)(vt,{parent:C,onClose:()=>w(null),onSecretRevealed:e=>{w(null),S(e)}}):null,(0,$.jsx)(I,{open:g!==null,onClose:()=>b(null),onConfirm:async()=>{if(!g)return;let e=await a.mutateAsync({keyId:g.id});S(e.secret),v.success(`Rotated ${g.label||g.id}.`),b(null)},title:P.title,message:P.message,confirmLabel:P.confirmLabel,danger:!0}),(0,$.jsx)(I,{open:p!==null,onClose:()=>m(null),onConfirm:async()=>{p&&(await i.mutateAsync(p.id),v.success(`Revoked ${p.label||p.id}.`),m(null))},title:`Revoke API key?`,message:p?`Revoke ${p.label||p.id}? Clients using it will be rejected.${p.keyMode===`share`?` All child keys and enrollment links will also be revoked.`:``}`:`Revoke this API key?`,confirmLabel:`Revoke`,danger:!0}),ne?(0,$.jsx)(ot,{onClose:()=>D(!1)}):null,(0,$.jsx)(it,{secret:x,onClose:()=>S(null)})]})}function bt(){let e=se(),t=N(),n=e.data;if(e.isPending)return(0,$.jsx)(E,{label:`Loading compression settings…`});if(e.isError||!n)return(0,$.jsx)(T,{message:F(e.error,`Failed to load compression settings`),onRetry:()=>void e.refetch()});let r=n.rtkPruneEnabled,i=n.ponyTailEnabled;return(0,$.jsxs)(z,{children:[(0,$.jsx)(R,{title:`Compression`,subtitle:`Shrink requests before they reach the provider`,icon:(0,$.jsx)(a,{size:16})}),(0,$.jsx)(L,{children:(0,$.jsxs)(K,{gap:`14px`,children:[(0,$.jsxs)(`div`,{className:`flex items-start gap-x-8 gap-y-5`,children:[(0,$.jsxs)(`div`,{className:`flex gap-5 min-w-0 flex-auto`,children:[(0,$.jsx)(Pe,{size:16,className:`shrink-0 mt-1 text-text-secondary`}),(0,$.jsxs)(`div`,{className:`min-w-0`,children:[(0,$.jsx)(`label`,{htmlFor:`compression-rtk`,children:(0,$.jsx)(q,{gap:`6px`,className:`text-body font-semibold`,children:`Prune tool output (RTK)`})}),(0,$.jsx)(`div`,{className:`text-badge text-text-tertiary wrap-anywhere max-w-full`,children:`Compact bulky tool results (diffs, grep dumps, listings) in the message history so a long conversation stops re-sending megabytes of raw output. Higher strength compresses more aggressively.`})]})]}),(0,$.jsxs)(q,{gap:`8px`,className:`shrink-0 items-center ml-auto`,children:[(0,$.jsx)(p,{id:`compression-rtk-level`,"aria-label":`RTK strength`,value:n.rtkPruneLevel,onValueChange:e=>t.mutate({rtkPruneLevel:e===`lite`||e===`full`||e===`ultra`?e:`full`},{onError:e=>v.error(F(e,`Could not update RTK strength.`))}),options:[{value:`lite`,label:`Lite`},{value:`full`,label:`Full`},{value:`ultra`,label:`Ultra`}]}),(0,$.jsx)(G,{id:`compression-rtk`,"aria-label":`Enable RTK prune`,checked:r,onChange:e=>t.mutate({rtkPruneEnabled:e},{onError:e=>v.error(F(e,`Could not update RTK prune.`))})})]})]}),(0,$.jsxs)(`div`,{className:`flex items-start gap-x-8 gap-y-5`,children:[(0,$.jsxs)(`div`,{className:`flex gap-5 min-w-0 flex-auto`,children:[(0,$.jsx)(P,{size:16,className:`shrink-0 mt-1 text-text-secondary`}),(0,$.jsxs)(`div`,{className:`min-w-0`,children:[(0,$.jsx)(`label`,{htmlFor:`compression-ponytail`,children:(0,$.jsx)(q,{gap:`6px`,className:`text-body font-semibold`,children:`PonyTail (system directive)`})}),(0,$.jsx)(`div`,{className:`text-badge text-text-tertiary wrap-anywhere max-w-full`,children:`Appends a minimal-code directive to the system content, biasing the model toward the smallest working change. Higher strength pushes harder toward deletion over addition.`})]})]}),(0,$.jsxs)(q,{gap:`8px`,className:`shrink-0 items-center ml-auto`,children:[(0,$.jsx)(p,{id:`compression-ponytail-level`,"aria-label":`PonyTail strength`,value:n.ponyTailLevel,onValueChange:e=>t.mutate({ponyTailLevel:e===`lite`||e===`full`||e===`ultra`?e:`full`},{onError:e=>v.error(F(e,`Could not update PonyTail strength.`))}),options:[{value:`lite`,label:`Lite`},{value:`full`,label:`Full`},{value:`ultra`,label:`Ultra`}]}),(0,$.jsx)(G,{id:`compression-ponytail`,"aria-label":`Enable PonyTail`,checked:i,onChange:e=>t.mutate({ponyTailEnabled:e},{onError:e=>v.error(F(e,`Could not update PonyTail.`))})})]})]}),r||i?(0,$.jsxs)(`p`,{role:`note`,className:`flex gap-4 items-start m-0 py-4 px-5 rounded-inset border border-orange bg-orange-soft text-text-secondary text-badge`,children:[(0,$.jsx)(w,{size:14,className:`shrink-0 mt-0.5 text-orange`}),(0,$.jsx)(`span`,{children:`Your AI may produce degraded quality while compression is active: it sees a shortened history and a minimal-code directive, so fewer tokens but a chance of missing detail. Turn it off if answers get worse.`})]}):null]})})]})}function xt(){let{copied:e,copy:t}=B(2e3),n=`${window.location.origin}/v1`,r=async()=>{if(await t(n)){v.success(`Gateway endpoint copied`,n);return}v.error(`Clipboard unavailable on this origin`)};return(0,$.jsxs)(z,{children:[(0,$.jsx)(R,{title:`Gateway Endpoint`,subtitle:`Universal OpenAI & Anthropic Wire API Endpoint Compatible`,icon:(0,$.jsx)(g,{size:16})}),(0,$.jsx)(L,{children:(0,$.jsxs)(K,{gap:`10px`,children:[(0,$.jsxs)(`div`,{className:`nested-panel flex items-center gap-4 rounded-control border border-inner-border bg-code-surface px-7 py-5 font-mono text-body font-semibold tabular-nums`,children:[(0,$.jsx)(`code`,{className:`min-w-0 flex-1 truncate`,title:n,children:n}),(0,$.jsx)(_,{variant:`secondary`,size:`sm`,icon:e?(0,$.jsx)(d,{size:13}):(0,$.jsx)(y,{size:13}),"aria-label":`Copy endpoint`,title:`Copy endpoint`,onClick:()=>void r(),className:`shrink-0`,children:e?`Copied`:`Copy`})]}),(0,$.jsxs)(`p`,{className:`text-caption leading-normal text-text-secondary`,children:[`Point any SDK, CLI, or agent (e.g. OpenCode, Pi, OMP, Kilo, Cline, Droid and more clients) to this same-origin base URL.`,(0,$.jsxs)(`span`,{className:`mt-1 block text-text-tertiary`,children:[`You may not put `,(0,$.jsx)(`code`,{className:`font-mono`,children:`/v1`}),` on Claude Code — the CLI appends it itself.`]})]})]})})]})}function St(){return(0,$.jsxs)(K,{gap:`16px`,children:[(0,$.jsx)(xt,{}),(0,$.jsx)(bt,{}),(0,$.jsx)(yt,{})]})}export{St as default};