cartethyia 2.5.3 → 2.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dashboard/assets/{BannedPage-hfCzi-Ux.js → BannedPage-C2orBsI2.js} +1 -1
- package/dist/dashboard/assets/BannedPage-C2orBsI2.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-DUcZ6pMB.js +1 -0
- package/dist/dashboard/assets/CliToolDetailPage-DUcZ6pMB.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-DEDyKBlC.js → CliToolsPage-BV6pxVQo.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-BV6pxVQo.js.gz +0 -0
- package/dist/dashboard/assets/{CombosPage-CfuYiQ8Z.js → CombosPage-Bsan4679.js} +1 -1
- package/dist/dashboard/assets/CombosPage-Bsan4679.js.gz +0 -0
- package/dist/dashboard/assets/{ConsoleLogPage-bsLuZmYH.js → ConsoleLogPage-CW-wPmJ6.js} +1 -1
- package/dist/dashboard/assets/ConsoleLogPage-CW-wPmJ6.js.gz +0 -0
- package/dist/dashboard/assets/{CustomizationPage-_UWhwziJ.js → CustomizationPage-0p8uX7vB.js} +1 -1
- package/dist/dashboard/assets/CustomizationPage-0p8uX7vB.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-BjqSMfko.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-BjqSMfko.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-DDVIfZGW.js → LoginPage-BvzCx8Kr.js} +1 -1
- package/dist/dashboard/assets/LoginPage-BvzCx8Kr.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-CWkTUtpX.js → ModelPicker-BY1aKEkP.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-BY1aKEkP.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-DOQpy1y0.js +1 -0
- package/dist/dashboard/assets/OverviewPage-DOQpy1y0.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-ecxyUMzt.js +3 -0
- package/dist/dashboard/assets/ProviderDetailPage-ecxyUMzt.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-Js9jUPSb.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-Js9jUPSb.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-J3TBi3Kx.js +7 -0
- package/dist/dashboard/assets/ProxyPage-J3TBi3Kx.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-C1gZS6xr.js +1 -0
- package/dist/dashboard/assets/QuotaPage-C1gZS6xr.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CS86ho5X.js +1 -0
- package/dist/dashboard/assets/SettingsPage-CS86ho5X.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-D6pmQ86L.js → SetupPage-DDbMQsoo.js} +1 -1
- package/dist/dashboard/assets/SetupPage-DDbMQsoo.js.gz +0 -0
- package/dist/dashboard/assets/{SortableList-DWvKiuSl.js → SortableList-CB3Z7VTf.js} +1 -1
- package/dist/dashboard/assets/SortableList-CB3Z7VTf.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-CmkUrgdj.js → StudioPage-D95UCPHI.js} +3 -3
- package/dist/dashboard/assets/StudioPage-D95UCPHI.js.gz +0 -0
- package/dist/dashboard/assets/{UsagePage-B47uOydr.js → UsagePage-Cnae9SWA.js} +13 -13
- package/dist/dashboard/assets/UsagePage-Cnae9SWA.js.gz +0 -0
- package/dist/dashboard/assets/{api-keys-uNywYlfv.js → api-keys-VY68Mhd1.js} +1 -1
- package/dist/dashboard/assets/api-keys-VY68Mhd1.js.gz +0 -0
- package/dist/dashboard/assets/app-COlK95oi.css +2 -0
- package/dist/dashboard/assets/app-COlK95oi.css.gz +0 -0
- package/dist/dashboard/assets/{assert-NHwYq95t.js → assert-Chi7pCnt.js} +1 -1
- package/dist/dashboard/assets/assert-Chi7pCnt.js.gz +0 -0
- package/dist/dashboard/assets/{badge-6uzDDvqs.js → badge-D4pQapKj.js} +1 -1
- package/dist/dashboard/assets/badge-D4pQapKj.js.gz +0 -0
- package/dist/dashboard/assets/{banner-4_WNg2WZ.js → banner-DH6cg-_B.js} +1 -1
- package/dist/dashboard/assets/banner-DH6cg-_B.js.gz +0 -0
- package/dist/dashboard/assets/{card--AtFedAq.js → card-BKqlUOe4.js} +1 -1
- package/dist/dashboard/assets/card-BKqlUOe4.js.gz +0 -0
- package/dist/dashboard/assets/{cli-tools-B3e7dx3N.js → cli-tools-fTgljTGA.js} +1 -1
- package/dist/dashboard/assets/cli-tools-fTgljTGA.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-NreUx6c8.js → clipboard-button-D5Ys_sM0.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-D5Ys_sM0.js.gz +0 -0
- package/dist/dashboard/assets/database-Dz9N3zGd.js +1 -0
- package/dist/dashboard/assets/database-Dz9N3zGd.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-BtpsJU98.js → dialog-BYj-8WuK.js} +1 -1
- package/dist/dashboard/assets/dialog-BYj-8WuK.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-WwwtzihF.js → drawer-CmXIZ6H1.js} +1 -1
- package/dist/dashboard/assets/drawer-CmXIZ6H1.js.gz +0 -0
- package/dist/dashboard/assets/entry-CBg5zeu8.js +1 -0
- package/dist/dashboard/assets/entry-CBg5zeu8.js.gz +0 -0
- package/dist/dashboard/assets/entry-CQBr6U9c.js +1 -0
- package/dist/dashboard/assets/entry-CQBr6U9c.js.gz +0 -0
- package/dist/dashboard/assets/entry-CgdSAzEV.js +4 -0
- package/dist/dashboard/assets/entry-CgdSAzEV.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-D_eM3IgG.js → github-badge-BlptL8Uf.js} +1 -1
- package/dist/dashboard/assets/github-badge-BlptL8Uf.js.gz +0 -0
- package/dist/dashboard/assets/{index-BydyKaDz.js → index-js6TJvdr.js} +2 -2
- package/dist/dashboard/assets/index-js6TJvdr.js.gz +0 -0
- package/dist/dashboard/assets/{inline-BcNGF8Gr.js → inline-DUWDSmDV.js} +1 -1
- package/dist/dashboard/assets/inline-DUWDSmDV.js.gz +0 -0
- package/dist/dashboard/assets/{input-BG41et9G.js → input-LVjgZq4f.js} +1 -1
- package/dist/dashboard/assets/input-LVjgZq4f.js.gz +0 -0
- package/dist/dashboard/assets/{layout-C8mer_Ak.js → layout-CPfxpsfy.js} +1 -1
- package/dist/dashboard/assets/layout-CPfxpsfy.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-Gh_W4OR_.js → list-row-DupvLfDx.js} +1 -1
- package/dist/dashboard/assets/list-row-DupvLfDx.js.gz +0 -0
- package/dist/dashboard/assets/{live-BJGYRBnR.js → live-C4pEJUqI.js} +1 -1
- package/dist/dashboard/assets/live-C4pEJUqI.js.gz +0 -0
- package/dist/dashboard/assets/model-bans-BRiuufkB.js +1 -0
- package/dist/dashboard/assets/model-bans-BRiuufkB.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-BkoLM5Ba.js → mutation-B4ROwk8L.js} +1 -1
- package/dist/dashboard/assets/mutation-B4ROwk8L.js.gz +0 -0
- package/dist/dashboard/assets/numbers-YNcHvRY7.js +2 -0
- package/dist/dashboard/assets/numbers-YNcHvRY7.js.gz +0 -0
- package/dist/dashboard/assets/{query-keys-D7wX2vM5.js → query-keys-gmjzC3RS.js} +1 -1
- package/dist/dashboard/assets/{query-keys-D7wX2vM5.js.gz → query-keys-gmjzC3RS.js.gz} +0 -0
- package/dist/dashboard/assets/{routing-CcusqXA7.js → routing-C7tBT5o-.js} +1 -1
- package/dist/dashboard/assets/routing-C7tBT5o-.js.gz +0 -0
- package/dist/dashboard/assets/{select-BO6h8yNv.js → select-WUCXorXw.js} +2 -2
- package/dist/dashboard/assets/select-WUCXorXw.js.gz +0 -0
- package/dist/dashboard/assets/{settings-mpmXTgUa.js → settings-C_zKl1GS.js} +1 -1
- package/dist/dashboard/assets/settings-C_zKl1GS.js.gz +0 -0
- package/dist/dashboard/assets/{stack-DwLmjUFK.js → stack-_3O0F8Xn.js} +1 -1
- package/dist/dashboard/assets/stack-_3O0F8Xn.js.gz +0 -0
- package/dist/dashboard/assets/{state-DYdQmdAl.js → state-BgTxHcJS.js} +1 -1
- package/dist/dashboard/assets/state-BgTxHcJS.js.gz +0 -0
- package/dist/dashboard/assets/switch-DEjTtOWf.js +1 -0
- package/dist/dashboard/assets/switch-DEjTtOWf.js.gz +0 -0
- package/dist/dashboard/assets/terminal-0_IjPWTk.js +1 -0
- package/dist/dashboard/assets/terminal-0_IjPWTk.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-w4u4CVue.js → useMutation-BWSC0UAS.js} +1 -1
- package/dist/dashboard/assets/useMutation-BWSC0UAS.js.gz +0 -0
- package/dist/dashboard/assets/zap-C4EeVW4q.js +1 -0
- package/dist/dashboard/assets/zap-C4EeVW4q.js.gz +0 -0
- package/dist/dashboard/donation/qris-risun-marke.png +0 -0
- package/dist/dashboard/index.html +1 -1
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +54 -54
- package/package.json +1 -1
- package/scripts/cli/autostart.ts +237 -0
- package/scripts/cli/command/console-menu.ts +4 -8
- package/scripts/cli/command/update.ts +27 -10
- package/scripts/cli/main.ts +28 -1
- package/src/boot/app.ts +693 -693
- package/src/boot/dependencies.ts +646 -616
- package/src/config/spec.ts +55 -0
- package/src/config/telemetry.ts +46 -0
- package/src/console/api-keys/bucket-spend.ts +15 -4
- package/src/console/backup/contracts.ts +15 -2
- package/src/console/console-router.ts +8 -0
- package/src/console/data-management/contracts.ts +93 -0
- package/src/console/data-management/operations.ts +229 -0
- package/src/console/data-management/routes.ts +104 -0
- package/src/console/domain-registration.ts +39 -0
- package/src/console/observability/contracts.ts +524 -485
- package/src/console/observability/store.ts +1115 -1018
- package/src/console/providers/catalog/contracts.ts +6 -0
- package/src/console/providers/catalog/provider-operations.ts +20 -0
- package/src/console/providers/catalog/routes.ts +7 -0
- package/src/console/providers/catalog/store.ts +22 -1
- package/src/console/quota/account-quota-shared.ts +52 -39
- package/src/console/quota/refresh.ts +441 -429
- package/src/console/routing/pools/import-parse.ts +206 -0
- package/src/console/routing/pools/operations.ts +543 -501
- package/src/console/routing/pools/routes.ts +181 -164
- package/src/console/settings/contracts.ts +37 -0
- package/src/console/settings/store.ts +51 -0
- package/src/console/updates/routes.ts +16 -0
- package/src/main.ts +10 -0
- package/src/observability/event-compaction.ts +315 -0
- package/src/observability/event-day-index.ts +442 -0
- package/src/observability/event-log.ts +152 -3
- package/src/observability/payload-capture.ts +8 -1
- package/src/observability/payload-store.ts +440 -376
- package/src/persistence/health-event-flush.ts +36 -0
- package/src/persistence/schema.ts +1402 -1360
- package/src/persistence/stores/telemetry-store.ts +237 -214
- package/src/protocol/codecs.ts +1 -2
- package/src/protocol/image-clamp.ts +19 -1
- package/src/protocol/registry.ts +8 -0
- package/src/protocol/request/gemini.ts +9 -2
- package/src/protocol/request/messages.ts +14 -2
- package/src/providers/adapters/compatible-adapter.ts +3 -0
- package/src/providers/bundled-discovery-providers.ts +122 -119
- package/src/providers/configured-openai-providers.ts +136 -134
- package/src/providers/default-registry.ts +5 -4
- package/src/providers/discovery/openai-model-discovery.ts +5 -0
- package/src/providers/discovery/openrouter-free-roster.ts +15 -17
- package/src/providers/integrations/cline/cline-quota.ts +132 -125
- package/src/providers/integrations/codex/codex-headers.ts +25 -39
- package/src/providers/integrations/codex/codex.ts +2 -29
- package/src/providers/integrations/cursor/cursor-quota.ts +310 -0
- package/src/providers/integrations/cursor/cursor-request.ts +616 -471
- package/src/providers/integrations/cursor/cursor-session-manager.ts +319 -281
- package/src/providers/integrations/cursor/cursor.ts +1485 -960
- package/src/providers/integrations/cursor/protocol/messages.ts +25 -0
- package/src/providers/integrations/factory/factory.ts +8 -1
- package/src/providers/integrations/gemini.ts +6 -1
- package/src/providers/integrations/grok/grok-quota.ts +279 -177
- package/src/providers/integrations/grok/grok-ratelimit-store.ts +73 -0
- package/src/providers/integrations/grok/grok-ratelimit.ts +218 -0
- package/src/providers/integrations/opencode/provider.ts +12 -13
- package/src/providers/integrations/qoder/qoder-quota.ts +192 -0
- package/src/providers/integrations/trae/trae-quota.ts +384 -0
- package/src/providers/operations/account-health-service.ts +1523 -1480
- package/src/providers/provider-contracts.ts +7 -0
- package/src/providers/provider-metadata.ts +6 -0
- package/src/providers/quota/quota-support.ts +8 -0
- package/src/security/api-key-auth.ts +504 -494
- package/src/transport/dispatch/generation.ts +6 -0
- package/src/transport/dispatch/proxy-request.ts +24 -1
- package/src/transport/dispatch/ratelimit-capture.ts +37 -0
- package/src/transport/dispatch/streaming/stream.ts +14 -0
- package/src/transport/dispatch/upstream.ts +9 -0
- package/src/transport/request/preparer.ts +7 -6
- package/src/transport/request/state.ts +9 -0
- package/src/transport/request/tenant-preferences.ts +60 -0
- package/src/transport/routing/route-catalog.ts +58 -10
- package/dist/dashboard/assets/BannedPage-hfCzi-Ux.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-DLGqYEwI.js +0 -1
- package/dist/dashboard/assets/CliToolDetailPage-DLGqYEwI.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-DEDyKBlC.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-CfuYiQ8Z.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-bsLuZmYH.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-_UWhwziJ.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-Cjfc5WFS.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-Cjfc5WFS.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-DDVIfZGW.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-CWkTUtpX.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-B53YYMng.js +0 -1
- package/dist/dashboard/assets/OverviewPage-B53YYMng.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-B0KaAQf0.js +0 -3
- package/dist/dashboard/assets/ProviderDetailPage-B0KaAQf0.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-Dx31DgIO.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-Dx31DgIO.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-BpnR4kXT.js +0 -7
- package/dist/dashboard/assets/ProxyPage-BpnR4kXT.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-BpOQCnun.js +0 -1
- package/dist/dashboard/assets/QuotaPage-BpOQCnun.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-DV1eo2J_.js +0 -1
- package/dist/dashboard/assets/SettingsPage-DV1eo2J_.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-D6pmQ86L.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-DWvKiuSl.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-CmkUrgdj.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-B47uOydr.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-uNywYlfv.js.gz +0 -0
- package/dist/dashboard/assets/app-DxivpfYG.css +0 -2
- package/dist/dashboard/assets/app-DxivpfYG.css.gz +0 -0
- package/dist/dashboard/assets/assert-NHwYq95t.js.gz +0 -0
- package/dist/dashboard/assets/badge-6uzDDvqs.js.gz +0 -0
- package/dist/dashboard/assets/banner-4_WNg2WZ.js.gz +0 -0
- package/dist/dashboard/assets/card--AtFedAq.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-B3e7dx3N.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-NreUx6c8.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BtpsJU98.js.gz +0 -0
- package/dist/dashboard/assets/download-A0SW9OCW.js +0 -1
- package/dist/dashboard/assets/download-A0SW9OCW.js.gz +0 -0
- package/dist/dashboard/assets/drawer-WwwtzihF.js.gz +0 -0
- package/dist/dashboard/assets/entry-CkryrIea.js +0 -4
- package/dist/dashboard/assets/entry-CkryrIea.js.gz +0 -0
- package/dist/dashboard/assets/entry-TJIye_jT.js +0 -1
- package/dist/dashboard/assets/entry-TJIye_jT.js.gz +0 -0
- package/dist/dashboard/assets/entry-jy9_4-Ke.js +0 -1
- package/dist/dashboard/assets/entry-jy9_4-Ke.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-D_eM3IgG.js.gz +0 -0
- package/dist/dashboard/assets/index-BydyKaDz.js.gz +0 -0
- package/dist/dashboard/assets/inline-BcNGF8Gr.js.gz +0 -0
- package/dist/dashboard/assets/input-BG41et9G.js.gz +0 -0
- package/dist/dashboard/assets/layout-C8mer_Ak.js.gz +0 -0
- package/dist/dashboard/assets/list-row-Gh_W4OR_.js.gz +0 -0
- package/dist/dashboard/assets/live-BJGYRBnR.js.gz +0 -0
- package/dist/dashboard/assets/model-bans-DeVLDA7I.js +0 -1
- package/dist/dashboard/assets/model-bans-DeVLDA7I.js.gz +0 -0
- package/dist/dashboard/assets/mutation-BkoLM5Ba.js.gz +0 -0
- package/dist/dashboard/assets/numbers-Bs4LOOuf.js +0 -2
- package/dist/dashboard/assets/numbers-Bs4LOOuf.js.gz +0 -0
- package/dist/dashboard/assets/routing-CcusqXA7.js.gz +0 -0
- package/dist/dashboard/assets/select-BO6h8yNv.js.gz +0 -0
- package/dist/dashboard/assets/settings-mpmXTgUa.js.gz +0 -0
- package/dist/dashboard/assets/sparkles-BRaRUxAU.js +0 -1
- package/dist/dashboard/assets/sparkles-BRaRUxAU.js.gz +0 -0
- package/dist/dashboard/assets/stack-DwLmjUFK.js.gz +0 -0
- package/dist/dashboard/assets/state-DYdQmdAl.js.gz +0 -0
- package/dist/dashboard/assets/switch-CidSfbG7.js +0 -1
- package/dist/dashboard/assets/switch-CidSfbG7.js.gz +0 -0
- package/dist/dashboard/assets/terminal-Bd4ee8TA.js +0 -1
- package/dist/dashboard/assets/terminal-Bd4ee8TA.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-w4u4CVue.js.gz +0 -0
- /package/dist/dashboard/assets/{app-CafWtVXA.js → app-DCQZRmHY.js} +0 -0
- /package/dist/dashboard/assets/{app-CafWtVXA.js.gz → app-DCQZRmHY.js.gz} +0 -0
|
@@ -34,6 +34,7 @@ import { ProxyRequestStateStore } from "../request/state";
|
|
|
34
34
|
import { ProxyRequestPreparer } from "../request/preparer";
|
|
35
35
|
import { completeAttempt, estimatedUsage } from "./attempt-finalize";
|
|
36
36
|
import { runAttemptLoop } from "./attempt-loop";
|
|
37
|
+
import { captureProviderRateLimit } from "./ratelimit-capture";
|
|
37
38
|
|
|
38
39
|
export interface GenerationHandlerDeps {
|
|
39
40
|
readonly db: CartethyiaDatabase;
|
|
@@ -125,6 +126,11 @@ async function passThrough(
|
|
|
125
126
|
},
|
|
126
127
|
): Promise<Response> {
|
|
127
128
|
const usage = estimatedUsage(0, 0);
|
|
129
|
+
// The CLI bucket comes back on every upstream response, so this is the free
|
|
130
|
+
// place to learn it: the quota surface then reads the stored value instead of
|
|
131
|
+
// spending a request of its own. Both the manual account test and automatic
|
|
132
|
+
// routing land here, so a captured reading follows real traffic.
|
|
133
|
+
captureProviderRateLimit(args.candidate.provider_id, args.candidate.provider_account_id, upstream.headers);
|
|
128
134
|
await completeAttempt(state, {
|
|
129
135
|
status: "completed",
|
|
130
136
|
providerId: args.candidate.provider_id,
|
|
@@ -15,7 +15,11 @@ import { messagesAdapter } from "../surface/messages/adapter";
|
|
|
15
15
|
import { completionAdapter } from "../surface/completion";
|
|
16
16
|
import { forwardedRequestHeaders, proxySuccessHeaders, buildUpstreamDispatchContext, reportedModelFrom } from "./upstream";
|
|
17
17
|
import { resolvePromptCacheKey } from "../../providers/operations/session-resolution";
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
applyTenantPreferences,
|
|
20
|
+
applyTenantDeadlineOverride,
|
|
21
|
+
resolveTenantImageClampMaxBytes,
|
|
22
|
+
} from "../request/tenant-preferences";
|
|
19
23
|
import { metrics } from "../../observability/metrics";
|
|
20
24
|
import type { NetworkPoolSelector } from "../../network/pool/selector";
|
|
21
25
|
import type { TelemetryBatchBuffer } from "../../observability/telemetry-buffer";
|
|
@@ -128,6 +132,22 @@ export async function handleProviderProxyRequest(
|
|
|
128
132
|
if (!prepared)
|
|
129
133
|
throw new GatewayError("admission_unavailable", 503, "The request was not prepared, so no route can be dialed. Resend the request.");
|
|
130
134
|
const canonicalRequest = await applyTenantPreferences(prepared, deps.db);
|
|
135
|
+
// Per-tenant timeout, applied as soon as the tenant is known. The deadline
|
|
136
|
+
// was armed at admission with the deployment default; this re-arms it.
|
|
137
|
+
await applyTenantDeadlineOverride(
|
|
138
|
+
prepared.authorization.snapshot.tenant_id,
|
|
139
|
+
deps.db,
|
|
140
|
+
(ms) => state.extendDeadline(ms),
|
|
141
|
+
);
|
|
142
|
+
// Memoized on the request: the ceiling is a tenant setting, so it cannot
|
|
143
|
+
// change between failover attempts, and each wire encoder that clamps an
|
|
144
|
+
// image reads it from here instead of paying for a preference read per
|
|
145
|
+
// attempt.
|
|
146
|
+
const imageClampMaxBytes = await resolveTenantImageClampMaxBytes(
|
|
147
|
+
deps.db,
|
|
148
|
+
prepared.authorization.snapshot.tenant_id,
|
|
149
|
+
);
|
|
150
|
+
if (imageClampMaxBytes !== undefined) state.imageClampMaxBytes = imageClampMaxBytes;
|
|
131
151
|
const candidates =
|
|
132
152
|
prepared.eligibleRouteCandidates.length > 0 ? prepared.eligibleRouteCandidates : [prepared.candidate];
|
|
133
153
|
const attemptCandidates = candidates;
|
|
@@ -226,6 +246,9 @@ export async function handleProviderProxyRequest(
|
|
|
226
246
|
headers: inboundHeaders,
|
|
227
247
|
requestId: state.requestId,
|
|
228
248
|
requestIdentity: state,
|
|
249
|
+
...(state.imageClampMaxBytes === undefined
|
|
250
|
+
? {}
|
|
251
|
+
: { imageClampMaxBytes: state.imageClampMaxBytes }),
|
|
229
252
|
...(conversationAffinity ? { conversationAffinity } : {}),
|
|
230
253
|
...(candidate.user_agent === undefined ? {} : { userAgent: candidate.user_agent }),
|
|
231
254
|
...(deps.networkBindingFactory
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Files a provider's rate-limit headers against the account that earned them.
|
|
2
|
+
//
|
|
3
|
+
// Providers answer real traffic with the bucket they are metering — Grok's CLI
|
|
4
|
+
// plane returns `x-ratelimit-*` on every response — and that is a reading the
|
|
5
|
+
// gateway already paid for. Capturing it here is what lets the quota surface
|
|
6
|
+
// show a balance without spending a request of its own.
|
|
7
|
+
//
|
|
8
|
+
// Kept provider-scoped and fail-open: a provider with no bucket parser is
|
|
9
|
+
// skipped, and any throw is swallowed, because a bookkeeping miss must never
|
|
10
|
+
// take down a request that already succeeded.
|
|
11
|
+
|
|
12
|
+
import { rememberGrokRateLimit } from "../../providers/integrations/grok/grok-ratelimit-store";
|
|
13
|
+
import { readGrokRateLimitHeaders } from "../../providers/integrations/grok/grok-ratelimit";
|
|
14
|
+
|
|
15
|
+
/** Providers whose response headers carry a readable bucket. */
|
|
16
|
+
const GROK_PROVIDER_IDS: readonly string[] = ["grok"];
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Stores whatever bucket the response carried, keyed by account.
|
|
20
|
+
*
|
|
21
|
+
* No account id means no home for the reading: a bucket filed under the wrong
|
|
22
|
+
* account is worse than none, since it would report another account's balance.
|
|
23
|
+
*/
|
|
24
|
+
export function captureProviderRateLimit(
|
|
25
|
+
providerId: string,
|
|
26
|
+
accountId: string | undefined,
|
|
27
|
+
headers: { get(name: string): string | null },
|
|
28
|
+
): void {
|
|
29
|
+
if (!accountId) return;
|
|
30
|
+
const id = providerId.toLowerCase();
|
|
31
|
+
if (!GROK_PROVIDER_IDS.includes(id)) return;
|
|
32
|
+
try {
|
|
33
|
+
rememberGrokRateLimit(accountId, readGrokRateLimitHeaders(headers));
|
|
34
|
+
} catch {
|
|
35
|
+
// Swallowed: advisory bookkeeping on a request that already succeeded.
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -49,6 +49,7 @@ import {
|
|
|
49
49
|
resolveCaptureDepthForRequest,
|
|
50
50
|
shouldCaptureProviderBodyForRequest,
|
|
51
51
|
} from "../attempt-finalize";
|
|
52
|
+
import { resolveTenantImageClampMaxBytes } from "../../request/tenant-preferences";
|
|
52
53
|
import { proxySuccessHeaders, buildUpstreamDispatchContext } from "../upstream";
|
|
53
54
|
import type { AttemptLeaseOwnership } from "../leases";
|
|
54
55
|
import type { ProviderContinuationScope } from "../../../providers/provider-contracts";
|
|
@@ -186,6 +187,16 @@ export async function dispatchStreamingAttempt(input: StreamingDispatchInput): P
|
|
|
186
187
|
providerCaptureMaxBytes(captureDepth),
|
|
187
188
|
)
|
|
188
189
|
: undefined;
|
|
190
|
+
// Same memoized ceiling the non-streaming path resolves, read before the
|
|
191
|
+
// dispatch context is built so a stream gets the same limit as a normal
|
|
192
|
+
// request rather than silently falling back to the deployment default.
|
|
193
|
+
if (state.imageClampMaxBytes === undefined) {
|
|
194
|
+
const clampMaxBytes = await resolveTenantImageClampMaxBytes(
|
|
195
|
+
deps.db,
|
|
196
|
+
prepared.authorization.snapshot.tenant_id,
|
|
197
|
+
);
|
|
198
|
+
if (clampMaxBytes !== undefined) state.imageClampMaxBytes = clampMaxBytes;
|
|
199
|
+
}
|
|
189
200
|
const dispatchContext = buildUpstreamDispatchContext({
|
|
190
201
|
credential,
|
|
191
202
|
deadline: state.deadlineMs,
|
|
@@ -193,6 +204,9 @@ export async function dispatchStreamingAttempt(input: StreamingDispatchInput): P
|
|
|
193
204
|
headers: inboundHeaders,
|
|
194
205
|
requestId: state.requestId,
|
|
195
206
|
requestIdentity: state,
|
|
207
|
+
...(state.imageClampMaxBytes === undefined
|
|
208
|
+
? {}
|
|
209
|
+
: { imageClampMaxBytes: state.imageClampMaxBytes }),
|
|
196
210
|
...(conversationAffinity ? { conversationAffinity } : {}),
|
|
197
211
|
...(candidate.user_agent === undefined ? {} : { userAgent: candidate.user_agent }),
|
|
198
212
|
...(wrappedOutboundFetch ? { outboundFetch: wrappedOutboundFetch } : {}),
|
|
@@ -56,6 +56,12 @@ export function buildUpstreamDispatchContext(input: {
|
|
|
56
56
|
readonly continuationScope?: ProviderContinuationScope;
|
|
57
57
|
/** Stable per-conversation cache affinity (preferred over random ids). */
|
|
58
58
|
readonly conversationAffinity?: string;
|
|
59
|
+
/**
|
|
60
|
+
* Per-tenant inline-attachment ceiling in bytes, resolved once per request.
|
|
61
|
+
* Carried through dispatch because wire encoders clamp images but hold no
|
|
62
|
+
* database handle.
|
|
63
|
+
*/
|
|
64
|
+
readonly imageClampMaxBytes?: number;
|
|
59
65
|
/** Opaque identity shared by retries of one logical gateway request. */
|
|
60
66
|
readonly requestIdentity?: object;
|
|
61
67
|
}): Record<string, unknown> {
|
|
@@ -94,6 +100,9 @@ export function buildUpstreamDispatchContext(input: {
|
|
|
94
100
|
...(input.leaseOwnership ? { lease_ownership: input.leaseOwnership } : {}),
|
|
95
101
|
...(input.continuationScope ? { continuation_scope: input.continuationScope } : {}),
|
|
96
102
|
...(input.conversationAffinity ? { conversation_affinity: input.conversationAffinity } : {}),
|
|
103
|
+
...(input.imageClampMaxBytes === undefined
|
|
104
|
+
? {}
|
|
105
|
+
: { image_clamp_max_bytes: input.imageClampMaxBytes }),
|
|
97
106
|
...(input.requestIdentity ? { request_identity: input.requestIdentity } : {}),
|
|
98
107
|
} as Record<string, unknown>;
|
|
99
108
|
}
|
|
@@ -85,12 +85,13 @@ export function resolveContinuationAffinity(
|
|
|
85
85
|
// id replayed from another key or tenant resolves nothing.
|
|
86
86
|
const record = lookupPendingCall(callId, scope);
|
|
87
87
|
if (record === undefined) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
)
|
|
88
|
+
// A replayed conversation carries every earlier tool result, and results
|
|
89
|
+
// previous legs already answered resolve nothing here — they are history,
|
|
90
|
+
// not the flight being continued. The request is a continuation only
|
|
91
|
+
// through the ids that still resolve; when none do, the ordinary routing
|
|
92
|
+
// path decides (a Cursor adapter may rebase the full history into a
|
|
93
|
+
// fresh Run).
|
|
94
|
+
continue;
|
|
94
95
|
}
|
|
95
96
|
if (affinity !== undefined && affinity.providerAccountId !== record.providerAccountId) {
|
|
96
97
|
throw new GatewayError(
|
|
@@ -117,6 +117,15 @@ export interface ProxyRequestState {
|
|
|
117
117
|
payloadCapturePreference?:
|
|
118
118
|
| { readonly ok: true; readonly mode: TelemetryPayloadMode; readonly depth: TelemetryPayloadDepth }
|
|
119
119
|
| { readonly ok: false };
|
|
120
|
+
/**
|
|
121
|
+
* This request's resolved inline-attachment ceiling, memoized for the same
|
|
122
|
+
* reason the capture preference is: it is a tenant setting, so it cannot
|
|
123
|
+
* change between attempts, and reading it per attempt would pay for it
|
|
124
|
+
* several times on a request that fails over.
|
|
125
|
+
*
|
|
126
|
+
* `undefined` means the deployment default.
|
|
127
|
+
*/
|
|
128
|
+
imageClampMaxBytes?: number;
|
|
120
129
|
/**
|
|
121
130
|
* Set by the dispatch handler synchronously before returning a streaming
|
|
122
131
|
* response. `afterResponse` lifecycle hooks read it to skip finalization
|
|
@@ -13,6 +13,66 @@ import type { PreparedProxyRequest } from "./preparer";
|
|
|
13
13
|
import { normalizeThinkingConfig } from "../translation/thinking";
|
|
14
14
|
import { compressRequest } from "./rtk/compress-request";
|
|
15
15
|
import { preferencesReaderFor } from "../../persistence/tenant-preferences";
|
|
16
|
+
import { resolveUpstreamTimeoutMs } from "../../config/timeouts";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Re-arms the request deadline from the tenant's timeout override.
|
|
20
|
+
*
|
|
21
|
+
* The initial deadline is armed before authentication, when the tenant is not
|
|
22
|
+
* yet known, so a per-tenant timeout can only be applied here — once the
|
|
23
|
+
* snapshot says who is asking. `extendDeadline` keeps the timer, the state
|
|
24
|
+
* field, and the in-flight record in step, so the backstop sweep cannot
|
|
25
|
+
* force-release a request whose budget legitimately grew.
|
|
26
|
+
*
|
|
27
|
+
* Non-fatal: a settings read outage leaves the deployment default in place
|
|
28
|
+
* rather than failing a dispatchable request.
|
|
29
|
+
*/
|
|
30
|
+
export async function applyTenantDeadlineOverride(
|
|
31
|
+
tenantId: string,
|
|
32
|
+
db: CartethyiaDatabase,
|
|
33
|
+
extend: (ms: number) => void,
|
|
34
|
+
): Promise<void> {
|
|
35
|
+
try {
|
|
36
|
+
const prefs = await preferencesReaderFor(db).readPreferences(tenantId);
|
|
37
|
+
const seconds = prefs?.upstreamTimeoutSeconds;
|
|
38
|
+
if (typeof seconds !== "number" || !Number.isFinite(seconds) || seconds <= 0) return;
|
|
39
|
+
const wanted = Math.floor(seconds * 1000);
|
|
40
|
+
// Only ever shorten or leave alone a deadline that has already begun: an
|
|
41
|
+
// override longer than the default cannot give back time the request has
|
|
42
|
+
// already spent, and arming a longer one would silently extend a budget
|
|
43
|
+
// the caller's own client has likely abandoned.
|
|
44
|
+
const remaining = Math.min(wanted, resolveUpstreamTimeoutMs());
|
|
45
|
+
extend(remaining);
|
|
46
|
+
} catch {
|
|
47
|
+
// Non-fatal
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The tenant's inline-attachment byte ceiling, or `undefined` for the default.
|
|
53
|
+
*
|
|
54
|
+
* Read through the shared revision-keyed preference reader, so the cost is a
|
|
55
|
+
* memory hit rather than a Postgres round trip — this is consulted while
|
|
56
|
+
* building a request body, on the dispatch path.
|
|
57
|
+
*
|
|
58
|
+
* Non-fatal: a settings read outage falls back to the deployment default,
|
|
59
|
+
* which is the safe direction (the default refuses less, but it never admits
|
|
60
|
+
* something no upstream will take).
|
|
61
|
+
*/
|
|
62
|
+
export async function resolveTenantImageClampMaxBytes(
|
|
63
|
+
db: CartethyiaDatabase,
|
|
64
|
+
tenantId: string | undefined,
|
|
65
|
+
): Promise<number | undefined> {
|
|
66
|
+
if (tenantId === undefined) return undefined;
|
|
67
|
+
try {
|
|
68
|
+
const prefs = await preferencesReaderFor(db).readPreferences(tenantId);
|
|
69
|
+
const bytes = prefs?.imageClampMaxBytes;
|
|
70
|
+
if (typeof bytes !== "number" || !Number.isFinite(bytes) || bytes <= 0) return undefined;
|
|
71
|
+
return Math.floor(bytes);
|
|
72
|
+
} catch {
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
16
76
|
|
|
17
77
|
/**
|
|
18
78
|
* Applies request shaping owned by tenant preferences and deployment config:
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { asc, eq, ne } from "drizzle-orm";
|
|
4
4
|
import type { CartethyiaDatabase } from "../../persistence/db-handle";
|
|
5
5
|
import {
|
|
6
|
+
consoleSettings,
|
|
6
7
|
models,
|
|
7
8
|
modelAliases,
|
|
8
9
|
modelCombos,
|
|
@@ -383,7 +384,7 @@ class RouteCatalogRepository {
|
|
|
383
384
|
constructor(private readonly db: CartethyiaDatabase) {}
|
|
384
385
|
|
|
385
386
|
async loadRouteCatalogSnapshot(tenantId?: string): Promise<RouteCatalogSnapshotResult> {
|
|
386
|
-
const [providerRows, modelRows, accountRows, aliasRows, comboRows, routingRows, poolRows, disabledModelRows, cliMappingRows, cliMappingSettingRows, poolSettingRows] =
|
|
387
|
+
const [providerRows, modelRows, accountRows, aliasRows, comboRows, routingRows, poolRows, disabledModelRows, cliMappingRows, cliMappingSettingRows, poolSettingRows, settingsRows] =
|
|
387
388
|
await Promise.all([
|
|
388
389
|
this.db.select(PROVIDER_COLUMNS).from(providers).where(eq(providers.enabled, true)),
|
|
389
390
|
this.db.select(MODEL_COLUMNS).from(models),
|
|
@@ -400,8 +401,38 @@ class RouteCatalogRepository {
|
|
|
400
401
|
this.db.select(CLI_MAPPING_COLUMNS).from(cliToolMappings),
|
|
401
402
|
this.db.select(CLI_MAPPING_SETTING_COLUMNS).from(cliToolSettings),
|
|
402
403
|
this.db.select(POOL_SETTING_COLUMNS).from(poolRoutingSettings),
|
|
404
|
+
// The cooldown override is a console_settings preference, read inside
|
|
405
|
+
// the same fan-out rather than per request: one more row set on a
|
|
406
|
+
// query that already runs, instead of a Postgres round trip on every
|
|
407
|
+
// admitted request the way `tenantConcurrencyLimit` once did.
|
|
408
|
+
this.db
|
|
409
|
+
.select({ tenantId: consoleSettings.tenantId, preferences: consoleSettings.preferences })
|
|
410
|
+
.from(consoleSettings),
|
|
403
411
|
]);
|
|
404
412
|
const mergedModelRows = mergeModelCatalog(modelRows);
|
|
413
|
+
/**
|
|
414
|
+
* Tenants that asked to ignore an account-wide cooldown.
|
|
415
|
+
*
|
|
416
|
+
* Read-only override: the cooldown is still on the row, still shown in
|
|
417
|
+
* Health, and still recovered by the sweep. This only stops it from
|
|
418
|
+
* excluding the candidate, so turning the setting off needs no cleanup.
|
|
419
|
+
*
|
|
420
|
+
* Per-(account, model) cooldowns are deliberately NOT overridden — see the
|
|
421
|
+
* preference's own doc.
|
|
422
|
+
*/
|
|
423
|
+
const cooldownOverrideTenants = new Set<string>();
|
|
424
|
+
for (const row of settingsRows ?? []) {
|
|
425
|
+
if (row.preferences?.forceDisableCooldown === true && row.tenantId) {
|
|
426
|
+
cooldownOverrideTenants.add(row.tenantId);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* A shared account has no tenant of its own (`tenant_id IS NULL`), so a
|
|
431
|
+
* tenant-keyed override would never match it — yet a shared account is
|
|
432
|
+
* exactly the one every tenant dials, and parking it stops all of them.
|
|
433
|
+
* When any tenant asks, the override covers shared accounts too.
|
|
434
|
+
*/
|
|
435
|
+
const anyTenantOverrides = cooldownOverrideTenants.size > 0;
|
|
405
436
|
|
|
406
437
|
// Compile tenant-scoped disables into a lookup keyed by the same
|
|
407
438
|
// composite identity the `models` table uses. A disabled key suppresses
|
|
@@ -701,16 +732,33 @@ class RouteCatalogRepository {
|
|
|
701
732
|
// Expired cooldowns read as healthy here without a fire-and-forget
|
|
702
733
|
// write: AccountHealthSweeper materializes the recovery every 30s,
|
|
703
734
|
// so the builder stays a pure read (no UPDATE racing the request).
|
|
735
|
+
//
|
|
736
|
+
// `forceDisableCooldown` skips the exclusion entirely. The cooldown
|
|
737
|
+
// is still real and still recorded — it simply stops deciding
|
|
738
|
+
// routing. That is what makes the setting reversible: nothing was
|
|
739
|
+
// written, so turning it off needs no cleanup, and Health still
|
|
740
|
+
// reports the truth about the account.
|
|
704
741
|
if (account.cooldownUntil && account.cooldownUntil.getTime() > Date.now()) {
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
742
|
+
const overrideActive =
|
|
743
|
+
rowTenantId === null
|
|
744
|
+
? anyTenantOverrides
|
|
745
|
+
: cooldownOverrideTenants.has(rowTenantId);
|
|
746
|
+
if (overrideActive) {
|
|
747
|
+
// The account is still cooling — this only stops it from being
|
|
748
|
+
// excluded. `plan()` reads `cooldown_until` to answer the
|
|
749
|
+
// all-cooling case, so it stays populated either way.
|
|
750
|
+
candidate.cooldown_until = account.cooldownUntil.getTime();
|
|
751
|
+
} else {
|
|
752
|
+
candidate.health_status = "cooldown";
|
|
753
|
+
candidate.cooldown_kind = hardCooldownCategory(account.lastErrorCategory)
|
|
754
|
+
? "hard"
|
|
755
|
+
: "soft";
|
|
756
|
+
// The deadline rides with the exclusion it explains. `plan()`
|
|
757
|
+
// reads it to answer the all-cooling case with the real reset
|
|
758
|
+
// instead of a bare 429, so a client backs off for the window
|
|
759
|
+
// that is actually open rather than inventing one.
|
|
760
|
+
candidate.cooldown_until = account.cooldownUntil.getTime();
|
|
761
|
+
}
|
|
714
762
|
}
|
|
715
763
|
}
|
|
716
764
|
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{n as e,o as t,r as n,t as r}from"./jsx-runtime-CXUMngI_.js";import{t as i}from"./arrow-left-CpBxkoxI.js";import{l as a,s as o}from"./numbers-Bs4LOOuf.js";import{t as s}from"./circle-check-BU3v_mFW.js";import{t as c}from"./download-A0SW9OCW.js";import{n as l,r as u}from"./state-DYdQmdAl.js";import{t as d}from"./route-CNSNj9Zm.js";import{t as f}from"./search-JOKus9rb.js";import{l as p}from"./query-keys-D7wX2vM5.js";import{$ as m,Z as h}from"./entry-CkryrIea.js";import{n as g,r as _,t as v}from"./card--AtFedAq.js";import{n as ee}from"./download-dRFo5_Wu.js";import{t as y}from"./badge-6uzDDvqs.js";import{n as te}from"./input-BG41et9G.js";import{t as ne}from"./select-BO6h8yNv.js";import{t as re}from"./ModelPicker-CWkTUtpX.js";import{t as b}from"./switch-CidSfbG7.js";import{t as x}from"./stack-DwLmjUFK.js";import{n as S}from"./inline-BcNGF8Gr.js";import{f as ie,t as C}from"./api-keys-uNywYlfv.js";import{a as w,i as ae,n as oe,o as T,r as E,t as se}from"./cli-tools-B3e7dx3N.js";import{t as D}from"./ToolIcon-K1ckcDKg.js";var ce=e(`book-open`,[[`path`,{d:`M12 5v16`,key:`1f6ucr`}],[`path`,{d:`M20.001 19A2 2 0 0022 17V5a2 2 0 00-1.999-2L16 3.002A5 5 0 0012 5a5 5 0 00-4-2H4a2 2 0 00-2 2v12a2 2 0 001.999 2H8a5 5 0 014 2 5 5 0 014-2z`,key:`1fyvmf`}]]),le=e(`circle-x`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`m15 9-6 6`,key:`1uzhvr`}],[`path`,{d:`m9 9 6 6`,key:`z0biqf`}]]),O=e(`file-code-corner`,[[`path`,{d:`M4 12.15V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2h-3.35`,key:`1wthlu`}],[`path`,{d:`M14 2v5a1 1 0 0 0 1 1h5`,key:`wfsgrz`}],[`path`,{d:`m5 16-3 3 3 3`,key:`331omg`}],[`path`,{d:`m9 22 3-3-3-3`,key:`lsp7cz`}]]),ue=e(`settings-2`,[[`path`,{d:`M14 17H5`,key:`gfn3mx`}],[`path`,{d:`M19 7h-9`,key:`6i9tg`}],[`circle`,{cx:`17`,cy:`17`,r:`3`,key:`18b49y`}],[`circle`,{cx:`7`,cy:`7`,r:`3`,key:`dfmy0x`}]]),k=t(n(),1);function A(e,t){return e==null?!1:e instanceof Set?e.has(t):e.includes(t)}function j(e){return e==null?0:e instanceof Set?e.size:e.length}function M(e){let t=e.lastIndexOf(`/`);return t<0?e:e.slice(t+1)}function N(e,t,n,r){let i=n?`${n}/${M(t)}`:void 0,a=[t,M(t),...i?[i]:[]];r&&r!==t&&a.push(r);let o=e.model_list;return e.model_access_mode===`blacklist`?a.some(e=>A(o,e))?`model-denied`:null:o==null||j(o)===0||a.some(e=>A(o,e))?null:`model-not-allowed`}function de(e,t,n,r){return N(e,t,n,r)===null}var P=r();function F(){let{toolId:e}=m(),t=w(),n=T(),r=C(),i=(0,k.useMemo)(()=>t.data?.find(t=>t.id===e)??null,[t.data,e]),a=(0,k.useMemo)(()=>{let t=n.data;if(t)return Array.isArray(t)?t.find(t=>t.toolId===e):t[e]},[n.data,e]);return t.isLoading?(0,P.jsx)(u,{label:`Loading tool details…`}):t.isError?(0,P.jsx)(l,{title:`Unable to load tool`,message:t.error?.message??`Could not load the CLI tool registry.`,onRetry:()=>void t.refetch()}):i?(0,P.jsx)(I,{tool:i,status:a,apiKeys:r.data??[]}):(0,P.jsxs)(x,{gap:`12px`,children:[(0,P.jsx)(h,{to:`/cli-tools`,className:`text-caption text-accent`,children:`← Back to CLI Tools`}),(0,P.jsx)(l,{title:`Tool not found`,message:`Unknown CLI tool id.`})]})}function I({tool:e,status:t,apiKeys:n}){let[r,l]=(0,k.useState)(``),[u,m]=(0,k.useState)(()=>typeof window>`u`?`http://localhost:12800`:window.location.origin),[C,w]=(0,k.useState)({}),[T,A]=(0,k.useState)(!1),[j,M]=(0,k.useState)({}),[N,F]=(0,k.useState)(null),[I,L]=(0,k.useState)(`slot`),R=(0,k.useRef)(null),z=(0,k.useMemo)(()=>n.filter(e=>e.keyMode===`personal`||e.keyMode===`share`&&e.parentKeyId===void 0),[n]),B=ae(e.id,r),V=E(),H=oe(),U=ie(),W=se(),G=z.find(e=>e.id===r),K=G?.scopes?.includes(`routing:cli_mapping`)===!0,q=G?.keyMode===`personal`,J=G?.modelAccessMode??`whitelist`,Y=G?.modelList,X=e=>de({model_access_mode:J,model_list:Y},e),fe=(0,k.useMemo)(()=>{let t=new Set;if(!e.mappingSupported)return t;for(let n of e.defaultModels){let e=j[n.alias]??``;if(e.length===0)continue;let r=C[n.alias]??n.defaultValue??n.id;!X(e)&&!X(r)&&t.add(n.alias)}return t},[e.defaultModels,e.mappingSupported,j,C,G]),pe=e=>{if(!G)return;let t=Y??[],n=J===`blacklist`?t.filter(t=>t!==e):[...new Set([...t,e])];U.mutate({keyId:G.id,request:{modelAccessMode:J,modelList:n}},{onSuccess:()=>o.success(J===`blacklist`?`Model unblocked for this key`:`Model allowed for this key`),onError:e=>o.error(p(e,`Could not update the API key.`))})};(0,k.useEffect)(()=>{let t=Object.fromEntries(e.defaultModels.map(e=>[e.alias,e.defaultValue??e.id]));w(t),M({}),R.current=null},[e,r]),(0,k.useEffect)(()=>{let t=B.data;if(t&&t.apiKeyId===r){if(A(t.enabled),t.mappings.length>0)w(e=>({...e,...Object.fromEntries(t.mappings.map(e=>[e.slotKey,e.sourceModel]))})),M(e=>({...e,...Object.fromEntries(t.mappings.map(e=>[e.slotKey,e.targetModel]))}));else{let t=Object.fromEntries(e.defaultModels.map(e=>[e.alias,e.defaultValue??e.id]));w(t),M({})}R.current=JSON.stringify({enabled:t.enabled,mappings:t.mappings})}},[r,B.data,e.defaultModels]);let me=t?.installed??!1,Z=t?.configured??!1,he=e.configType===`guide`,Q=(0,k.useMemo)(()=>e.defaultModels.map(e=>C[e.alias]??e.defaultValue??e.id),[C,e.defaultModels]),$=(0,k.useCallback)(()=>{if(e.mappingSupported)return{enabled:T,mappings:e.defaultModels.filter(e=>(j[e.alias]??``).length>0).map(e=>({slotKey:e.alias,sourceModel:C[e.alias]??e.defaultValue??e.id,targetModel:j[e.alias]??``,enabled:!0})).filter(e=>e.targetModel.length>0)}},[T,j,C,e.defaultModels,e.mappingSupported]);(0,k.useEffect)(()=>{if(!e.mappingSupported||r.length===0)return;let t=B.data;if(!t||t.apiKeyId!==r)return;let n=$();if(!n)return;let i=JSON.stringify({enabled:n.enabled,mappings:n.mappings});if(i===R.current)return;let a=setTimeout(()=>{V.mutate({toolId:e.id,keyId:r,input:n},{onSuccess:()=>{R.current=i},onError:e=>o.error(p(e,`Could not save mappings.`))})},600);return()=>clearTimeout(a)},[$,r,B.data,V,e.id,e.mappingSupported]);let ge=(0,k.useCallback)(()=>{let t=Object.fromEntries(e.defaultModels.map(e=>[e.alias,C[e.alias]??e.defaultValue??e.id])),n=e.defaultModels.find(e=>e.roleKind===`subagent`);return{endpoint:u,apiKey:``,modelIds:e.id===`claude`?[]:Q,modelSlots:t,...Q[0]?{activeModel:Q[0]}:{},...n?{subagentModel:C[n.alias]??n.defaultValue??n.id}:{},mappingOwnerId:r,...e.mappingSupported?{mapping:$()}:{}}},[Q,$,r,u,C,e]);return(0,P.jsxs)(x,{gap:`16px`,children:[(0,P.jsxs)(h,{to:`/cli-tools`,className:`inline-flex items-center gap-3 text-caption text-accent`,children:[(0,P.jsx)(i,{size:14}),`Back to CLI Tools`]}),(0,P.jsx)(v,{children:(0,P.jsxs)(g,{className:`flex flex-col gap-5`,children:[(0,P.jsxs)(S,{gap:`14px`,align:`flex-start`,className:`flex-wrap`,children:[(0,P.jsx)(D,{toolId:e.id,name:e.name,color:e.color,size:46}),(0,P.jsxs)(S,{gap:`8px`,className:`flex-wrap`,children:[(0,P.jsx)(`h1`,{className:`text-title font-bold m-0`,children:e.name}),me?(0,P.jsx)(y,{tone:`ok`,children:`Installed`}):(0,P.jsx)(y,{tone:`disabled`,children:`Not installed`}),Z?(0,P.jsx)(y,{tone:`accent`,children:`Configured`}):null,e.configType===`guide`?(0,P.jsx)(y,{tone:`info`,children:`Guide`}):null]})]}),(0,P.jsx)(`p`,{className:`m-0 text-caption text-text-secondary`,children:e.description}),e.docsUrl?(0,P.jsx)(`a`,{href:e.docsUrl,target:`_blank`,rel:`noreferrer`,className:`inline-block text-caption text-accent`,children:`Official documentation →`}):null,t?.settingsPath?(0,P.jsxs)(`div`,{className:`font-mono tabular-nums px-6 py-4 rounded-inset bg-surface-2 border border-inner-border text-badge text-text-secondary break-all`,children:[`Config file: `,t.settingsPath]}):null,t?.message?(0,P.jsxs)(`div`,{className:`flex items-center gap-3 text-badge ${Z?`text-green`:`text-orange`}`,children:[Z?(0,P.jsx)(s,{size:13}):(0,P.jsx)(le,{size:13}),t.message]}):null]})}),(0,P.jsxs)(v,{children:[(0,P.jsx)(_,{title:`Setup`,subtitle:`Pick the Apikey Name and endpoint, set the models, then download. The key's secret is read server-side — you never paste it.`,icon:(0,P.jsx)(ue,{size:16})}),(0,P.jsx)(g,{children:(0,P.jsx)(x,{gap:`12px`,children:(0,P.jsxs)(`div`,{className:`grid grid-cols-2 gap-4 items-start min-w-0 max-medium:grid-cols-1`,children:[(0,P.jsxs)(`div`,{className:`flex flex-col gap-3 min-w-0`,children:[(0,P.jsx)(ne,{label:`Apikey Name`,id:`api-key-select`,value:r,onValueChange:l,options:[{value:``,label:`Select…`},...z.map(e=>({value:e.id,label:`${e.label} — ${e.keyMode===`personal`?`Personal`:`Share template`}`}))]}),(0,P.jsx)(`div`,{className:`flex items-center gap-3 flex-wrap`,children:G===void 0?(0,P.jsx)(`span`,{className:`mt-1 text-badge text-text-tertiary`,children:`Choose the Apikey Name whose routes this CLI session should use.`}):K?(0,P.jsxs)(P.Fragment,{children:[(0,P.jsx)(y,{tone:`ok`,children:`routing:cli_mapping`}),(0,P.jsx)(`span`,{className:`mt-1 text-badge text-text-tertiary`,children:`Remote routes work with this key.`})]}):(0,P.jsxs)(P.Fragment,{children:[(0,P.jsx)(y,{tone:`warn`,title:`This key cannot resolve persisted CLI mappings on /v1/*. Turn on Remote Routing below to enable it.`,children:`no CLI scope`}),(0,P.jsx)(`span`,{className:`mt-1 text-badge text-text-tertiary`,children:`Remote routes stay inert on this key — turning on Remote Routing below will activate it.`})]})}),G?.keyMode===`share`?(0,P.jsx)(`span`,{className:`mt-1 text-badge text-text-tertiary`,children:`Share template: its child keys inherit these routes automatically.`}):null]}),(0,P.jsx)(`div`,{className:`min-w-0`,children:(0,P.jsx)(te,{label:`Endpoint`,value:u,onChange:e=>m(e.target.value)})})]})})})]}),(0,P.jsxs)(v,{children:[(0,P.jsx)(_,{title:`Model routing`,icon:(0,P.jsx)(d,{size:16}),subtitle:e.mappingSupported?`Left is the model name the CLI asks for. Right is the route Cartethyia sends it to. Leave a target empty to send that slot straight to the provider.`:`The model names written into this tool's config.`,action:e.mappingSupported?(0,P.jsxs)(`div`,{className:`flex items-center gap-5`,children:[(0,P.jsx)(b,{checked:T,disabled:!r,onChange:t=>{if(A(t),!r){o.error(`Select an API key first to configure its Remote Routing.`);return}if(t&&G&&!K){let e=[...new Set([...G.scopes??[],`routing:cli_mapping`])];U.mutate({keyId:G.id,request:{scopes:e}},{onSuccess:()=>{o.success(`Activated routing:cli_mapping on "${G.label}".`)},onError:e=>{o.error(p(e,`Could not update key scope.`))}})}let n=$();n&&V.mutate({toolId:e.id,keyId:r,input:{...n,enabled:t}},{onError:e=>o.error(p(e,`Could not save mappings.`))})},label:T?`Remote Routing on`:`Remote Routing off`}),(0,P.jsx)(a,{variant:`secondary`,size:`sm`,disabled:!r||H.isPending,onClick:()=>{if(!r){o.error(`Select an API key first.`);return}H.mutate({toolId:e.id,keyId:r},{onSuccess:()=>{A(!1),M({}),o.success(`Remote routes cleared and Remote Routing turned off.`)},onError:e=>o.error(p(e,`Could not reset remote routes.`))})},children:H.isPending?`Resetting…`:`Reset`})]}):void 0}),(0,P.jsxs)(g,{children:[r?null:(0,P.jsx)(`p`,{className:`text-badge text-text-tertiary mb-5`,children:`Select an API key above to load and save remote routes for that key. You can still set models below to download a config file.`}),e.mappingSupported?(0,P.jsxs)(`div`,{className:`mb-5 flex items-center gap-4 rounded-inset border border-inner-border bg-surface-2 px-5 py-3 text-micro text-text-tertiary`,children:[(0,P.jsx)(`span`,{className:`flex-1`,children:`CLI asks for`}),(0,P.jsx)(`span`,{className:`w-9 text-center`,children:`→`}),(0,P.jsx)(`span`,{className:`flex-1`,children:`Cartethyia routes to`})]}):null,(0,P.jsx)(x,{gap:`6px`,children:e.defaultModels.map(t=>{let n=C[t.alias]??t.defaultValue??t.id,r=j[t.alias]??``,i=r===n;return(0,P.jsxs)(`div`,{className:`grid items-center gap-4 ${e.mappingSupported?`grid-cols-2`:`grid-cols-1`}`,children:[(0,P.jsxs)(`div`,{className:`flex flex-col gap-1 min-w-0`,children:[(0,P.jsxs)(`button`,{type:`button`,onClick:()=>{F(t.alias),L(`slot`)},className:`font-mono tabular-nums flex w-full min-w-0 cursor-pointer items-center justify-between gap-4 rounded-inset border border-inner-border bg-surface-2 px-5 py-3.5 text-left text-badge text-text-primary`,children:[(0,P.jsx)(`span`,{className:`overflow-hidden truncate whitespace-nowrap`,children:n}),(0,P.jsx)(f,{size:12,className:`text-text-tertiary shrink-0`})]}),(0,P.jsx)(`span`,{className:`text-micro text-text-tertiary pl-1`,children:t.roleLabel??t.name})]}),e.mappingSupported?(0,P.jsxs)(`div`,{className:`flex flex-col gap-1 min-w-0`,children:[(0,P.jsxs)(`button`,{type:`button`,onClick:()=>{F(t.alias),L(`target`)},className:`font-mono tabular-nums flex w-full min-w-0 cursor-pointer items-center justify-between gap-4 rounded-inset border px-5 py-3.5 text-left text-badge ${i?`border-inner-border bg-surface-2 text-text-tertiary`:`border-accent bg-accent-soft text-text-primary`}`,children:[(0,P.jsx)(`span`,{className:`overflow-hidden truncate whitespace-nowrap`,children:r.length>0?r:`Not routed`}),(0,P.jsx)(f,{size:12,className:`text-text-tertiary shrink-0`})]}),(0,P.jsxs)(`span`,{className:`pl-1 text-micro ${r.length===0?`text-text-tertiary`:`text-accent`}`,children:[r.length===0?`not routed`:`rerouted`,r.length>0?(0,P.jsx)(`button`,{type:`button`,onClick:()=>M(e=>({...e,[t.alias]:``})),className:`ml-3 border-none bg-none p-0 text-micro text-text-tertiary cursor-pointer underline`,children:`clear`}):null]}),fe.has(t.alias)?(0,P.jsxs)(`span`,{className:`mt-1 flex items-center gap-3 text-micro text-orange`,children:[(0,P.jsx)(`span`,{children:J===`blacklist`?`This key blocks the target model.`:`This key does not allow the target model.`}),(0,P.jsx)(`button`,{type:`button`,disabled:U.isPending,onClick:()=>pe(r),className:`border-none bg-none p-0 text-micro font-semibold text-accent underline ${U.isPending?`cursor-not-allowed`:`cursor-pointer`}`,children:J===`blacklist`?`Unblock`:`Allow this model`})]}):null]}):null]},t.alias)})})]})]}),he&&e.guideSteps?(0,P.jsxs)(v,{children:[(0,P.jsx)(_,{title:`Guide`,subtitle:`Manual setup steps for tools that do not support file injection.`,icon:(0,P.jsx)(ce,{size:16})}),(0,P.jsx)(g,{children:(0,P.jsx)(`ol`,{className:`flex flex-col gap-6 pl-9`,children:e.guideSteps.map(e=>(0,P.jsxs)(`li`,{children:[(0,P.jsx)(`strong`,{className:`text-body`,children:e.title}),e.desc?(0,P.jsx)(`p`,{className:`text-caption text-text-secondary mt-2`,children:e.desc}):null,e.value?(0,P.jsx)(`pre`,{className:`mt-3 overflow-x-auto rounded-control border border-inner-border bg-surface-2 p-5 font-mono text-badge`,children:e.value.replace(/\{\{baseUrl\}\}/g,u)}):null]},e.step))})})]}):null,(0,P.jsxs)(v,{children:[(0,P.jsx)(_,{title:`Config file`,subtitle:`Download the config file for this CLI tool. The selected API key secret is decrypted automatically into the download.`,icon:(0,P.jsx)(O,{size:16})}),(0,P.jsxs)(g,{className:`flex flex-col gap-6`,children:[(0,P.jsx)(`div`,{className:`flex gap-4 flex-wrap justify-end`,children:(0,P.jsx)(a,{variant:`primary`,icon:(0,P.jsx)(c,{size:14}),onClick:()=>{if(!r){o.error(`Select an API key first.`);return}W.mutate({toolId:e.id,keyId:r,input:ge()},{onSuccess:e=>{ee(e.filename,e.content,e.mimeType),o.success(`Downloaded ${e.filename}.`)},onError:e=>o.error(p(e,`Could not download config.`))})},disabled:W.isPending||!q,children:W.isPending?`Downloading…`:`Download config`})}),q?null:(0,P.jsx)(`p`,{className:`text-badge text-text-tertiary text-right`,children:G?.keyMode===`share`?`A share template has no secret to write into a config. Select a personal key to download the config file.`:`Select an Apikey Name above to enable downloading the config file.`})]})]}),(0,P.jsx)(re,{open:N!==null,onClose:()=>F(null),selected:N?[I===`target`?j[N]??C[N]??``:C[N]??``]:[],onToggle:()=>{},onSelectOne:e=>{N&&(I===`target`?M(t=>({...t,[N]:e})):w(t=>({...t,[N]:e}))),F(null)},title:N?`Select ${N} model`:`Select model`,multi:!1})]})}export{F as default};
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{o as e,r as t,t as n}from"./jsx-runtime-CXUMngI_.js";import{r}from"./terminal-Bd4ee8TA.js";import{l as i,s as a}from"./numbers-Bs4LOOuf.js";import{n as o,r as s,t as c}from"./state-DYdQmdAl.js";import{t as l}from"./dialog-BtpsJU98.js";import{t as u}from"./rotate-ccw-CufcqITi.js";import{C as d,T as f,v as p}from"./assert-NHwYq95t.js";import{o as m,t as h}from"./query-keys-D7wX2vM5.js";import{t as g}from"./useMutation-w4u4CVue.js";import{O as _,R as v,n as y,v as b}from"./entry-CkryrIea.js";import{t as x}from"./badge-6uzDDvqs.js";import{n as S}from"./inline-BcNGF8Gr.js";import{t as C}from"./layout-C8mer_Ak.js";async function w(e){return m(`/quota/overview`,e?{signal:e}:void 0)}function T(){return d({queryKey:h.quota.all,queryFn:e=>w(p(e)),staleTime:1/0,refetchInterval:!1,refetchIntervalInBackground:!1,refetchOnMount:!1,refetchOnWindowFocus:!1,placeholderData:e=>e})}function E(e){let t=f(),n=h.quota.account(e);return g({mutationFn:async()=>m(`/accounts/${encodeURIComponent(e)}/quota/refresh`,{method:`POST`}),onSuccess:async()=>{await Promise.all([t.invalidateQueries({queryKey:n,exact:!0}),t.invalidateQueries({queryKey:h.quota.all}),t.invalidateQueries({queryKey:h.providers.all})])}})}var D=100;function O(){let e=f();return g({mutationFn:async e=>{let t=[];for(let n=0;n<e.length;n+=D)t.push(e.slice(n,n+D));let n=await Promise.all(t.map(e=>m(`/quota/refresh`,{method:`POST`,body:JSON.stringify({accountIds:e})}))),r=n.flatMap(e=>e.failures);return{ok:r.length===0,queued:n.reduce((e,t)=>e+t.queued,0),succeeded:n.reduce((e,t)=>e+t.succeeded,0),failed:n.reduce((e,t)=>e+t.failed,0),failures:r}},onSuccess:async(t,n)=>{let r=new Set(t.failures.map(e=>e.accountId)),i=n.filter(e=>!r.has(e));await Promise.all([...i.map(t=>e.invalidateQueries({queryKey:h.quota.account(t),exact:!0})),e.invalidateQueries({queryKey:h.quota.all})])}})}function k(){let e=f();return g({mutationFn:({id:e,active:t})=>m(`/accounts/${encodeURIComponent(e)}`,{method:`PATCH`,body:JSON.stringify({active:t})}),onSuccess:async()=>{await Promise.all([e.invalidateQueries({queryKey:h.quota.all}),e.invalidateQueries({queryKey:h.providers.all})])}})}function A(){let e=f();return g({mutationFn:async({ids:e,active:t})=>{e.length!==0&&await m(`/accounts/batch`,{method:`PATCH`,body:JSON.stringify({ids:e,active:t})})},onSuccess:async()=>{await Promise.all([e.invalidateQueries({queryKey:h.quota.all}),e.invalidateQueries({queryKey:h.providers.all})])}})}function j(){let e=f();return g({mutationFn:async e=>{await m(`/accounts/${encodeURIComponent(e)}`,{method:`DELETE`})},onSuccess:async(t,n)=>{e.removeQueries({queryKey:h.quota.account(n),exact:!0}),await Promise.all([e.invalidateQueries({queryKey:h.quota.all}),e.invalidateQueries({queryKey:h.providers.all})])}})}var M=new Set([`workbuddy`,`workbuddycn`,`cb`,`cbcn`]);function N(e){return M.has(e.trim().toLowerCase())}function P(e){let t=f();return g({mutationFn:async()=>{if(e.length===0)throw Error(`Select a provider first`);return m(`/quota/daily-growth`,{method:`POST`,body:JSON.stringify({providerId:e})})},onSuccess:async()=>{await Promise.all([t.invalidateQueries({queryKey:h.quota.all}),t.invalidateQueries({queryKey:h.providers.all})])}})}function F(e){let t=f();return g({mutationFn:async()=>{let t=await m(`/accounts/${encodeURIComponent(e)}/activity-report`,{method:`POST`}),n=t.message??`Activity reported`,r=await m(`/accounts/${encodeURIComponent(e)}/checkin`,{method:`POST`}),i=r.message??`Daily check-in done`;return{ok:t.ok,message:t.ok?`Report success · Check-in ${i}`:`Report failed: ${n} · Check-in ${i}`,checkin:r,report:t}},onSuccess:async()=>{await Promise.all([t.invalidateQueries({queryKey:h.quota.all}),t.invalidateQueries({queryKey:h.providers.all})])}})}var I=new Set([`codex`,`claude`,`anthropic`]);function L(e){return I.has(e.trim().toLowerCase())}function R(e,t){return d({queryKey:h.quota.resets(e),queryFn:({signal:t})=>m(`/accounts/${encodeURIComponent(e)}/resets`,t?{signal:t}:void 0),enabled:t,staleTime:3e4})}function z(e){let t=f();return g({mutationFn:async t=>m(`/accounts/${encodeURIComponent(e)}/reset`,{method:`POST`,body:JSON.stringify(t??{})}),onSettled:async()=>{await Promise.all([t.invalidateQueries({queryKey:h.quota.all}),t.invalidateQueries({queryKey:h.providers.all})])}})}var B=e(t(),1),V=n();function H(e){return e===`cooldown`}function U(e){return e===`active`?`ok`:e===`disabled`?`disabled`:`warn`}function W(e,t){return e?{title:`Enable static-token mode?`,message:`${t} will be used exactly as issued and will not be refreshed. Re-enable refresh before this token expires.`,confirmLabel:`Enable static mode`}:{title:`Re-enable credential refresh?`,message:`${t} will return to the OAuth refresh sweep. Use this only when a refresh token is available.`,confirmLabel:`Re-enable refresh`}}function G({summary:e,onClose:t}){let{providerId:n,accountId:d,title:f,status:p,errorCategory:m,errorMessage:h}=e,g=y(n,d),w=b(),T=g.data??[],E=_(),D=e.credentialKind===`oauth`||e.credentialMode===`jwt`||e.staticToken===!0,[O,k]=(0,B.useState)(e.staticToken===!0),[A,j]=(0,B.useState)(null);return(0,B.useEffect)(()=>k(e.staticToken===!0),[e.staticToken]),(0,V.jsx)(l,{open:!0,onClose:t,title:`Health & Error Log — ${f}`,size:`lg`,children:(0,V.jsxs)(`div`,{className:`flex flex-col gap-7 min-w-0`,children:[(0,V.jsxs)(`div`,{className:`flex flex-wrap items-center justify-between gap-5 rounded-control border border-inner-border bg-surface-2 py-5 px-7`,children:[(0,V.jsxs)(`div`,{className:`min-w-0`,children:[(0,V.jsxs)(S,{gap:`8px`,className:`flex-wrap`,children:[(0,V.jsx)(`span`,{className:`text-body font-semibold`,children:`Account status:`}),(0,V.jsx)(x,{tone:U(p),dot:!0,children:p.toUpperCase()}),m?(0,V.jsx)(x,{tone:`warn`,title:h??void 0,children:m}):null]}),h?(0,V.jsx)(`p`,{className:`text-badge text-red mt-2 wrap-anywhere`,children:h}):null]}),H(p)?(0,V.jsx)(i,{size:`sm`,variant:`secondary`,icon:(0,V.jsx)(u,{size:12,className:w.isPending?`animate-spin`:``}),disabled:w.isPending,onClick:()=>w.mutate({providerId:n,accountId:d},{onSuccess:()=>void g.refetch()}),children:w.isPending?`Recovering...`:`Recover Now`}):null]}),D?(0,V.jsxs)(`div`,{className:`flex items-center justify-between gap-6 rounded-control border border-inner-border bg-surface-2 py-5 px-7`,children:[(0,V.jsxs)(`div`,{children:[(0,V.jsx)(`div`,{className:`text-caption font-semibold`,children:`Credential management`}),(0,V.jsx)(`div`,{className:`text-badge text-text-tertiary`,children:O?`Static mode: no refresh request will be sent.`:`OAuth refresh is enabled for this account.`})]}),(0,V.jsx)(i,{size:`sm`,variant:O?`primary`:`secondary`,disabled:E.isPending,onClick:()=>j(!O),children:O?`Static · no refresh`:`Enable static mode`})]}):null,(0,V.jsxs)(`div`,{className:`min-w-0`,children:[(0,V.jsxs)(`h4`,{className:`text-caption font-semibold text-text-secondary mb-4`,children:[`Transition Events & Audit (`,T.length,`)`]}),g.isPending?(0,V.jsx)(s,{compact:!0,label:`Loading health events...`}):g.isError?(0,V.jsx)(o,{compact:!0,message:`Failed to load health events`,onRetry:()=>void g.refetch()}):T.length===0?(0,V.jsx)(c,{compact:!0,title:`No health events recorded`,message:e.emptyMessage,icon:(0,V.jsx)(r,{size:20})}):(0,V.jsx)(`div`,{className:`rounded-inset border border-inner-border`,children:(0,V.jsx)(C,{maxHeight:480,scrollRegion:!0,headers:[`Timestamp`,`From → To`,`Model`,`Category`,`Reason / Details`],children:T.map(e=>(0,V.jsxs)(`tr`,{children:[(0,V.jsx)(`td`,{className:`text-badge text-text-tertiary whitespace-nowrap`,children:new Date(e.createdAt).toLocaleString()}),(0,V.jsxs)(`td`,{className:`whitespace-nowrap`,children:[(0,V.jsxs)(`span`,{className:`text-badge text-text-secondary`,children:[e.fromStatus??`new`,` →`,` `]}),(0,V.jsx)(x,{tone:U(e.toStatus),children:e.toStatus})]}),(0,V.jsx)(`td`,{className:`font-mono tabular-nums text-badge text-text-secondary wrap-anywhere`,children:e.modelId??`—`}),(0,V.jsx)(`td`,{className:`font-mono tabular-nums text-badge text-text-secondary`,children:e.errorCategory??`—`}),(0,V.jsx)(`td`,{className:`text-badge text-text-primary min-w-110 max-w-260 wrap-anywhere`,children:e.reason??`—`})]},e.id))})})]}),(0,V.jsx)(v,{open:A!==null,onClose:()=>j(null),onConfirm:()=>{let e=A===!0;E.mutate({providerId:n,accountId:d,request:{staticToken:e}},{onSuccess:()=>{k(e),j(null),a.success(e?`Static mode enabled`:`Credential refresh re-enabled`,f),g.refetch()},onError:e=>a.error(`Credential update failed`,e instanceof Error?e.message:`Unable to update credential mode`)})},title:W(A===!0,f).title,message:W(A===!0,f).message,confirmLabel:W(A===!0,f).confirmLabel,cancelLabel:`Cancel`}),(0,V.jsx)(S,{justify:`flex-end`,gap:`0px`,children:(0,V.jsx)(i,{variant:`secondary`,size:`sm`,onClick:t,children:`Close`})})]})})}export{j as a,O as c,F as d,P as f,R as i,A as l,N as n,T as o,k as p,L as r,E as s,G as t,z as u};
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{n as e,o as t,r as n,t as r}from"./jsx-runtime-CXUMngI_.js";import{a as i,i as a,n as o,o as s,r as c}from"./model-bans-DeVLDA7I.js";import{n as l,t as u}from"./fingerprint-pattern-BWuHtbW4.js";import{E as d,O as f,b as p,l as m,s as h,w as g}from"./numbers-Bs4LOOuf.js";import{t as _}from"./chevron-right-BXIy5l3h.js";import{t as v}from"./SortableList-DWvKiuSl.js";import{t as y}from"./download-A0SW9OCW.js";import{t as b}from"./eye-off-Bk5g2luF.js";import{t as x}from"./eye-C_hawfH4.js";import{t as S}from"./gauge-BbIMy7MB.js";import{n as C,r as w,t as ee}from"./state-DYdQmdAl.js";import{t as T}from"./key-round-UkavsGxp.js";import{n as E,r as D,t as O}from"./clipboard-button-NreUx6c8.js";import{t as k}from"./dialog-BtpsJU98.js";import{t as te}from"./pencil-768ysRAr.js";import{t as A}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 N}from"./search-JOKus9rb.js";import{t as ne}from"./shield-check-DVS0Foqh.js";import{n as re,r as ie,t as P}from"./settings-mpmXTgUa.js";import{t as ae}from"./sparkles-BRaRUxAU.js";import{t as oe}from"./trash-Cppn5c3R.js";import{b as se,x as ce}from"./assert-NHwYq95t.js";import{l as F}from"./query-keys-D7wX2vM5.js";import{A as le,R as I,at as ue,rt as de,st as fe}from"./entry-CkryrIea.js";import{n as L,r as R,t as z}from"./card--AtFedAq.js";import{t as pe}from"./use-clipboard-B0Sy03fE.js";import{n as me}from"./download-dRFo5_Wu.js";import{c as B,d as V}from"./tokens-DDxMbSRL.js";import{t as H}from"./badge-6uzDDvqs.js";import{n as U,t as he}from"./input-BG41et9G.js";import{t as W}from"./select-BO6h8yNv.js";import{t as ge}from"./ModelPicker-CWkTUtpX.js";import{t as G}from"./switch-CidSfbG7.js";import{t as K}from"./stack-DwLmjUFK.js";import{n as q}from"./inline-BcNGF8Gr.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-uNywYlfv.js";import{a as X,i as Z,n as De,r as Oe}from"./cli-tools-B3e7dx3N.js";import{t as ke}from"./layout-C8mer_Ak.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":N,"dashboard:read":x,"dashboard:write":te,"providers:read":de,"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)(U,{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:()=>h.success(`Saved remote routing for ${e.name}.`),onError:e=>h.error(F(e,`Could not save remote routing.`))})}}),(0,$.jsx)(m,{variant:`secondary`,size:`sm`,disabled:n||d||!c&&u===0,onClick:()=>s.mutate({toolId:e.id,keyId:t},{onSuccess:()=>h.success(`Cleared remote routes for ${e.name}.`),onError:e=>h.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,f]=(0,Q.useState)(t?.keyPrefix??``),[p,g]=(0,Q.useState)(``),[_,v]=(0,Q.useState)(t?.requestsPerMinute?.toString()??``),[y,b]=(0,Q.useState)(Je(t?.dailyTokenLimit??null)),[x,C]=(0,Q.useState)(Je(t?.monthlyTokenLimit??null)),[w,ee]=(0,Q.useState)(Je(t?.lifetimeTokenBudget??null)),[D,O]=(0,Q.useState)(t?.lifetimeTokenBudget==null?`recurring`:`one-time`),[k,te]=(0,Q.useState)(t?.maxConcurrentRequests?.toString()??``),[A,j]=(0,Q.useState)(()=>nt(t)),[N,re]=(0,Q.useState)(t?.modelAccessMode??`whitelist`),[P,ae]=(0,Q.useState)(()=>{let e=t?.modelList;return e?[...e]:[]}),[oe,le]=(0,Q.useState)(!1),[I,de]=(0,Q.useState)(()=>{let e=t?.clientRouterDenylist;return e?[...new Set(e.map(ce).filter(e=>e!==void 0))]:[]}),[L,R]=(0,Q.useState)(()=>t?.notesTitle??``),[z,pe]=(0,Q.useState)(()=>t?.notesSubtitle??``),[me,B]=(0,Q.useState)(()=>t?.notesBody??``),[V,H]=(0,Q.useState)(t?.sharePopupEnabled??!1),[W,K]=(0,Q.useState)(null),[q,_e]=(0,Q.useState)(!1),[ve,J]=(0,Q.useState)(null),be=t?.sharePopupImageMime!==void 0,xe=W??(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=D===`one-time`,Z=e===`edit`,De=ye(),Oe=e=>j(t=>t.includes(e)?t.filter(t=>t!==e):[...t,e]),ke=()=>{r({label:o.trim(),...et(Y,p,d),scopes:A,modelAccessMode:N,modelList:P,clientRouterDenylist:I,requestsPerMinute:Xe(_),maxConcurrentRequests:Xe(k),dailyTokenLimit:X?null:qe(y)??null,monthlyTokenLimit:X?null:qe(x)??null,lifetimeTokenBudget:X?qe(w)??null:null,notesTitle:L.trim(),notesSubtitle:z.trim(),notesBody:me.trim(),sharePopupEnabled:V,...W===null?q?{sharePopupImage:null}:{}:{sharePopupImage:W},sharePopupTitle:Se.trim(),sharePopupBody:we.trim()})};return(0,Q.useEffect)(()=>{a?.((0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(m,{variant:`secondary`,size:`sm`,onClick:i,disabled:n,children:`Cancel`}),(0,$.jsx)(m,{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)(T,{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)(U,{label:`Name`,value:o,onChange:e=>c(e.target.value),placeholder:`ci-key…`,disabled:n,autoFocus:!0}),(0,$.jsx)(U,{label:`Key prefix`,value:d,onChange:e=>f(e.target.value),placeholder:`pn-carte- (default)…`,disabled:n||p.trim().length>0})]}),Y===`personal`&&(0,$.jsx)(U,{label:`Custom API key value (optional)`,value:p,onChange:e=>g(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)(U,{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)(E,{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)(U,{label:`Title`,value:L,onChange:e=>R(e.target.value),placeholder:`e.g. Bansos Token`,disabled:n}),(0,$.jsx)(U,{label:`Subtitle`,value:z,onChange:e=>pe(e.target.value),placeholder:`e.g. Come and save your tokens`,disabled:n}),(0,$.jsx)(U,{label:`Body`,value:me,onChange:e=>B(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)(S,{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)(U,{label:`Requests per minute`,type:`number`,min:`0`,value:_,onChange:e=>v(e.target.value),placeholder:`Unlimited…`,disabled:n}),(0,$.jsx)(U,{label:`Max concurrent requests`,type:`number`,min:`0`,value:k,onChange:e=>te(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)(S,{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:()=>O(`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:()=>O(`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:w,onChange:ee,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:y,onChange:b,disabled:n}),(0,$.jsx)(Ze,{label:`Monthly token limit`,value:x,onChange:C,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)(m,{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)(U,{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)(ie,{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)(fe,{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)(ne,{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":N===e,disabled:n,onClick:()=>re(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 ${N===e?`var(--accent)`:`var(--inner-border)`}`,background:N===e?`var(--accent-soft)`:`var(--surface-2)`,color:N===e?`var(--accent)`:`var(--text-secondary)`,cursor:n?`not-allowed`:`pointer`,opacity:n?.6:1},children:[e===`whitelist`?`Whitelist`:`Blacklist`,e===`whitelist`?(0,$.jsx)(ne,{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:N===`whitelist`?`Allowed models`:`Blocked models`}),(0,$.jsx)(`div`,{className:`flex gap-3 flex-wrap mb-4`,children:P.length===0?(0,$.jsx)(`span`,{className:`mt-1 text-badge text-text-tertiary`,children:N===`whitelist`?`All models allowed`:`No models blocked`}):P.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:()=>ae(t=>t.filter(t=>t!==e)),disabled:n,className:`bg-transparent border-0 cursor-pointer p-0 flex`,children:`×`})]},e))}),(0,$.jsx)(m,{variant:`secondary`,size:`sm`,type:`button`,onClick:()=>le(!0),disabled:n,children:`Browse models`}),(0,$.jsx)(ge,{open:oe,onClose:()=>le(!1),selected:P,onToggle:e=>ae(t=>t.includes(e)?t.filter(t=>t!==e):[...t,e]),title:N===`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:se.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=>h.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)(ue,{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)(m,{variant:`secondary`,size:`sm`,icon:(0,$.jsx)(y,{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`),h.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)(w,{compact:!0,label:`Loading banned users…`}):t.isError?(0,$.jsx)(C,{compact:!0,message:F(t.error,`Could not load banned users.`),onRetry:()=>void t.refetch()}):a.length===0?(0,$.jsx)(ee,{compact:!0,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)(m,{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)(m,{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),h.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)(m,{variant:`secondary`,size:`sm`,icon:i?(0,$.jsx)(b,{size:13}):(0,$.jsx)(x,{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=>h.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?`${B(e)} · Unlimited`:`${B(e)} / ${B(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?`${B(e)} tokens consumed, no lifetime budget set`:`${B(e)} of ${B(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:B(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:[B(e),` / `,B(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`:`${B(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":`${B(e)} of ${B(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)(w,{label:`Loading activity…`,compact:!0});if(n.isError)return(0,$.jsx)(C,{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:B(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:B(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,a]=(0,Q.useState)(`active`),[o,c]=(0,Q.useState)(null),l=ve(),u=we(),d=Te(),f=xe(e.id),p=e.keyMode!==`share`,g=J(p?null:e.id),v=g.data??[],y=i===`active`?v.filter(e=>e.revokedAt===null):v.filter(e=>e.revokedAt!==null),b=v.filter(e=>e.revokedAt===null&&e.allTime.totalTokens===0);(0,Q.useEffect)(()=>{r(null)},[e.id]);let x=async(t,n)=>{let r=0;for(let n of t)try{await d.mutateAsync({parentKeyId:e.id,childKeyId:n.id}),r+=1}catch{}await g.refetch();let i=t.length-r;i>0?h.error(`Revoked ${r} of ${t.length} ${n}; ${i} failed.`):h.success(`Revoked ${r} ${n}.`)},S=l.isPending||u.isPending,[T,E]=(0,Q.useState)(!1),k=async()=>{if(p){let n=await u.mutateAsync({keyId:e.id});t?.(n.secret);return}await l.mutateAsync({keyId:e.id,regenerate:!0}),h.success(`Link regenerated; the previous URL no longer works.`)},te=()=>{l.mutate({keyId:e.id},{onError:e=>h.error(F(e,`Could not create link.`))})},A=f.data?.url??null,M=dt(p);return(0,$.jsxs)(`div`,{className:`grid min-w-0 gap-6`,children:[(0,$.jsx)(`section`,{"aria-label":`Link`,children:f.isPending?(0,$.jsx)(w,{label:`Loading link…`,compact:!0}):f.isError?(0,$.jsx)(C,{compact:!0,message:F(f.error,`Could not load the link.`),onRetry:()=>void f.refetch()}):A?(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:A,className:`min-w-0 flex-auto overflow-hidden font-mono text-caption text-text-primary text-ellipsis whitespace-nowrap`,children:A}),(0,$.jsx)(O,{value:A,size:`sm`,variant:`secondary`,label:`Copy`,copiedLabel:`Copied`}),(0,$.jsx)(m,{variant:`secondary`,size:`sm`,icon:(0,$.jsx)(Ne,{size:13}),loading:S,disabled:S,onClick:()=>E(!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)(m,{variant:`primary`,size:`sm`,loading:S,disabled:S,onClick:te,children:`Create link`})]})}),p?(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,p?(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:B(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(v),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(v)]}),(0,$.jsx)(`div`,{className:`inline-flex items-center gap-1 rounded-chip border border-inner-border p-0.5`,role:`group`,"aria-label":`Filter recipients by status`,children:[`active`,`revoked`].map(e=>(0,$.jsx)(`button`,{type:`button`,"aria-pressed":i===e,onClick:()=>a(e),className:`rounded-chip border-0 px-3 py-1 text-badge font-semibold capitalize cursor-pointer ${i===e?`bg-surface-1 text-text-primary`:`bg-transparent text-text-secondary hover:text-text-primary`}`,children:e},e))}),o===null?null:(0,$.jsxs)(`span`,{className:`inline-flex items-center gap-2 rounded-chip border border-danger-border bg-surface-2 px-3 py-1`,children:[(0,$.jsx)(`span`,{className:`text-badge text-text-secondary`,children:o===`all`?`Revoke ${y.length}?`:`Revoke ${b.length} unused?`}),(0,$.jsx)(m,{variant:`danger`,size:`sm`,loading:d.isPending,onClick:()=>{let e=o===`all`?y:b,t=o===`all`?`recipients`:`unused recipients`;c(null),x(e,t)},children:`Confirm`}),(0,$.jsx)(m,{variant:`ghost`,size:`sm`,onClick:()=>c(null),children:`Cancel`})]}),(0,$.jsx)(m,{variant:`ghost`,size:`sm`,disabled:b.length===0||d.isPending,title:b.length===0?`No active recipient has spent zero tokens`:`Revoke ${b.length} recipient${b.length===1?``:`s`} that never spent a token`,onClick:()=>c(`unused`),icon:(0,$.jsx)(s,{size:13}),children:`Revoke no-usage`}),(0,$.jsx)(m,{variant:`ghost`,size:`sm`,disabled:y.length===0||d.isPending,title:y.length===0?`Nothing to revoke in this view`:`Revoke all ${y.length} listed recipient${y.length===1?``:`s`}`,onClick:()=>c(`all`),icon:(0,$.jsx)(s,{size:13}),children:`Revoke all`}),(0,$.jsx)(m,{variant:`ghost`,size:`sm`,onClick:()=>void g.refetch(),icon:(0,$.jsx)(j,{size:13,className:g.isFetching?`animate-spin`:void 0}),children:`Refresh`})]}),g.isPending&&!g.data?(0,$.jsx)(w,{label:`Loading recipients…`,compact:!0}):g.isError?(0,$.jsx)(C,{compact:!0,message:F(g.error,`Could not load recipients.`),onRetry:()=>void g.refetch()}):y.length===0?(0,$.jsx)(ee,{compact:!0,title:i===`revoked`?`No revoked recipients`:`No active recipients`,message:i===`revoked`?`Revoked keys appear here once a recipient's key is revoked.`:`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:y.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)(fe,{size:14}):(0,$.jsx)(_,{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:B(t.today.totalTokens)}),(0,$.jsx)(`td`,{children:B(t.allTime.totalTokens)}),(0,$.jsx)(`td`,{children:ct(t.lastUsedAt)}),(0,$.jsx)(`td`,{className:`text-right whitespace-nowrap`,children:(0,$.jsx)(m,{variant:`danger`,size:`sm`,disabled:d.isPending||!!t.revokedAt,onClick:()=>d.mutate({parentKeyId:e.id,childKeyId:t.id},{onError:e=>h.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:T,onClose:()=>E(!1),onConfirm:k,title:M.title,message:M.message,confirmLabel:M.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=le(),n=Se(),r=ye(),i=Ce(),a=we(),o=_e(),s=be(),{copy:c}=pe(),[l,u]=(0,Q.useState)(!1),[f,p]=(0,Q.useState)(null),[g,_]=(0,Q.useState)(null),[y,b]=(0,Q.useState)(null),[x,S]=(0,Q.useState)(null),[E,D]=(0,Q.useState)(null),[O,j]=(0,Q.useState)(!1),[M,N]=(0,Q.useState)(null),[ne,re]=(0,Q.useState)(null),ie=e.data??[],P=t.data?.isPlatformAdmin??!1,ae=e=>{S(null),D(e)},se=async e=>{try{let{secret:t}=await o.mutateAsync({keyId:e.id});await c(t)?h.success(`API key copied`):h.error(`Clipboard unavailable on this origin`)}catch(e){h.error(F(e,`Could not reveal API key.`))}},ce=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)(T,{size:16}),action:(0,$.jsxs)(`div`,{className:`flex items-center gap-4`,children:[P?(0,$.jsx)(m,{variant:`secondary`,size:`sm`,onClick:()=>j(!0),icon:(0,$.jsx)(Ie,{size:13}),children:`Banned Users`}):null,(0,$.jsx)(m,{variant:`primary`,size:`sm`,onClick:()=>{S(null),u(!0)},icon:(0,$.jsx)(A,{size:13}),children:`Create Key`})]})}),(0,$.jsx)(L,{children:e.isPending&&!e.data?(0,$.jsx)(w,{label:`Loading API keys…`}):e.isError&&!e.data?(0,$.jsx)(C,{message:F(e.error,`Could not load credentials.`),onRetry:()=>void e.refetch()}):ie.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)(T,{size:20})}):(0,$.jsx)(`div`,{className:`scroll-region max-h-280 pr-1`,children:(0,$.jsx)(v,{items:ie,label:`API credentials`,disabled:s.isPending,onReorder:e=>s.mutate(e,{onError:e=>h.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)(H,{tone:e.revokedAt?`err`:e.enabled?`ok`:`warn`,children:e.revokedAt?`revoked`:e.enabled?`enabled`:`disabled`}),(0,$.jsx)(H,{tone:e.keyMode===`share`?`accent`:`default`,children:e.keyMode===`share`?`share template`:`personal`}),e.isDefault?(0,$.jsx)(H,{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`}),B(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`}),V(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`}),V(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`}),V(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`}),V(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`}),V(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:()=>h.success(`${e.label||`API credential`} ${t?`enabled`:`disabled`}`),onError:e=>h.error(F(e,`Could not update credential state.`))})}),(0,$.jsx)(m,{variant:`secondary`,size:`sm`,icon:(0,$.jsx)(te,{size:13}),onClick:()=>{S(null),p(e)},children:`Edit`}),!e.revokedAt&&e.keyMode!==`share`?(0,$.jsx)(m,{variant:`secondary`,size:`sm`,icon:(0,$.jsx)(d,{size:13}),onClick:()=>void se(e),disabled:o.isPending,children:`Copy`}):null,!e.revokedAt&&(0,$.jsx)(m,{variant:`secondary`,size:`sm`,icon:(0,$.jsx)(Fe,{size:13}),onClick:()=>ae(e),children:e.keyMode===`share`?`Recipients`:`Share`}),!e.revokedAt&&e.keyMode!==`share`&&(0,$.jsx)(m,{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)(m,{variant:`danger`,size:`sm`,icon:(0,$.jsx)(oe,{size:13}),onClick:()=>_(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:M,children:(0,$.jsx)(rt,{mode:`create`,record:null,busy:n.isPending,onClose:()=>u(!1),actionsRef:N,onDone:e=>{n.mutate(e,{onSuccess:t=>{u(!1),S(tt(e.keyMode,t.secret)),h.success(`API key created`)},onError:e=>h.error(F(e,`Could not create API key.`))})}})}),(0,$.jsx)(k,{open:f!==null,onClose:()=>p(null),title:`Edit API Key`,description:`Update the key's model access, permissions, limits, share notes, and optional public popup.`,size:`lg`,footer:ne,children:(0,$.jsx)(rt,{mode:`edit`,record:f,busy:r.isPending,onClose:()=>p(null),actionsRef:re,onDone:e=>{f&&r.mutate({keyId:f.id,request:e},{onSuccess:t=>{p(null),S(tt(e.keyMode,t.secret)),h.success(`API key updated`)},onError:e=>h.error(F(e,`Could not update API key.`))})}})}),E?(0,$.jsx)(vt,{parent:E,onClose:()=>D(null),onSecretRevealed:e=>{D(null),S(e)}}):null,(0,$.jsx)(I,{open:y!==null,onClose:()=>b(null),onConfirm:async()=>{if(!y)return;let e=await a.mutateAsync({keyId:y.id});S(e.secret),h.success(`Rotated ${y.label||y.id}.`),b(null)},title:ce.title,message:ce.message,confirmLabel:ce.confirmLabel,danger:!0}),(0,$.jsx)(I,{open:g!==null,onClose:()=>_(null),onConfirm:async()=>{g&&(await i.mutateAsync(g.id),h.success(`Revoked ${g.label||g.id}.`),_(null))},title:`Revoke API key?`,message:g?`Revoke ${g.label||g.id}? Clients using it will be rejected.${g.keyMode===`share`?` All child keys and enrollment links will also be revoked.`:``}`:`Revoke this API key?`,confirmLabel:`Revoke`,danger:!0}),O?(0,$.jsx)(ot,{onClose:()=>j(!1)}):null,(0,$.jsx)(it,{secret:x,onClose:()=>S(null)})]})}function bt(){let e=P(),t=re(),n=e.data;if(e.isPending)return(0,$.jsx)(w,{label:`Loading compression settings…`});if(e.isError||!n)return(0,$.jsx)(C,{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)(W,{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=>h.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=>h.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)(ae,{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)(W,{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=>h.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=>h.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)(p,{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}=pe(2e3),n=`${window.location.origin}/v1`,r=async()=>{if(await t(n)){h.success(`Gateway endpoint copied`,n);return}h.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)(m,{variant:`secondary`,size:`sm`,icon:e?(0,$.jsx)(f,{size:13}):(0,$.jsx)(d,{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};
|
|
Binary file
|