cartethyia 2.5.4 → 2.5.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +13 -4
- package/README.md +2 -2
- package/dist/dashboard/assets/{BannedPage-C2orBsI2.js → BannedPage-DE2607Tb.js} +1 -1
- package/dist/dashboard/assets/BannedPage-DE2607Tb.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-Duv6mfrl.js +1 -0
- package/dist/dashboard/assets/CliToolDetailPage-Duv6mfrl.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-BV6pxVQo.js → CliToolsPage-C2PNYWzk.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-C2PNYWzk.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-TXP9OfAM.js +1 -0
- package/dist/dashboard/assets/CombosPage-TXP9OfAM.js.gz +0 -0
- package/dist/dashboard/assets/{ConsoleLogPage-CW-wPmJ6.js → ConsoleLogPage-B-9xnAQN.js} +1 -1
- package/dist/dashboard/assets/ConsoleLogPage-B-9xnAQN.js.gz +0 -0
- package/dist/dashboard/assets/{CustomizationPage-0p8uX7vB.js → CustomizationPage-xeax9nJt.js} +1 -1
- package/dist/dashboard/assets/CustomizationPage-xeax9nJt.js.gz +0 -0
- package/dist/dashboard/assets/{HealthEventsModal-BjqSMfko.js → HealthEventsModal-Df4jjaHJ.js} +1 -1
- package/dist/dashboard/assets/HealthEventsModal-Df4jjaHJ.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-BvzCx8Kr.js → LoginPage-Dyojsm84.js} +1 -1
- package/dist/dashboard/assets/LoginPage-Dyojsm84.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-C7pDb41R.js +1 -0
- package/dist/dashboard/assets/ModelPicker-C7pDb41R.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-BJfg4ow6.js +1 -0
- package/dist/dashboard/assets/OverviewPage-BJfg4ow6.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-ChCX1gmT.js +3 -0
- package/dist/dashboard/assets/ProviderDetailPage-ChCX1gmT.js.gz +0 -0
- package/dist/dashboard/assets/{ProvidersPage-Js9jUPSb.js → ProvidersPage-DeWSOEUO.js} +1 -1
- package/dist/dashboard/assets/ProvidersPage-DeWSOEUO.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-CkpWln2S.js +7 -0
- package/dist/dashboard/assets/ProxyPage-CkpWln2S.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-B3VOgQmt.js +1 -0
- package/dist/dashboard/assets/QuotaPage-B3VOgQmt.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-C_klIZom.js +1 -0
- package/dist/dashboard/assets/SettingsPage-C_klIZom.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-DDbMQsoo.js → SetupPage-BYph-InK.js} +1 -1
- package/dist/dashboard/assets/SetupPage-BYph-InK.js.gz +0 -0
- package/dist/dashboard/assets/{SortableList-CB3Z7VTf.js → SortableList-CvyQCYWJ.js} +1 -1
- package/dist/dashboard/assets/SortableList-CvyQCYWJ.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-D95UCPHI.js → StudioPage-Cm1JYilI.js} +3 -3
- package/dist/dashboard/assets/StudioPage-Cm1JYilI.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-DlFnc3_S.js +56 -0
- package/dist/dashboard/assets/UsagePage-DlFnc3_S.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-DQYLQxpW.js +1 -0
- package/dist/dashboard/assets/api-keys-DQYLQxpW.js.gz +0 -0
- package/dist/dashboard/assets/app-CX_Z8cMs.css +2 -0
- package/dist/dashboard/assets/app-CX_Z8cMs.css.gz +0 -0
- package/dist/dashboard/assets/{assert-Chi7pCnt.js → assert-C9FD_JwJ.js} +1 -1
- package/dist/dashboard/assets/assert-C9FD_JwJ.js.gz +0 -0
- package/dist/dashboard/assets/{badge-D4pQapKj.js → badge-CR72dasT.js} +1 -1
- package/dist/dashboard/assets/badge-CR72dasT.js.gz +0 -0
- package/dist/dashboard/assets/{banner-DH6cg-_B.js → banner-CI9_eSfO.js} +1 -1
- package/dist/dashboard/assets/banner-CI9_eSfO.js.gz +0 -0
- package/dist/dashboard/assets/{card-BKqlUOe4.js → card-DS7CQEsO.js} +1 -1
- package/dist/dashboard/assets/card-DS7CQEsO.js.gz +0 -0
- package/dist/dashboard/assets/{cli-tools-fTgljTGA.js → cli-tools-CNqzrfdb.js} +1 -1
- package/dist/dashboard/assets/cli-tools-CNqzrfdb.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-D5Ys_sM0.js → clipboard-button-611pSsaX.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-611pSsaX.js.gz +0 -0
- package/dist/dashboard/assets/{fingerprint-pattern-BWuHtbW4.js → concurrency-BDF0ok7y.js} +1 -1
- package/dist/dashboard/assets/concurrency-BDF0ok7y.js.gz +0 -0
- package/dist/dashboard/assets/dialog-k37exjNt.js +1 -0
- package/dist/dashboard/assets/dialog-k37exjNt.js.gz +0 -0
- package/dist/dashboard/assets/drawer-CX7N8ucR.js +1 -0
- package/dist/dashboard/assets/drawer-CX7N8ucR.js.gz +0 -0
- package/dist/dashboard/assets/{entry-CQBr6U9c.js → entry-B9Xvtnfj.js} +1 -1
- package/dist/dashboard/assets/entry-B9Xvtnfj.js.gz +0 -0
- package/dist/dashboard/assets/entry-Bog0p925.js +4 -0
- package/dist/dashboard/assets/entry-Bog0p925.js.gz +0 -0
- package/dist/dashboard/assets/entry-CTP8AyiS.js +1 -0
- package/dist/dashboard/assets/entry-CTP8AyiS.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-BlptL8Uf.js → github-badge-sltk1qUi.js} +1 -1
- package/dist/dashboard/assets/github-badge-sltk1qUi.js.gz +0 -0
- package/dist/dashboard/assets/{index-js6TJvdr.js → index-CZQIrdoX.js} +2 -2
- package/dist/dashboard/assets/index-CZQIrdoX.js.gz +0 -0
- package/dist/dashboard/assets/{inline-DUWDSmDV.js → inline-BhY2sSnO.js} +1 -1
- package/dist/dashboard/assets/inline-BhY2sSnO.js.gz +0 -0
- package/dist/dashboard/assets/{input-LVjgZq4f.js → input-V1umFiBk.js} +1 -1
- package/dist/dashboard/assets/input-V1umFiBk.js.gz +0 -0
- package/dist/dashboard/assets/layout-BBOYycyS.js +1 -0
- package/dist/dashboard/assets/layout-BBOYycyS.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-DupvLfDx.js → list-row-1zzZYM0B.js} +1 -1
- package/dist/dashboard/assets/list-row-1zzZYM0B.js.gz +0 -0
- package/dist/dashboard/assets/{live-C4pEJUqI.js → live-BivGfuUs.js} +1 -1
- package/dist/dashboard/assets/live-BivGfuUs.js.gz +0 -0
- package/dist/dashboard/assets/{model-bans-BRiuufkB.js → model-bans-CmffjLuG.js} +1 -1
- package/dist/dashboard/assets/model-bans-CmffjLuG.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-B4ROwk8L.js → mutation-46P0oWMe.js} +1 -1
- package/dist/dashboard/assets/mutation-46P0oWMe.js.gz +0 -0
- package/dist/dashboard/assets/numbers-DkfMR5Qs.js +2 -0
- package/dist/dashboard/assets/numbers-DkfMR5Qs.js.gz +0 -0
- package/dist/dashboard/assets/{query-keys-gmjzC3RS.js → query-keys-BFhGpwR1.js} +1 -1
- package/dist/dashboard/assets/query-keys-BFhGpwR1.js.gz +0 -0
- package/dist/dashboard/assets/{routing-C7tBT5o-.js → routing-CAb4F37R.js} +1 -1
- package/dist/dashboard/assets/routing-CAb4F37R.js.gz +0 -0
- package/dist/dashboard/assets/{select-WUCXorXw.js → select-BPpSsBgL.js} +1 -1
- package/dist/dashboard/assets/select-BPpSsBgL.js.gz +0 -0
- package/dist/dashboard/assets/{settings-C_zKl1GS.js → settings-DNX9sydi.js} +1 -1
- package/dist/dashboard/assets/settings-DNX9sydi.js.gz +0 -0
- package/dist/dashboard/assets/{stack-_3O0F8Xn.js → stack-BHTfXdz_.js} +1 -1
- package/dist/dashboard/assets/stack-BHTfXdz_.js.gz +0 -0
- package/dist/dashboard/assets/{state-BgTxHcJS.js → state-mPDRlQkl.js} +1 -1
- package/dist/dashboard/assets/state-mPDRlQkl.js.gz +0 -0
- package/dist/dashboard/assets/{switch-DEjTtOWf.js → switch-DOmJxg7H.js} +1 -1
- package/dist/dashboard/assets/switch-DOmJxg7H.js.gz +0 -0
- package/dist/dashboard/assets/tokens-COzario9.js +1 -0
- package/dist/dashboard/assets/tokens-COzario9.js.gz +0 -0
- package/dist/dashboard/assets/use-modal-focus-By7oJoGd.js +1 -0
- package/dist/dashboard/assets/use-modal-focus-By7oJoGd.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-BWSC0UAS.js → useMutation-Dem0lXQj.js} +1 -1
- package/dist/dashboard/assets/useMutation-Dem0lXQj.js.gz +0 -0
- package/dist/dashboard/index.html +1 -1
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +54 -54
- package/migrations/README.md +52 -52
- package/migrations/lite-sqlite3/0000_baseline.sql +2 -2
- package/package.json +95 -95
- package/scripts/cli/admin.ts +125 -0
- package/scripts/cli/{ui/background.ts → background.ts} +1 -1
- package/scripts/cli/{command/console-menu.ts → console-menu.ts} +275 -244
- package/scripts/cli/doctor.ts +37 -0
- package/scripts/cli/{support/env.ts → env.ts} +99 -99
- package/scripts/cli/{gateway/spawn.ts → gateway.ts} +112 -24
- package/scripts/cli/instance.ts +224 -0
- package/scripts/cli/main.ts +61 -18
- package/scripts/cli/{support/platform.ts → platform.ts} +64 -64
- package/scripts/cli/port.ts +100 -0
- package/scripts/cli/{support/services.ts → services.ts} +88 -88
- package/scripts/cli/{command/setup.ts → setup.ts} +35 -34
- package/scripts/cli/{command/start.ts → start.ts} +10 -5
- package/scripts/cli/{support/startup-signal.ts → startup-signal.ts} +23 -23
- package/scripts/cli/tray/index.ts +62 -12
- package/scripts/cli/{command/tray.ts → tray.ts} +208 -208
- package/scripts/cli/{command/update.ts → update.ts} +39 -39
- package/scripts/cli/version.ts +67 -0
- package/scripts/cli/{ui/wordmark.ts → wordmark.ts} +58 -58
- package/src/boot/app.ts +697 -693
- package/src/boot/dependencies.ts +647 -646
- package/src/boot/timeout.ts +2 -13
- package/src/config/cooldowns.ts +186 -138
- package/src/config/network.ts +8 -14
- package/src/config/providers.ts +7 -10
- package/src/config/runtime.ts +76 -76
- package/src/config/security.ts +15 -0
- package/src/config/spec.ts +20 -30
- package/src/config/telemetry.ts +0 -20
- package/src/config/timeouts.ts +0 -6
- package/src/console/admin/model-abuse/contracts.ts +28 -6
- package/src/console/admin/model-abuse/routes.ts +56 -3
- package/src/console/api-keys/contracts.ts +410 -414
- package/src/console/api-keys/routes.ts +881 -880
- package/src/console/auth/credentials.ts +3 -3
- package/src/console/auth/setup-service.ts +2 -2
- package/src/console/backup/export-endpoint.ts +6 -2
- package/src/console/backup/nine-router.ts +2 -5
- package/src/console/backup/service.ts +4 -0
- package/src/console/backup/store.ts +192 -29
- package/src/console/backup/validate.ts +3 -45
- package/src/console/cli-tools/fs-ops.ts +487 -487
- package/src/console/console-router-guards.ts +1 -1
- package/src/console/data-management/contracts.ts +13 -6
- package/src/console/data-management/operations.ts +23 -18
- package/src/console/data-management/routes.ts +0 -1
- package/src/console/domain-registration.ts +10 -5
- package/src/console/observability/live.ts +22 -0
- package/src/console/observability/store.ts +39 -16
- package/src/console/observability/usage-dimensions.ts +4 -4
- package/src/console/providers/catalog/catalog-projections.ts +6 -7
- package/src/console/providers/catalog/model-operations.ts +5 -2
- package/src/console/providers/catalog/provider-operations.ts +4 -4
- package/src/console/providers/catalog/public-model-store.ts +8 -11
- package/src/console/providers/catalog/store.ts +1553 -1553
- package/src/console/providers/oauth/routes.ts +8 -12
- package/src/console/quota/account-quota-tenant.ts +6 -6
- package/src/console/quota/quota-view.ts +1 -1
- package/src/console/remote-support/contracts.ts +61 -0
- package/src/console/remote-support/routes.ts +65 -0
- package/src/console/remote-support/service.ts +207 -0
- package/src/console/response-compression.ts +0 -3
- package/src/console/routing/pools/probe-result.ts +3 -2
- package/src/console/routing/pools/probe-verdict.ts +132 -132
- package/src/console/routing/pools/store.ts +8 -8
- package/src/console/settings/contracts.ts +58 -2
- package/src/console/settings/store.ts +30 -3
- package/src/console/shared/cursor.ts +8 -8
- package/src/console/shared/errors.ts +1 -3
- package/src/network/http2-bidi.ts +2 -4
- package/src/network/pool/agent.ts +478 -478
- package/src/network/pool/selector.ts +5 -4
- package/src/network/retry.ts +1 -2
- package/src/network/ssrf.ts +5 -4
- package/src/observability/event-compaction.ts +80 -78
- package/src/observability/event-day-index.ts +13 -15
- package/src/observability/event-log.ts +4 -4
- package/src/observability/logger.ts +6 -6
- package/src/observability/payload-capture.ts +151 -33
- package/src/observability/payload-store.ts +397 -440
- package/src/observability/redaction.ts +5 -6
- package/src/observability/request-notify.ts +43 -0
- package/src/observability/telemetry-status.ts +8 -9
- package/src/observability/token-speed.ts +5 -4
- package/src/persistence/db-mode.ts +7 -7
- package/src/persistence/readiness.ts +1 -1
- package/src/persistence/schema.ts +1406 -1402
- package/src/persistence/shared-cache.ts +1 -2
- package/src/persistence/stores/api-key-store.ts +18 -6
- package/src/persistence/stores/share-store.ts +4 -3
- package/src/persistence/stores/telemetry-store.ts +236 -237
- package/src/protocol/canonical-model.ts +11 -3
- package/src/protocol/codecs.ts +138 -4
- package/src/protocol/gateway-error.ts +6 -4
- package/src/protocol/request/chat.ts +21 -1
- package/src/protocol/request/codex.ts +10 -1
- package/src/protocol/request/messages.ts +4 -5
- package/src/protocol/request/responses.ts +9 -1
- package/src/protocol/response/chat.ts +34 -4
- package/src/protocol/response/codex.ts +7 -4
- package/src/protocol/response/dsml.ts +140 -28
- package/src/protocol/response/responses.ts +11 -5
- package/src/protocol/stream-error-frames.ts +4 -5
- package/src/providers/authentication/jwt-validator.ts +9 -10
- package/src/providers/authentication/oauth-refresh-service.ts +9 -11
- package/src/providers/configured-openai-providers.ts +134 -136
- package/src/providers/discovery/catalog/catalog-blob.ts +18 -18
- package/src/providers/discovery/catalog/catalog-format.ts +3 -6
- package/src/providers/discovery/models-dev-catalog.ts +354 -354
- package/src/providers/discovery/probe-phases.ts +4 -4
- package/src/providers/discovery/probe-wire.ts +6 -13
- package/src/providers/discovery/probing-service.ts +1117 -1045
- package/src/providers/integrations/alibabacp/alibabacp.ts +1 -1
- package/src/providers/integrations/antigravity/antigravity-quota.ts +5 -7
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +23 -2
- package/src/providers/integrations/buddy/buddy-checkin.ts +1 -4
- package/src/providers/integrations/buddy/buddy-cli-headers.ts +23 -6
- package/src/providers/integrations/buddy/buddy-conversation-headers.ts +0 -1
- package/src/providers/integrations/buddy/buddy-identity.ts +52 -0
- package/src/providers/integrations/buddy/buddy-media.ts +2 -2
- package/src/providers/integrations/buddy/buddy-usage-store.ts +3 -4
- package/src/providers/integrations/buddy/codebuddy/quota.ts +2 -13
- package/src/providers/integrations/buddy/codebuddy/shared.ts +118 -115
- package/src/providers/integrations/buddy/workbuddy/quota.ts +0 -11
- package/src/providers/integrations/buddy/workbuddy/shared.ts +5 -2
- package/src/providers/integrations/claude/claude.ts +608 -608
- package/src/providers/integrations/cline/cline-quota.ts +132 -132
- package/src/providers/integrations/cline/cline.ts +537 -539
- package/src/providers/integrations/codex/codex-oauth.ts +1 -1
- package/src/providers/integrations/commandcode/commandcode.ts +674 -675
- package/src/providers/integrations/commandcode/discovery.ts +117 -116
- package/src/providers/integrations/cursor/cursor-auth.ts +418 -418
- package/src/providers/integrations/cursor/cursor-quota.ts +309 -310
- package/src/providers/integrations/cursor/cursor.ts +11 -163
- package/src/providers/integrations/factory/factory-models.ts +3 -5
- package/src/providers/integrations/grok/grok-install-id.ts +2 -3
- package/src/providers/integrations/grok/grok.ts +6 -7
- package/src/providers/integrations/hermes.ts +14 -18
- package/src/providers/integrations/kiro/kiro-discovery.ts +1 -1
- package/src/providers/integrations/kiro/kiro.ts +1 -1
- package/src/providers/integrations/mistral.ts +6 -6
- package/src/providers/integrations/opencode/fingerprint.ts +3 -3
- package/src/providers/integrations/opencode/provider.ts +8 -8
- package/src/providers/integrations/qoder/qoder-catalog.ts +450 -450
- package/src/providers/integrations/qoder/qoder-oauth.ts +388 -310
- package/src/providers/integrations/qoder/qoder.ts +884 -865
- package/src/providers/integrations/shared/connect.ts +6 -6
- package/src/providers/integrations/trae/trae-catalog.ts +1 -2
- package/src/providers/integrations/trae/trae.ts +2 -2
- package/src/providers/integrations/zcode/oauth.ts +6 -1
- package/src/providers/operations/account-health-service.ts +1528 -1523
- package/src/providers/operations/account-reset-service.ts +1 -1
- package/src/providers/operations/client-versions.ts +6 -6
- package/src/providers/operations/custom-cli-headers.ts +3 -4
- package/src/providers/operations/provider-catalog-seeder.ts +95 -95
- package/src/providers/operations/provider-catalog-service.ts +3 -4
- package/src/providers/provider-metadata.ts +4 -8
- package/src/providers/provider-registry.ts +6 -7
- package/src/providers/provider-usage.ts +1 -1
- package/src/security/abuse.ts +15 -10
- package/src/security/access-control.ts +144 -146
- package/src/security/admission/contracts.ts +11 -0
- package/src/security/admission/in-memory-store.ts +4 -3
- package/src/security/admission/lease-sweep.ts +6 -6
- package/src/security/admission/reasons.ts +5 -1
- package/src/security/admission/redis-store.ts +211 -210
- package/src/security/admission/service.ts +4 -0
- package/src/security/api-key-auth.ts +504 -504
- package/src/security/ip-boundary.ts +155 -15
- package/src/security/model-abuse.ts +366 -94
- package/src/security/outbound-headers.ts +3 -4
- package/src/shared/sleep.ts +4 -0
- package/src/transport/dispatch/attempt-finalize.ts +47 -5
- package/src/transport/dispatch/attempt-loop.ts +531 -530
- package/src/transport/dispatch/stream-bridge.ts +1 -3
- package/src/transport/dispatch/streaming/finalize.ts +1 -1
- package/src/transport/dispatch/streaming/stream.ts +5 -6
- package/src/transport/failure-policy.ts +162 -8
- package/src/transport/guards/auth-guard.ts +7 -4
- package/src/transport/guards/concurrency-guard.ts +0 -5
- package/src/transport/guards/error-lifecycle.ts +13 -6
- package/src/transport/guards/pipeline.ts +15 -3
- package/src/transport/guards/request-context.ts +8 -2
- package/src/transport/request/inflight.ts +5 -6
- package/src/transport/request/preparer.ts +2 -2
- package/src/transport/request/state.ts +12 -8
- package/src/transport/routing/admission.ts +4 -0
- package/src/transport/routing/route-catalog.ts +8 -1
- package/src/transport/routing/route-model.ts +46 -14
- package/src/transport/routing/router.ts +21 -14
- package/src/transport/surface/completion.ts +2 -3
- package/src/transport/surface/dialects.ts +5 -6
- package/src/transport/surface/responses/encode.ts +38 -0
- package/src/transport/surface/responses/parse.ts +4 -0
- package/src/transport/translation/capabilities.ts +2 -3
- package/src/transport/translation/quirks.ts +3 -3
- package/src/workers/buddy-growth-tasks.ts +5 -10
- package/src/workers/daily-checkin.ts +17 -23
- package/src/workers/quota-refresh-sweep.ts +26 -26
- package/dist/dashboard/assets/BannedPage-C2orBsI2.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-DUcZ6pMB.js +0 -1
- package/dist/dashboard/assets/CliToolDetailPage-DUcZ6pMB.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-BV6pxVQo.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-Bsan4679.js +0 -1
- package/dist/dashboard/assets/CombosPage-Bsan4679.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-CW-wPmJ6.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-0p8uX7vB.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-BjqSMfko.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-BvzCx8Kr.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-BY1aKEkP.js +0 -1
- package/dist/dashboard/assets/ModelPicker-BY1aKEkP.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-DOQpy1y0.js +0 -1
- package/dist/dashboard/assets/OverviewPage-DOQpy1y0.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-ecxyUMzt.js +0 -3
- package/dist/dashboard/assets/ProviderDetailPage-ecxyUMzt.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-Js9jUPSb.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-J3TBi3Kx.js +0 -7
- package/dist/dashboard/assets/ProxyPage-J3TBi3Kx.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-C1gZS6xr.js +0 -1
- package/dist/dashboard/assets/QuotaPage-C1gZS6xr.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CS86ho5X.js +0 -1
- package/dist/dashboard/assets/SettingsPage-CS86ho5X.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-DDbMQsoo.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-CB3Z7VTf.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-D95UCPHI.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-Cnae9SWA.js +0 -56
- package/dist/dashboard/assets/UsagePage-Cnae9SWA.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-VY68Mhd1.js +0 -1
- package/dist/dashboard/assets/api-keys-VY68Mhd1.js.gz +0 -0
- package/dist/dashboard/assets/app-COlK95oi.css +0 -2
- package/dist/dashboard/assets/app-COlK95oi.css.gz +0 -0
- package/dist/dashboard/assets/assert-Chi7pCnt.js.gz +0 -0
- package/dist/dashboard/assets/badge-D4pQapKj.js.gz +0 -0
- package/dist/dashboard/assets/banner-DH6cg-_B.js.gz +0 -0
- package/dist/dashboard/assets/card-BKqlUOe4.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-fTgljTGA.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-D5Ys_sM0.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BYj-8WuK.js +0 -1
- package/dist/dashboard/assets/dialog-BYj-8WuK.js.gz +0 -0
- package/dist/dashboard/assets/drawer-CmXIZ6H1.js +0 -1
- package/dist/dashboard/assets/drawer-CmXIZ6H1.js.gz +0 -0
- package/dist/dashboard/assets/entry-CBg5zeu8.js +0 -1
- package/dist/dashboard/assets/entry-CBg5zeu8.js.gz +0 -0
- package/dist/dashboard/assets/entry-CQBr6U9c.js.gz +0 -0
- package/dist/dashboard/assets/entry-CgdSAzEV.js +0 -4
- package/dist/dashboard/assets/entry-CgdSAzEV.js.gz +0 -0
- package/dist/dashboard/assets/fingerprint-pattern-BWuHtbW4.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-BlptL8Uf.js.gz +0 -0
- package/dist/dashboard/assets/index-js6TJvdr.js.gz +0 -0
- package/dist/dashboard/assets/inline-DUWDSmDV.js.gz +0 -0
- package/dist/dashboard/assets/input-LVjgZq4f.js.gz +0 -0
- package/dist/dashboard/assets/layout-CPfxpsfy.js +0 -1
- package/dist/dashboard/assets/layout-CPfxpsfy.js.gz +0 -0
- package/dist/dashboard/assets/list-row-DupvLfDx.js.gz +0 -0
- package/dist/dashboard/assets/live-C4pEJUqI.js.gz +0 -0
- package/dist/dashboard/assets/model-bans-BRiuufkB.js.gz +0 -0
- package/dist/dashboard/assets/mutation-B4ROwk8L.js.gz +0 -0
- package/dist/dashboard/assets/numbers-YNcHvRY7.js +0 -2
- package/dist/dashboard/assets/numbers-YNcHvRY7.js.gz +0 -0
- package/dist/dashboard/assets/query-keys-gmjzC3RS.js.gz +0 -0
- package/dist/dashboard/assets/routing-C7tBT5o-.js.gz +0 -0
- package/dist/dashboard/assets/select-WUCXorXw.js.gz +0 -0
- package/dist/dashboard/assets/settings-C_zKl1GS.js.gz +0 -0
- package/dist/dashboard/assets/stack-_3O0F8Xn.js.gz +0 -0
- package/dist/dashboard/assets/state-BgTxHcJS.js.gz +0 -0
- package/dist/dashboard/assets/switch-DEjTtOWf.js.gz +0 -0
- package/dist/dashboard/assets/tokens-DDxMbSRL.js +0 -1
- package/dist/dashboard/assets/tokens-DDxMbSRL.js.gz +0 -0
- package/dist/dashboard/assets/use-presence-BSZEBx7U.js +0 -1
- package/dist/dashboard/assets/use-presence-BSZEBx7U.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-BWSC0UAS.js.gz +0 -0
- package/scripts/cli/command/admin.ts +0 -65
- package/scripts/cli/command/doctor.ts +0 -257
- package/scripts/cli/gateway/drain-token.ts +0 -78
- package/scripts/cli/gateway/instance-lock.ts +0 -105
- package/scripts/cli/gateway/stop.ts +0 -51
- package/scripts/cli/support/bun.ts +0 -27
- package/scripts/cli/support/version.ts +0 -37
- /package/dist/dashboard/assets/{app-DCQZRmHY.js → app-BIt446yE.js} +0 -0
- /package/dist/dashboard/assets/{app-DCQZRmHY.js.gz → app-BIt446yE.js.gz} +0 -0
- /package/scripts/cli/{ui/prompt.ts → prompt.ts} +0 -0
|
@@ -226,10 +226,10 @@ function spreadDeadline(cooldownUntil: number | undefined): { cooldown_until?: n
|
|
|
226
226
|
* The soonest moment a quota window the caller is blocked on refills.
|
|
227
227
|
*
|
|
228
228
|
* A usage-floor exclusion is time-bounded by construction — the floor compares
|
|
229
|
-
* the *remaining* allowance, and the window states when that allowance resets
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
229
|
+
* the *remaining* allowance, and the window states when that allowance resets.
|
|
230
|
+
* The exclusion carries a deadline so `plan()` can tell a client how long to
|
|
231
|
+
* wait and `earliestResetMs` reports a value. The minimum across windows, for
|
|
232
|
+
* the same reason `earliestResetMs` takes it: the account
|
|
233
233
|
* can serve again as soon as its first window refills, so quoting the slowest
|
|
234
234
|
* would hold the client off longer than necessary.
|
|
235
235
|
*
|
|
@@ -317,9 +317,8 @@ export class EligibilityEvaluator {
|
|
|
317
317
|
return { eligible: false, reason: "model_cooldown", candidate, ...spreadDeadline(state.cooldown_until) };
|
|
318
318
|
if (state.health_status === "cooldown" && state.cooldown_kind === "hard")
|
|
319
319
|
return { eligible: false, reason: "cooldown_hard", candidate, ...spreadDeadline(state.cooldown_until) };
|
|
320
|
-
// The floor is checked here, not after selection:
|
|
321
|
-
//
|
|
322
|
-
// its floor still won the route. An account with no reading is left
|
|
320
|
+
// The floor is checked here, not after selection: an account below its
|
|
321
|
+
// floor must not win the route. An account with no reading is left
|
|
323
322
|
// eligible — a missing quota fetch is not proof the allowance is gone.
|
|
324
323
|
if (belowUsageFloor(state))
|
|
325
324
|
return {
|
|
@@ -487,8 +486,8 @@ export class RoutingEngine {
|
|
|
487
486
|
* Stamps each account with its provider's usage floor and the quota reading
|
|
488
487
|
* the console last fetched, so eligibility can reject an account that has
|
|
489
488
|
* spent down to the floor. The reading is the live cache, not the route
|
|
490
|
-
* snapshot: a snapshot rebuilt every few minutes
|
|
491
|
-
*
|
|
489
|
+
* snapshot: a snapshot rebuilt every few minutes routes on a stale balance
|
|
490
|
+
* and lets a spent account through.
|
|
492
491
|
*/
|
|
493
492
|
private async applyUsageFloor(
|
|
494
493
|
candidates: readonly RouteCandidate[],
|
|
@@ -598,10 +597,10 @@ export class RoutingEngine {
|
|
|
598
597
|
d.reason === "model_cooldown" ||
|
|
599
598
|
// A usage-floor exclusion whose window states a reset is the same
|
|
600
599
|
// shape as the two above: the account is resting until a named
|
|
601
|
-
// instant, not broken.
|
|
602
|
-
//
|
|
603
|
-
//
|
|
604
|
-
//
|
|
600
|
+
// instant, not broken. Falling through to 503 tells the client the
|
|
601
|
+
// model is down while every account serving it is merely waiting for
|
|
602
|
+
// its window to refill — the "503 DOWN while the model is allowed"
|
|
603
|
+
// report again. Only a floor with *no* stated
|
|
605
604
|
// reset stays unavailable: there is nothing to retry against yet.
|
|
606
605
|
(d.reason === "quota_exhausted" && typeof d.cooldown_until === "number"),
|
|
607
606
|
);
|
|
@@ -828,7 +827,15 @@ export class RoutingEngine {
|
|
|
828
827
|
const admitted = await this.admission.admit(candidate);
|
|
829
828
|
if (admitted.admitted) return this.reservations.acquire(candidate, plan.revision);
|
|
830
829
|
}
|
|
831
|
-
|
|
830
|
+
// Last candidate's identity names the provider the plan was actually for;
|
|
831
|
+
// every candidate shares it, so any one of them is representative.
|
|
832
|
+
const last = plan.candidates[plan.candidates.length - 1];
|
|
833
|
+
throw capacityExhaustedError({
|
|
834
|
+
...(last?.provider_id !== undefined ? { providerId: last.provider_id } : {}),
|
|
835
|
+
...(plan.resolved_model !== undefined ? { model: plan.resolved_model } : {}),
|
|
836
|
+
candidateCount: plan.candidates.length,
|
|
837
|
+
...(last?.max_inflight !== undefined ? { maxInflight: last.max_inflight } : {}),
|
|
838
|
+
});
|
|
832
839
|
}
|
|
833
840
|
async release(reservation: Reservation): Promise<void> {
|
|
834
841
|
await this.admission.release(reservation);
|
|
@@ -45,9 +45,8 @@ function stopReason(events: readonly CanonicalEvent[]): string | null {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
|
-
* Completion wire usage. Delegates to the chat builder (same wire family)
|
|
49
|
-
* completion responses
|
|
50
|
-
* them (intended fix; previously only the three totals were forwarded).
|
|
48
|
+
* Completion wire usage. Delegates to the chat builder (same wire family), so
|
|
49
|
+
* completion responses carry cache/reasoning details, not just the three totals.
|
|
51
50
|
*/
|
|
52
51
|
function usage(events: readonly CanonicalEvent[]): OpenAiUsage | undefined {
|
|
53
52
|
const terminal = [...events].reverse().find((event) => event.type === "terminal");
|
|
@@ -79,12 +79,11 @@ function parseToolObject(item: Record<string, unknown>, dialect: ToolDialect): T
|
|
|
79
79
|
}
|
|
80
80
|
} else if (dialect === "responses") {
|
|
81
81
|
// A Responses caller may still send the Chat-shaped envelope — the two
|
|
82
|
-
// spellings are interchangeable in the wild
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
// Chat dialect does before falling back to the flat Responses spelling.
|
|
82
|
+
// spellings are interchangeable in the wild. Unwrap it the way the Chat
|
|
83
|
+
// dialect does before falling back to the flat Responses spelling; taking
|
|
84
|
+
// `schema = item` instead ships the whole tool object upstream as the
|
|
85
|
+
// parameter schema and comes back as `invalid_function_parameters`
|
|
86
|
+
// (CodeBuddy 11129).
|
|
88
87
|
const wrapped = isRecord(item["function"]) ? item["function"] : undefined;
|
|
89
88
|
schema =
|
|
90
89
|
item["input_schema"] ??
|
|
@@ -704,6 +704,44 @@ export class ResponsesEventEncoder extends SurfaceStreamEncoder<CanonicalEvent,
|
|
|
704
704
|
throw new ResponsesLifecycleError("Responses stream ended without a terminal event");
|
|
705
705
|
return [];
|
|
706
706
|
}
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* Encodes a fatal stream error as `response.error` followed immediately by
|
|
710
|
+
* `response.failed`.
|
|
711
|
+
*
|
|
712
|
+
* The OpenAI Responses SDK (and any strict client) requires a terminal
|
|
713
|
+
* lifecycle event (`response.completed`, `response.failed`, or
|
|
714
|
+
* `response.incomplete`) to close the stream cleanly. Emitting only
|
|
715
|
+
* `response.error` and then closing the SSE connection causes the SDK to
|
|
716
|
+
* throw "stream closed before a terminal response event was received".
|
|
717
|
+
*
|
|
718
|
+
* `response.error` is kept so the client can surface the error message;
|
|
719
|
+
* `response.failed` immediately follows so the client's state machine
|
|
720
|
+
* reaches a terminal state and stops waiting.
|
|
721
|
+
*/
|
|
722
|
+
encodeError(input: {
|
|
723
|
+
readonly origin: string;
|
|
724
|
+
readonly code: string;
|
|
725
|
+
readonly message: string;
|
|
726
|
+
readonly details: unknown;
|
|
727
|
+
}): ResponsesWireEvent[] {
|
|
728
|
+
if (this.terminal) return [];
|
|
729
|
+
this.terminal = true;
|
|
730
|
+
const errorEvent = this.emit("response.error", {
|
|
731
|
+
origin: input.origin,
|
|
732
|
+
code: input.code,
|
|
733
|
+
message: input.message,
|
|
734
|
+
details: input.details,
|
|
735
|
+
});
|
|
736
|
+
const response = this.responseObject("failed");
|
|
737
|
+
response["error"] = {
|
|
738
|
+
type: "server_error",
|
|
739
|
+
code: input.code,
|
|
740
|
+
message: input.message,
|
|
741
|
+
};
|
|
742
|
+
const failedEvent = this.emit("response.failed", { response });
|
|
743
|
+
return [errorEvent, failedEvent];
|
|
744
|
+
}
|
|
707
745
|
}
|
|
708
746
|
|
|
709
747
|
/** Encode synchronous canonical events into ordered Responses wire events. */
|
|
@@ -456,6 +456,10 @@ function parseInputItem(item: ResponsesInputItem): {
|
|
|
456
456
|
encrypted_content: encrypted,
|
|
457
457
|
};
|
|
458
458
|
if (summary !== undefined) reasoningPart.summary = summary;
|
|
459
|
+
// The upstream binds encrypted reasoning to the item id it was issued
|
|
460
|
+
// for; a replay under a different id is rejected outright.
|
|
461
|
+
const itemId = typeof item["id"] === "string" ? item["id"] : undefined;
|
|
462
|
+
if (itemId !== undefined) reasoningPart.item_id = itemId;
|
|
459
463
|
return {
|
|
460
464
|
message: { role: "assistant", content: [reasoningPart] },
|
|
461
465
|
metadata: itemMetadata(item),
|
|
@@ -334,9 +334,8 @@ export function deriveRequiredCapabilities(
|
|
|
334
334
|
for (const key of Object.keys(request.generation_controls)) {
|
|
335
335
|
// `extension:*` keys are passthrough hints to the wire codec, not route
|
|
336
336
|
// capabilities: the codec reads the names it understands and drops the
|
|
337
|
-
// rest. Gating them here
|
|
338
|
-
//
|
|
339
|
-
// `metadata`, etc. and re-warning per request.
|
|
337
|
+
// rest. Gating them here would strip `include_usage`, `prompt_cache_key`,
|
|
338
|
+
// `user`, `metadata`, etc. and re-warn per request.
|
|
340
339
|
if (key.startsWith("extension:")) continue;
|
|
341
340
|
capabilities.push(`generation_control:${key}`);
|
|
342
341
|
}
|
|
@@ -180,9 +180,9 @@ export function applyParamQuirks(
|
|
|
180
180
|
const quirk = findParamQuirk(providerId, request.model);
|
|
181
181
|
// The thinking-sampling strip is universal, not quirk-gated: extended
|
|
182
182
|
// thinking rejects temperature/top_p/top_k on every Messages-wire upstream.
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
//
|
|
183
|
+
// It runs before the early return below so a provider with no PARAM_QUIRKS
|
|
184
|
+
// row still gets it — otherwise a strict upstream takes a 400 the strip
|
|
185
|
+
// exists to prevent. Declarative rows still apply after.
|
|
186
186
|
|
|
187
187
|
let controls = request.generation_controls;
|
|
188
188
|
let changed = false;
|
|
@@ -22,6 +22,7 @@ import { randomUUID } from "node:crypto";
|
|
|
22
22
|
import type { CartethyiaDatabase } from "../persistence/db-handle";
|
|
23
23
|
import { buddyTaskLedger, providerAccounts } from "../persistence/schema";
|
|
24
24
|
import { log } from "../observability/logger";
|
|
25
|
+
import { sleep } from "../shared/sleep";
|
|
25
26
|
import { buddyAccountUid } from "../providers/integrations/buddy/buddy-oauth-shared";
|
|
26
27
|
import {
|
|
27
28
|
adoptFirstBuddy,
|
|
@@ -207,12 +208,6 @@ async function forAccount(
|
|
|
207
208
|
return run(call);
|
|
208
209
|
}
|
|
209
210
|
|
|
210
|
-
/** Pause between accounts, so one pass does not look like a burst upstream. */
|
|
211
|
-
function sleep(ms: number): Promise<void> {
|
|
212
|
-
const { promise, resolve } = Promise.withResolvers<void>();
|
|
213
|
-
setTimeout(resolve, ms);
|
|
214
|
-
return promise;
|
|
215
|
-
}
|
|
216
211
|
/** Walks the accounts, skipping any already settled today. */
|
|
217
212
|
async function runTask(
|
|
218
213
|
deps: BuddyTaskDeps,
|
|
@@ -353,10 +348,10 @@ export function buddyTrialTask(deps: BuddyTaskDeps) {
|
|
|
353
348
|
* Growth-activity report: the `/v2/report` batch that lights the login streak and
|
|
354
349
|
* scores the daily growth event.
|
|
355
350
|
*
|
|
356
|
-
* It
|
|
357
|
-
*
|
|
358
|
-
*
|
|
359
|
-
*
|
|
351
|
+
* It is a once-a-day task with its own ledger, gated by the same
|
|
352
|
+
* (day, account, task) ledger as every other growth task: running it on the
|
|
353
|
+
* quota sweep would post two rounds of four events per account per sweep, and
|
|
354
|
+
* the sweep runs every minute.
|
|
360
355
|
*
|
|
361
356
|
* The report is not a credit claim, so its failure never marks an account
|
|
362
357
|
* unhealthy — the result is logged and the ledger is stamped either way.
|
|
@@ -120,9 +120,8 @@ export function ledgerKey(dayKey: string, accountId: string): string {
|
|
|
120
120
|
* With Redis the `SET NX EX` is the cross-process guard (two gateway instances
|
|
121
121
|
* cannot both attempt the same account). Without it the ledger *table* is the
|
|
122
122
|
* guard, and its composite PK makes the insert atomic — so Lite records the
|
|
123
|
-
* attempt it just made instead of silently returning `true`, which
|
|
124
|
-
*
|
|
125
|
-
* forever.
|
|
123
|
+
* attempt it just made instead of silently returning `true`, which leaves the
|
|
124
|
+
* console unable to answer "attempted today" and shows "Check-in due" forever.
|
|
126
125
|
*/
|
|
127
126
|
async function reserveDaySlot(
|
|
128
127
|
db: CartethyiaDatabase | undefined,
|
|
@@ -160,8 +159,8 @@ async function reserveDaySlot(
|
|
|
160
159
|
* Releases today's slot for many accounts in one round-trip.
|
|
161
160
|
*
|
|
162
161
|
* The sweep reserves a slot per account as it walks the wave; releasing them
|
|
163
|
-
* one at a time on the error path
|
|
164
|
-
* failed account. A pass that budgets out with twenty failures
|
|
162
|
+
* one at a time on the error path turns every failed pass into a Redis RTT per
|
|
163
|
+
* failed account. A pass that budgets out with twenty failures pays twenty
|
|
165
164
|
* round-trips for bookkeeping that has no bearing on the claim itself.
|
|
166
165
|
*
|
|
167
166
|
* A pipeline is used when Redis is present (one `MULTI/DEL` for the whole
|
|
@@ -246,8 +245,8 @@ export async function markCheckinAttempted(args: {
|
|
|
246
245
|
* The read belongs next to the writes because there are two places the marker
|
|
247
246
|
* can live — the Redis key when Redis is present, the `daily_checkin_ledger`
|
|
248
247
|
* row when it is not — and only this module knows which one its own writes
|
|
249
|
-
* used. A caller that
|
|
250
|
-
* every Lite deployment, where the sweep
|
|
248
|
+
* used. A caller that reads the Redis key directly answers "not attempted" on
|
|
249
|
+
* every Lite deployment, where the sweep writes the row.
|
|
251
250
|
*
|
|
252
251
|
* Absent or unreadable means unknown, never "not claimed": the sweep may
|
|
253
252
|
* simply not have reached the account yet.
|
|
@@ -355,9 +354,9 @@ export function supportsDailyCheckin(providerId: string): boolean {
|
|
|
355
354
|
* Wraps a proxy-bound fetcher so a 403 falls back to a direct connection once.
|
|
356
355
|
*
|
|
357
356
|
* A proxy that answers 403 (auth, expired pool credential, a provider that
|
|
358
|
-
* rejects the egress)
|
|
359
|
-
*
|
|
360
|
-
*
|
|
357
|
+
* rejects the egress) would fail the whole claim even though the account's own
|
|
358
|
+
* credential was fine — the proxy is the only thing wrong. One direct retry
|
|
359
|
+
* costs nothing when the proxy was healthy (the first attempt already
|
|
361
360
|
* succeeded) and rescues the claim when it was not.
|
|
362
361
|
*
|
|
363
362
|
* The fallback only fires on 403, not on every error: a 401 means the account
|
|
@@ -375,15 +374,10 @@ export function withDirectFallback(fetcher: typeof fetch): typeof fetch {
|
|
|
375
374
|
return wrapped;
|
|
376
375
|
}
|
|
377
376
|
/**
|
|
378
|
-
* The outcome of one daily check-in pass: the claim alone.
|
|
379
|
-
*
|
|
380
|
-
*
|
|
381
|
-
*
|
|
382
|
-
/**
|
|
383
|
-
* Runs the full daily growth pass for one account, once per calendar day: the
|
|
384
|
-
* check-in claim alone. The growth-activity report moved to its own once-a-day
|
|
385
|
-
* growth task (`buddy-activity-report`) with its own ledger, so it is not
|
|
386
|
-
* re-POSTed on every sweep. See `buddy-growth-tasks.ts`.
|
|
377
|
+
* The outcome of one daily check-in pass: the claim alone. The
|
|
378
|
+
* growth-activity report runs as its own once-a-day growth task
|
|
379
|
+
* (`buddy-activity-report`) with its own ledger, so it is not re-POSTed on
|
|
380
|
+
* every sweep. See `buddy-growth-tasks.ts`.
|
|
387
381
|
*/
|
|
388
382
|
export type DailyGrowthPassResult = {
|
|
389
383
|
readonly checkin: DailyCheckinResult;
|
|
@@ -448,10 +442,10 @@ export async function attemptDailyGrowthPass(args: {
|
|
|
448
442
|
|
|
449
443
|
// Only a transient `error` releases the slot so a later pass today can retry
|
|
450
444
|
// (network, 5xx). `unavailable` is the status probe answering that no
|
|
451
|
-
// activity is running — retrying that every pass
|
|
452
|
-
// slot on a door the provider
|
|
453
|
-
//
|
|
454
|
-
//
|
|
445
|
+
// activity is running — retrying that every pass spends the account's daily
|
|
446
|
+
// slot on a door the provider has closed, and releasing the marker also
|
|
447
|
+
// erases the evidence that anything was attempted: the console then reads
|
|
448
|
+
// "Check-in due" forever while the sweep retries once a minute.
|
|
455
449
|
// `already_claimed`, `not_eligible`, and `event_ended` are terminal for the
|
|
456
450
|
// day and keep the slot.
|
|
457
451
|
return { checkin };
|
|
@@ -91,11 +91,11 @@ const DEFAULT_MAX_CHECKINS_PER_PASS = 10;
|
|
|
91
91
|
*
|
|
92
92
|
* The provider filter is applied *before* the slot count is consulted, and
|
|
93
93
|
* that ordering is the whole point of the function: the sweep's batch is
|
|
94
|
-
* oldest-first across every provider, so a cap checked first
|
|
95
|
-
* non-buddy accounts the ride-along would
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
94
|
+
* oldest-first across every provider, so a cap checked first is spent on
|
|
95
|
+
* non-buddy accounts the ride-along would skip anyway. A buddy account sitting
|
|
96
|
+
* behind them (a `cbcn` row past the tenth older non-buddy row) then never gets
|
|
97
|
+
* its automatic check-in — only a manual press, which bypasses the cap, ever
|
|
98
|
+
* reaches it.
|
|
99
99
|
*/
|
|
100
100
|
export function checkinSlotAvailable(
|
|
101
101
|
providerId: string,
|
|
@@ -114,7 +114,7 @@ export function checkinSlotAvailable(
|
|
|
114
114
|
*
|
|
115
115
|
* Module scope, not per-call: suppression must survive across passes, and one
|
|
116
116
|
* `quotaRefreshSweep` call is one pass. Bounded because it is keyed by account
|
|
117
|
-
* id and
|
|
117
|
+
* id and is never pruned, so an account deleted years ago still holds an entry;
|
|
118
118
|
* the oldest keys are the ones least likely to be announced again.
|
|
119
119
|
*/
|
|
120
120
|
const LAST_SWEEP_LINE_MAX = 512;
|
|
@@ -159,7 +159,7 @@ function quotaPhrase(outcome: QuotaRefreshOutcome): string {
|
|
|
159
159
|
* nothing to report.
|
|
160
160
|
*
|
|
161
161
|
* Only providers that expose the check-in route appear here at all: the sweep
|
|
162
|
-
* skips the rest, and `daily unsupported` on every non-WorkBuddy account
|
|
162
|
+
* skips the rest, and `daily unsupported` on every non-WorkBuddy account is
|
|
163
163
|
* pure noise. The routine "nothing to claim today" states collapse to one
|
|
164
164
|
* short phrase so the line stays scannable; only a real grant or a failure
|
|
165
165
|
* carries detail.
|
|
@@ -238,11 +238,11 @@ function logSweepAccounts(
|
|
|
238
238
|
/**
|
|
239
239
|
* How stale a cached quota reading must be before it is refreshed again.
|
|
240
240
|
*
|
|
241
|
-
* Half the cache TTL. The sweep runs every 60 s, so
|
|
242
|
-
* account
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
241
|
+
* Half the cache TTL. The sweep runs every 60 s, so a 4 min threshold refreshes
|
|
242
|
+
* every account roughly three times per 15 min TTL window — two of those three
|
|
243
|
+
* passes hit the provider and learn nothing the cache did not already know.
|
|
244
|
+
* 8 min is still comfortably inside the TTL, so a reading never goes stale
|
|
245
|
+
* while it waits.
|
|
246
246
|
*/
|
|
247
247
|
const DEFAULT_MIN_AGE_MS = 8 * 60_000;
|
|
248
248
|
const DEFAULT_MAX_PER_PASS = 40;
|
|
@@ -296,7 +296,7 @@ export async function quotaRefreshSweep(deps: QuotaRefreshSweepDeps): Promise<vo
|
|
|
296
296
|
? await deps.listTargets()
|
|
297
297
|
: await listQuotaRefreshTargets(deps.db);
|
|
298
298
|
// Read cache ages in one batched pass per lens: the sweep runs over
|
|
299
|
-
// dozens of accounts each minute, so a `get` per account
|
|
299
|
+
// dozens of accounts each minute, so a `get` per account makes the age
|
|
300
300
|
// scan's latency scale with the account count. `getCachedQuotaEntries`
|
|
301
301
|
// is the cache's own batched reader, so the key format stays owned by
|
|
302
302
|
// the cache module.
|
|
@@ -406,10 +406,10 @@ export async function quotaRefreshSweep(deps: QuotaRefreshSweepDeps): Promise<vo
|
|
|
406
406
|
// The check-in ride-along still runs: the daily credit grant is the
|
|
407
407
|
// *remedy* for an account that just reported an exhausted or unreadable
|
|
408
408
|
// quota, and it is a different upstream call that does not depend on the
|
|
409
|
-
// quota leg. Returning here
|
|
410
|
-
// one that most needs the grant —
|
|
411
|
-
//
|
|
412
|
-
//
|
|
409
|
+
// quota leg. Returning here leaves an account sitting at 0 credits — the
|
|
410
|
+
// one that most needs the grant — with no check-in attempted, so the
|
|
411
|
+
// Quota page reads "Check-in due" while the sweep believes it has done
|
|
412
|
+
// its job. Only a credential we could not resolve at all rules the
|
|
413
413
|
// check-in out; that is the one input both legs share.
|
|
414
414
|
if (outcome.failure === "missing_credential") return;
|
|
415
415
|
}
|
|
@@ -420,16 +420,16 @@ export async function quotaRefreshSweep(deps: QuotaRefreshSweepDeps): Promise<vo
|
|
|
420
420
|
// pass after the first of the day.
|
|
421
421
|
//
|
|
422
422
|
// The provider filter comes *before* the slot is consumed. The batch is
|
|
423
|
-
// oldest-first across every provider, so checking the cap first
|
|
423
|
+
// oldest-first across every provider, so checking the cap first spends the
|
|
424
424
|
// whole per-pass budget on non-buddy accounts that the next line would
|
|
425
|
-
//
|
|
426
|
-
//
|
|
427
|
-
//
|
|
425
|
+
// skip anyway — a buddy account past the cap (a cbcn row behind ten older
|
|
426
|
+
// non-buddy rows) is never checked in automatically, only when an operator
|
|
427
|
+
// presses the button, which bypasses this cap entirely.
|
|
428
428
|
if (!checkinSlotAvailable(entry.target.providerId, checkins, maxCheckins)) return;
|
|
429
429
|
// Claim the slot before the first await. The wave runs its items
|
|
430
430
|
// concurrently, and reading `checkins` only after the growth pass
|
|
431
|
-
//
|
|
432
|
-
// same stale count — the cap then
|
|
431
|
+
// resolves lets every item in the wave pass the cap check against the
|
|
432
|
+
// same stale count — the cap then bounds nothing.
|
|
433
433
|
checkins += 1;
|
|
434
434
|
try {
|
|
435
435
|
// is only for the `preconnect` static Bun adds to its own fetch (the
|
|
@@ -489,9 +489,9 @@ export async function quotaRefreshSweep(deps: QuotaRefreshSweepDeps): Promise<vo
|
|
|
489
489
|
logSweepAccounts(attemptedTargets, quotaOutcomes, checkinOutcomes, failed, claimed);
|
|
490
490
|
|
|
491
491
|
// Release the slots for the accounts whose check-in errored, in one
|
|
492
|
-
// round-trip.
|
|
493
|
-
//
|
|
494
|
-
//
|
|
492
|
+
// round-trip. Releasing inline per account makes a pass that budgets out with
|
|
493
|
+
// twenty failures pay twenty Redis round-trips for bookkeeping that has no
|
|
494
|
+
// bearing on the claim itself.
|
|
495
495
|
const releasedAccountIds: string[] = [];
|
|
496
496
|
for (const outcome of checkinOutcomes.values()) {
|
|
497
497
|
if (outcome.state === "error") releasedAccountIds.push(outcome.accountId);
|
|
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{c as a,s as o}from"./numbers-YNcHvRY7.js";import{t as ee}from"./circle-check-BU3v_mFW.js";import{n as s,r as c}from"./state-BgTxHcJS.js";import{t as l}from"./route-CNSNj9Zm.js";import{t as u}from"./search-JOKus9rb.js";import{l as d}from"./query-keys-gmjzC3RS.js";import{Q as f,et as p,lt as m}from"./entry-CgdSAzEV.js";import{n as h,r as g,t as _}from"./card-BKqlUOe4.js";import{n as te}from"./download-dRFo5_Wu.js";import{t as v}from"./badge-D4pQapKj.js";import{n as ne}from"./input-LVjgZq4f.js";import{t as re}from"./select-WUCXorXw.js";import{t as ie}from"./ModelPicker-BY1aKEkP.js";import{t as y}from"./switch-DEjTtOWf.js";import{t as b}from"./stack-_3O0F8Xn.js";import{n as x}from"./inline-DUWDSmDV.js";import{f as ae,t as S}from"./api-keys-VY68Mhd1.js";import{a as C,i as oe,n as se,o as w,r as T,t as ce}from"./cli-tools-fTgljTGA.js";import{t as le}from"./ToolIcon-K1ckcDKg.js";var ue=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`}]]),E=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`}]]),D=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`}]]),O=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}=p(),t=C(),n=w(),r=S(),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)(c,{label:`Loading tool details…`}):t.isError?(0,P.jsx)(s,{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)(b,{gap:`12px`,children:[(0,P.jsx)(f,{to:`/cli-tools`,className:`text-caption text-accent`,children:`← Back to CLI Tools`}),(0,P.jsx)(s,{title:`Tool not found`,message:`Unknown CLI tool id.`})]})}function I({tool:e,status:t,apiKeys:n}){let[r,s]=(0,k.useState)(``),[c,p]=(0,k.useState)(()=>typeof window>`u`?`http://localhost:12800`:window.location.origin),[S,C]=(0,k.useState)({}),[w,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=oe(e.id,r),V=T(),H=se(),U=ae(),W=ce(),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=S[n.alias]??n.defaultValue??n.id;!X(e)&&!X(r)&&t.add(n.alias)}return t},[e.defaultModels,e.mappingSupported,j,S,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(d(e,`Could not update the API key.`))})};(0,k.useEffect)(()=>{let t=Object.fromEntries(e.defaultModels.map(e=>[e.alias,e.defaultValue??e.id]));C(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)C(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]));C(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=>S[e.alias]??e.defaultValue??e.id),[S,e.defaultModels]),$=(0,k.useCallback)(()=>{if(e.mappingSupported)return{enabled:w,mappings:e.defaultModels.filter(e=>(j[e.alias]??``).length>0).map(e=>({slotKey:e.alias,sourceModel:S[e.alias]??e.defaultValue??e.id,targetModel:j[e.alias]??``,enabled:!0})).filter(e=>e.targetModel.length>0)}},[w,j,S,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(d(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,S[e.alias]??e.defaultValue??e.id])),n=e.defaultModels.find(e=>e.roleKind===`subagent`);return{endpoint:c,apiKey:``,modelIds:e.id===`claude`?[]:Q,modelSlots:t,...Q[0]?{activeModel:Q[0]}:{},...n?{subagentModel:S[n.alias]??n.defaultValue??n.id}:{},mappingOwnerId:r,...e.mappingSupported?{mapping:$()}:{}}},[Q,$,r,c,S,e]);return(0,P.jsxs)(b,{gap:`16px`,children:[(0,P.jsxs)(f,{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)(_,{children:(0,P.jsxs)(h,{className:`flex flex-col gap-5`,children:[(0,P.jsxs)(x,{gap:`14px`,align:`flex-start`,className:`flex-wrap`,children:[(0,P.jsx)(le,{toolId:e.id,name:e.name,color:e.color,size:46}),(0,P.jsxs)(x,{gap:`8px`,className:`flex-wrap`,children:[(0,P.jsx)(`h1`,{className:`text-title font-bold m-0`,children:e.name}),me?(0,P.jsx)(v,{tone:`ok`,children:`Installed`}):(0,P.jsx)(v,{tone:`disabled`,children:`Not installed`}),Z?(0,P.jsx)(v,{tone:`accent`,children:`Configured`}):null,e.configType===`guide`?(0,P.jsx)(v,{tone:`info`,children:`Guide`}):null]})]}),(0,P.jsx)(`p`,{className:`m-0 text-caption text-text-secondary`,children:e.description}),e.docsUrl?(0,P.jsx)(`a`,{href:e.docsUrl,target:`_blank`,rel:`noreferrer`,className:`inline-block text-caption text-accent`,children:`Official documentation →`}):null,t?.settingsPath?(0,P.jsxs)(`div`,{className:`font-mono tabular-nums px-6 py-4 rounded-inset bg-surface-2 border border-inner-border text-badge text-text-secondary break-all`,children:[`Config file: `,t.settingsPath]}):null,t?.message?(0,P.jsxs)(`div`,{className:`flex items-center gap-3 text-badge ${Z?`text-green`:`text-orange`}`,children:[Z?(0,P.jsx)(ee,{size:13}):(0,P.jsx)(E,{size:13}),t.message]}):null]})}),(0,P.jsxs)(_,{children:[(0,P.jsx)(g,{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)(O,{size:16})}),(0,P.jsx)(h,{children:(0,P.jsx)(b,{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)(re,{label:`Apikey Name`,id:`api-key-select`,value:r,onValueChange:s,options:[{value:``,label:`Select…`},...z.map(e=>({value:e.id,label:`${e.label} — ${e.keyMode===`personal`?`Personal`:`Share template`}`}))]}),(0,P.jsx)(`div`,{className:`flex items-center gap-3 flex-wrap`,children:G===void 0?(0,P.jsx)(`span`,{className:`mt-1 text-badge text-text-tertiary`,children:`Choose the Apikey Name whose routes this CLI session should use.`}):K?(0,P.jsxs)(P.Fragment,{children:[(0,P.jsx)(v,{tone:`ok`,children:`routing:cli_mapping`}),(0,P.jsx)(`span`,{className:`mt-1 text-badge text-text-tertiary`,children:`Remote routes work with this key.`})]}):(0,P.jsxs)(P.Fragment,{children:[(0,P.jsx)(v,{tone:`warn`,title:`This key cannot resolve persisted CLI mappings on /v1/*. Turn on Remote Routing below to enable it.`,children:`no CLI scope`}),(0,P.jsx)(`span`,{className:`mt-1 text-badge text-text-tertiary`,children:`Remote routes stay inert on this key — turning on Remote Routing below will activate it.`})]})}),G?.keyMode===`share`?(0,P.jsx)(`span`,{className:`mt-1 text-badge text-text-tertiary`,children:`Share template: its child keys inherit these routes automatically.`}):null]}),(0,P.jsx)(`div`,{className:`min-w-0`,children:(0,P.jsx)(ne,{label:`Endpoint`,value:c,onChange:e=>p(e.target.value)})})]})})})]}),(0,P.jsxs)(_,{children:[(0,P.jsx)(g,{title:`Model routing`,icon:(0,P.jsx)(l,{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)(y,{checked:w,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(d(e,`Could not update key scope.`))}})}let n=$();n&&V.mutate({toolId:e.id,keyId:r,input:{...n,enabled:t}},{onError:e=>o.error(d(e,`Could not save mappings.`))})},label:w?`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(d(e,`Could not reset remote routes.`))})},children:H.isPending?`Resetting…`:`Reset`})]}):void 0}),(0,P.jsxs)(h,{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)(b,{gap:`6px`,children:e.defaultModels.map(t=>{let n=S[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)(u,{size:12,className:`text-text-tertiary shrink-0`})]}),(0,P.jsx)(`span`,{className:`text-micro text-text-tertiary pl-1`,children:t.roleLabel??t.name})]}),e.mappingSupported?(0,P.jsxs)(`div`,{className:`flex flex-col gap-1 min-w-0`,children:[(0,P.jsxs)(`button`,{type:`button`,onClick:()=>{F(t.alias),L(`target`)},className:`font-mono tabular-nums flex w-full min-w-0 cursor-pointer items-center justify-between gap-4 rounded-inset border px-5 py-3.5 text-left text-badge ${i?`border-inner-border bg-surface-2 text-text-tertiary`:`border-accent bg-accent-soft text-text-primary`}`,children:[(0,P.jsx)(`span`,{className:`overflow-hidden truncate whitespace-nowrap`,children:r.length>0?r:`Not routed`}),(0,P.jsx)(u,{size:12,className:`text-text-tertiary shrink-0`})]}),(0,P.jsxs)(`span`,{className:`pl-1 text-micro ${r.length===0?`text-text-tertiary`:`text-accent`}`,children:[r.length===0?`not routed`:`rerouted`,r.length>0?(0,P.jsx)(`button`,{type:`button`,onClick:()=>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)(_,{children:[(0,P.jsx)(g,{title:`Guide`,subtitle:`Manual setup steps for tools that do not support file injection.`,icon:(0,P.jsx)(ue,{size:16})}),(0,P.jsx)(h,{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,c)}):null]},e.step))})})]}):null,(0,P.jsxs)(_,{children:[(0,P.jsx)(g,{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)(D,{size:16})}),(0,P.jsxs)(h,{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)(m,{size:14}),onClick:()=>{if(!r){o.error(`Select an API key first.`);return}W.mutate({toolId:e.id,keyId:r,input:ge()},{onSuccess:e=>{te(e.filename,e.content,e.mimeType),o.success(`Downloaded ${e.filename}.`)},onError:e=>o.error(d(e,`Could not download config.`))})},disabled:W.isPending||!q,children:W.isPending?`Downloading…`:`Download config`})}),q?null:(0,P.jsx)(`p`,{className:`text-badge text-text-tertiary text-right`,children:G?.keyMode===`share`?`A share template has no secret to write into a config. Select a personal key to download the config file.`:`Select an Apikey Name above to enable downloading the config file.`})]})]}),(0,P.jsx)(ie,{open:N!==null,onClose:()=>F(null),selected:N?[I===`target`?j[N]??S[N]??``:S[N]??``]:[],onToggle:()=>{},onSelectOne:e=>{N&&(I===`target`?M(t=>({...t,[N]:e})):C(t=>({...t,[N]:e}))),F(null)},title:N?`Select ${N} model`:`Select model`,multi:!1})]})}export{F as default};
|
|
Binary file
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{n as e,o as t,r as n,t as r}from"./jsx-runtime-CXUMngI_.js";import{t as i}from"./arrow-right-Dl5yKnp8.js";import{D as a,c as o,s,y as c}from"./numbers-YNcHvRY7.js";import{t as l}from"./SortableList-CB3Z7VTf.js";import{n as u,r as d,t as f}from"./state-BgTxHcJS.js";import{t as p}from"./layers-2-glBbjl.js";import{t as m}from"./dialog-BYj-8WuK.js";import{t as h}from"./pencil-768ysRAr.js";import{t as ee}from"./plus-DIdX_g-x.js";import{t as g}from"./route-CNSNj9Zm.js";import{t as _}from"./search-JOKus9rb.js";import{t as te}from"./trash-Cppn5c3R.js";import{l as v}from"./query-keys-gmjzC3RS.js";import{t as ne}from"./dialog-actions-B1axpEg7.js";import{R as y}from"./entry-CgdSAzEV.js";import{n as b,r as re,t as ie}from"./card-BKqlUOe4.js";import{t as ae}from"./use-clipboard-B0Sy03fE.js";import{n as x}from"./input-LVjgZq4f.js";import{t as S}from"./select-WUCXorXw.js";import{t as C}from"./ModelPicker-BY1aKEkP.js";import{t as w}from"./stack-_3O0F8Xn.js";import{n as T,t as oe}from"./inline-DUWDSmDV.js";import{t as E}from"./list-row-DupvLfDx.js";import{a as D,d as O,f as k,i as A,n as j,o as M,p as N,r as P,s as F,t as I,u as L}from"./routing-C7tBT5o-.js";import{t as R}from"./use-timeout-BwcBBRrT.js";var z=e(`copy-plus`,[[`line`,{x1:`15`,x2:`15`,y1:`12`,y2:`18`,key:`1p7wdc`}],[`line`,{x1:`12`,x2:`18`,y1:`15`,y2:`15`,key:`1nscbv`}],[`rect`,{width:`14`,height:`14`,x:`8`,y:`8`,rx:`2`,ry:`2`,key:`17jyea`}],[`path`,{d:`M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2`,key:`zix9uf`}]]),B=t(n(),1),V=[{value:`fallback`,label:`Fallback (try in order)`},{value:`round_robin`,label:`Round Robin (rotate)`}],H=r(),U=()=>`m${Date.now().toString(36)}${Math.random().toString(36).slice(2,8)}`;function W(){let e=M(),t=j(),n=k(),r=A(),c=L(),[p,S]=(0,B.useState)(!1),[D,O]=(0,B.useState)(null),[N,P]=(0,B.useState)(``),[F,I]=(0,B.useState)(``),[z,V]=(0,B.useState)(!1),[U,W]=(0,B.useState)(null),[G,K]=(0,B.useState)(null),{copy:q}=ae(),J=R(),Y=(0,B.useMemo)(()=>e.data??[],[e.data]),X=()=>{O(null),P(``),I(``),S(!0)},Z=e=>{O(e),P(e.alias),I(e.targetModel),S(!0)},Q=e=>{e.preventDefault(),N.trim()&&F.trim()&&(D?n.mutate({id:D.id,request:{alias:N.trim(),targetModel:F.trim()}},{onSuccess:()=>{S(!1),O(null),s.success(`Alias updated`)},onError:e=>s.error(v(e,`Could not save the alias.`))}):t.mutate({alias:N.trim(),targetModel:F.trim()},{onSuccess:()=>{S(!1),P(``),I(``),s.success(`Alias created`)},onError:e=>s.error(v(e,`Could not create the alias.`))}))},se=(e,t)=>{q(e).then(e=>{if(!e){s.error(`Copy failed`);return}K(t),J(()=>K(null),1500)})};return(0,H.jsxs)(ie,{children:[(0,H.jsx)(re,{title:`Model Aliases`,subtitle:`Map readable names to real models (e.g. claude-mythos-5 → claude-opus-5)`,icon:(0,H.jsx)(g,{size:16}),action:(0,H.jsx)(oe,{children:(0,H.jsx)(o,{variant:`primary`,size:`sm`,icon:(0,H.jsx)(ee,{size:13}),onClick:X,children:`New Alias`})})}),(0,H.jsx)(b,{children:e.isPending?(0,H.jsx)(d,{label:`Loading aliases...`}):e.isError?(0,H.jsx)(u,{message:`Failed to load aliases`,onRetry:()=>e.refetch()}):Y.length===0?(0,H.jsx)(f,{title:`No aliases defined`,message:`Create an alias to route short or friendly names to real provider model IDs.`}):(0,H.jsx)(l,{items:Y,label:`Model aliases`,gap:`8px`,disabled:c.isPending,onReorder:e=>c.mutate(e,{onError:e=>s.error(v(e,`Could not save the new order.`))}),renderItem:e=>(0,H.jsx)(E,{className:`items-start flex-col sm:flex-row`,actions:(0,H.jsxs)(T,{gap:`4px`,className:`w-full justify-start sm:w-auto sm:justify-end`,children:[(0,H.jsx)(o,{variant:`ghost`,size:`sm`,icon:(0,H.jsx)(a,{size:13}),onClick:()=>se(e.alias,e.id),title:G===e.id?`Copied!`:`Copy alias`}),(0,H.jsx)(o,{variant:`ghost`,size:`sm`,icon:(0,H.jsx)(h,{size:13}),onClick:()=>Z(e),title:`Edit target model`}),(0,H.jsx)(o,{variant:`ghost`,size:`sm`,icon:(0,H.jsx)(te,{size:13}),onClick:()=>W(e),title:`Delete alias`,className:`text-red`})]}),children:(0,H.jsxs)(`div`,{className:`flex items-center gap-4 flex-wrap min-w-0`,children:[(0,H.jsx)(y,{value:e.alias,tone:`accent`}),(0,H.jsx)(i,{size:13,className:`text-text-tertiary shrink-0`}),(0,H.jsx)(y,{value:e.targetModel,tone:`plain`})]})})})}),(0,H.jsx)(m,{open:p,onClose:()=>S(!1),title:D?`Edit Model Alias`:`New Model Alias`,footer:(0,H.jsxs)(H.Fragment,{children:[(0,H.jsx)(o,{variant:`secondary`,type:`button`,onClick:()=>S(!1),children:`Cancel`}),(0,H.jsx)(o,{variant:`primary`,type:`submit`,form:`alias-form`,disabled:t.isPending||n.isPending||!N.trim()||!F.trim(),children:D?`Save Changes`:`Create Alias`})]}),children:(0,H.jsxs)(`form`,{id:`alias-form`,onSubmit:Q,className:`flex flex-col gap-6`,children:[(0,H.jsx)(x,{label:`Alias name`,id:`alias-name-input`,value:N,onChange:e=>P(e.target.value),placeholder:`e.g. fast, sonnet, smart`,required:!0}),(0,H.jsxs)(T,{gap:`8px`,align:`flex-end`,children:[(0,H.jsx)(`div`,{className:`flex-1`,children:(0,H.jsx)(x,{label:`Target model`,id:`target-model-input`,value:F,onChange:e=>I(e.target.value),placeholder:`e.g. codex/gpt-5.5, claude/claude-sonnet-5`,required:!0})}),(0,H.jsx)(o,{variant:`secondary`,size:`sm`,type:`button`,onClick:()=>V(!0),icon:(0,H.jsx)(_,{size:13}),children:`Browse`})]}),(0,H.jsx)(C,{open:z,onClose:()=>V(!1),selected:F?[F]:[],onToggle:()=>{},onSelectOne:e=>I(e),title:`Select target model`,multi:!1})]})}),(0,H.jsx)(m,{open:!!U,onClose:()=>W(null),title:`Delete Model Alias`,children:(0,H.jsxs)(w,{gap:`12px`,children:[(0,H.jsxs)(`p`,{className:`text-body text-text-secondary`,children:[`Are you sure you want to delete alias `,(0,H.jsx)(`strong`,{children:U?.alias}),`? Clients requesting this name will need their full model path.`]}),(0,H.jsxs)(ne,{children:[(0,H.jsx)(o,{variant:`secondary`,onClick:()=>W(null),children:`Cancel`}),(0,H.jsx)(o,{variant:`danger`,onClick:()=>{U&&r.mutate(U.id,{onSuccess:()=>W(null)})},disabled:r.isPending,children:r.isPending?`Deleting...`:`Delete Alias`})]})]})})]})}function G(){let e=F(),t=P(),n=I(),r=N(),i=D(),g=O(),[k,A]=(0,B.useState)(!1),[j,M]=(0,B.useState)(null),[L,W]=(0,B.useState)(``),[G,K]=(0,B.useState)([]),[q,J]=(0,B.useState)(`fallback`),[Y,X]=(0,B.useState)(!1),[Z,Q]=(0,B.useState)(null),[se,ce]=(0,B.useState)(null),{copy:le}=ae(),ue=R(),$=(0,B.useMemo)(()=>G.map(e=>e.value),[G]),de=e=>{$.includes(e)?K(G.filter(t=>t.value!==e)):K([...G,{id:U(),value:e.trim()}])},fe=e.data??[],pe=()=>{M(null),W(``),K([]),J(`fallback`),A(!0)},me=e=>{M(e),W(e.name),K(e.members.map(e=>({id:U(),value:e}))),A(!0)},he=e=>{n.mutate(e.id,{onSuccess:e=>{let t=e.skippedMembers;t.length>0?s.success(`Combo cloned`,`${e.combo.name} — skipped unresolvable member${t.length>1?`s`:``}: ${t.join(`, `)}`):s.success(`Combo cloned`,e.combo.name)},onError:e=>s.error(v(e,`Could not clone the combo.`))})},ge=e=>{e.preventDefault(),L.trim()&&$.length!==0&&(j?r.mutate({id:j.id,request:{name:L.trim(),members:$,strategy:q}},{onSuccess:()=>{A(!1),M(null),s.success(`Combo saved`)},onError:e=>s.error(v(e,`Could not save the combo.`))}):t.mutate({name:L.trim(),members:$,strategy:q},{onSuccess:()=>{A(!1),W(``),K([]),s.success(`Combo created`)},onError:e=>s.error(v(e,`Could not create the combo.`))}))},_e=(e,t)=>{r.mutate({id:e.id,request:{strategy:t}},{onSuccess:()=>s.success(`Combo strategy updated`),onError:e=>s.error(v(e,`Could not update the combo strategy.`))})},ve=(e,t)=>{le(e).then(e=>{if(!e){s.error(`Copy failed`);return}ce(t),ue(()=>ce(null),1500)})};return(0,H.jsxs)(ie,{children:[(0,H.jsx)(re,{title:`Combos`,subtitle:`Combine multiple models with fallback or round-robin rotation`,icon:(0,H.jsx)(p,{size:16}),action:(0,H.jsx)(oe,{children:(0,H.jsx)(o,{variant:`primary`,size:`sm`,icon:(0,H.jsx)(ee,{size:13}),onClick:pe,children:`New Combo`})})}),(0,H.jsx)(b,{children:e.isPending?(0,H.jsx)(d,{label:`Loading combos...`}):e.isError?(0,H.jsx)(u,{message:`Failed to load combos`,onRetry:()=>e.refetch()}):fe.length===0?(0,H.jsx)(f,{title:`No combos defined`,message:`Create a model combo to distribute requests across multiple models.`}):(0,H.jsx)(l,{items:fe,label:`Model combos`,disabled:g.isPending,onReorder:e=>g.mutate(e,{onError:e=>s.error(v(e,`Could not save the new order.`))}),renderItem:e=>(0,H.jsxs)(E,{className:`items-start flex-col sm:flex-row`,actions:(0,H.jsxs)(T,{gap:`8px`,className:`w-full justify-between sm:w-auto sm:justify-start`,children:[(0,H.jsx)(S,{value:e.strategy,onValueChange:t=>_e(e,t),options:V}),(0,H.jsxs)(T,{gap:`4px`,className:`flex-wrap`,children:[(0,H.jsx)(o,{variant:`ghost`,size:`sm`,icon:(0,H.jsx)(a,{size:13}),onClick:()=>ve(e.name,e.id),title:se===e.id?`Copied!`:`Copy combo name`}),(0,H.jsx)(o,{variant:`ghost`,size:`sm`,icon:(0,H.jsx)(z,{size:13}),onClick:()=>he(e),disabled:n.isPending,title:`Clone combo (same members and strategy)`}),(0,H.jsx)(o,{variant:`ghost`,size:`sm`,icon:(0,H.jsx)(h,{size:13}),onClick:()=>me(e),title:`Edit combo`}),(0,H.jsx)(o,{variant:`ghost`,size:`sm`,icon:(0,H.jsx)(te,{size:13}),onClick:()=>Q(e),title:`Delete combo`,className:`text-red`})]})]}),children:[(0,H.jsx)(`div`,{className:`flex items-center justify-between gap-6 flex-wrap`,children:(0,H.jsx)(`strong`,{className:`text-body`,children:e.name})}),(0,H.jsx)(T,{gap:`6px`,className:`w-full flex-wrap`,children:e.members.map(e=>(0,H.jsx)(y,{value:e},e))})]})})}),(0,H.jsx)(m,{open:k,onClose:()=>A(!1),title:j?`Edit Model Combo`:`New Model Combo`,footer:(0,H.jsxs)(H.Fragment,{children:[(0,H.jsx)(o,{variant:`secondary`,type:`button`,onClick:()=>A(!1),children:`Cancel`}),(0,H.jsx)(o,{variant:`primary`,type:`submit`,form:`combo-form`,disabled:t.isPending||r.isPending||!L.trim()||$.length===0,children:j?`Save Changes`:`Create Combo`})]}),children:(0,H.jsxs)(`form`,{id:`combo-form`,onSubmit:ge,className:`flex flex-col gap-6`,children:[(0,H.jsx)(x,{label:`Combo name`,id:`combo-name-input`,value:L,onChange:e=>W(e.target.value),placeholder:`e.g. smart-combo, fast-pool`,required:!0}),(0,H.jsx)(S,{label:`Strategy`,id:`combo-strategy-select`,value:q,onValueChange:e=>J(e),options:V}),(0,H.jsxs)(w,{gap:`6px`,children:[(0,H.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,H.jsx)(`label`,{className:`text-caption font-semibold text-text-secondary`,children:`Member models — use arrows or drag to reorder`}),(0,H.jsx)(o,{variant:`secondary`,size:`sm`,type:`button`,icon:(0,H.jsx)(_,{size:12}),onClick:()=>X(!0),children:`Browse models`})]}),(0,H.jsx)(`div`,{role:`list`,"aria-label":`Combo members`,"data-testid":`combo-members-list`,className:`flex min-h-22 flex-col gap-2 rounded-control border border-dashed border-inner-border ${G.length===0?`p-0`:`p-3`}`,children:G.length===0?(0,H.jsx)(`button`,{type:`button`,onClick:()=>X(!0),className:`flex-1 cursor-pointer font-inherit text-caption text-text-tertiary`,children:`Pick models to add`}):(0,H.jsx)(l,{items:G,label:`Combo members`,gap:`4px`,onReorder:e=>{let t=new Map(G.map(e=>[e.id,e]));K(e.map(e=>t.get(e)).filter(Boolean))},renderItem:e=>(0,H.jsx)(E,{density:`compact`,actions:(0,H.jsx)(`button`,{type:`button`,onClick:()=>de(e.value),"aria-label":`Remove ${e.value}`,className:`flex shrink-0 cursor-pointer text-text-tertiary`,children:(0,H.jsx)(c,{size:12})}),children:(0,H.jsx)(y,{value:e.value,tone:`accent`})})})})]}),(0,H.jsx)(C,{open:Y,onClose:()=>X(!1),selected:$,onToggle:de,title:`Select combo members`,multi:!0})]})}),(0,H.jsx)(m,{open:!!Z,onClose:()=>Q(null),title:`Delete Model Combo`,children:(0,H.jsxs)(w,{gap:`12px`,children:[(0,H.jsxs)(`p`,{className:`text-body text-text-secondary`,children:[`Are you sure you want to delete combo `,(0,H.jsx)(`strong`,{children:Z?.name}),`? Any aliases pointing to it will fail to resolve.`]}),(0,H.jsxs)(ne,{children:[(0,H.jsx)(o,{variant:`secondary`,onClick:()=>Q(null),children:`Cancel`}),(0,H.jsx)(o,{variant:`danger`,onClick:()=>{Z&&i.mutate(Z.id,{onSuccess:()=>Q(null)})},disabled:i.isPending,children:i.isPending?`Deleting...`:`Delete Combo`})]})]})})]})}function K(){return(0,H.jsxs)(w,{gap:`16px`,children:[(0,H.jsx)(G,{}),(0,H.jsx)(W,{})]})}export{K 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{c as r,k as i,y as a}from"./numbers-YNcHvRY7.js";import{t as o}from"./dialog-BYj-8WuK.js";import{t as s}from"./route-CNSNj9Zm.js";import{t as c}from"./search-JOKus9rb.js";import{C as l,v as u}from"./assert-Chi7pCnt.js";import{o as d,t as f}from"./query-keys-gmjzC3RS.js";import{s as p}from"./tokens-DDxMbSRL.js";import{t as m}from"./badge-D4pQapKj.js";import{t as h}from"./ProviderIcon-CmfzxpVM.js";var g=e(t(),1),_=n();function v(e){let t=l({queryKey:f.providers.flatAll,queryFn:e=>d(`/providers/models/flat`,{signal:u(e)}),enabled:e,staleTime:0});return{items:t.data??[],isLoading:t.isPending,isError:t.isError,refetch:()=>void t.refetch()}}function y(e){return e==null?`?`:e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(0)}k`:String(e)}function b(e){let t=e.toLowerCase();return t===`responses`?`Responses`:t===`messages`?`Messages`:`Chat`}function x(e){let t=e.toLowerCase();return t===`responses`?`var(--purple)`:t===`messages`?`var(--orange)`:`var(--teal)`}function S(e){return e===`systemone`?`System One decision model — not a chat model. Call it at POST /v1/systemone with {state, questions}; it answers {answers}. It cannot run in a chat playground.`:e===`websearch`?`Web-search model — not a chat model. Call it at POST /v1/search with {query, max_results}; it answers a normalized list of hits. It cannot run in a chat playground.`:`This model is served by a native '${e}' route, not the chat wire.`}function C({open:e,onClose:t,selected:n,onToggle:l,onSelectOne:u,size:d=`lg`,title:f=`Select model`,multi:C=!0,filter:w}){let[T,E]=(0,g.useState)(``),{items:D,isLoading:O,isError:k,refetch:A}=v(e),j=(0,g.useMemo)(()=>{let e=T.trim().toLowerCase();return D.filter(t=>w&&!w(t)?!1:!e||t.qualified.toLowerCase().includes(e)||t.modelId.toLowerCase().includes(e)||t.providerId.toLowerCase().includes(e))},[D,T,w]),M=(0,g.useMemo)(()=>{let e=new Map;for(let t of j){let n=t.kind===`model`?t.providerId:t.kind===`alias`?`Aliases`:`Combos`,r=e.get(n)??[];r.push(t),e.set(n,r)}let t=(e,t)=>{let n=t[0]?.kind;return n===`alias`?0:n===`combo`?1:2};return Array.from(e.entries()).sort(([e,n],[r,i])=>{let a=t(e,n),o=t(r,i);return a===o?e.localeCompare(r):a-o})},[j]),N=e=>n.includes(e),P=e=>{u&&!C?(u(e),t()):l(e)};return(0,_.jsx)(o,{open:e,onClose:t,title:f,size:d,footer:(0,_.jsx)(r,{variant:`secondary`,onClick:t,children:`Done`}),children:(0,_.jsxs)(`div`,{className:`flex max-w-full flex-col gap-6`,children:[(0,_.jsxs)(`div`,{className:`relative`,children:[(0,_.jsx)(c,{size:14,className:`absolute left-5 top-1/2 -translate-y-1/2 text-text-tertiary`}),(0,_.jsx)(`input`,{value:T,onChange:e=>E(e.target.value),placeholder:`Search models or providers (e.g. gpt-5.4, codex)`,className:`w-full max-w-full rounded-inset border border-inner-border bg-surface-2 py-4 pr-5 pl-16 text-caption text-text-primary`})]}),C&&n.length>0&&(0,_.jsx)(`div`,{className:`flex flex-wrap gap-3 rounded-inset border border-inner-border bg-surface-2 p-4`,children:n.map(e=>(0,_.jsxs)(`span`,{className:`font-mono tabular-nums inline-flex items-center gap-2 text-badge bg-accent-soft text-accent py-1 px-3 rounded-chip border border-accent max-w-full overflow-hidden break-all`,children:[(0,_.jsx)(`span`,{className:`overflow-hidden truncate whitespace-nowrap`,children:e}),(0,_.jsx)(`button`,{type:`button`,onClick:()=>l(e),className:`bg-transparent border-none cursor-pointer p-0 flex shrink-0`,children:(0,_.jsx)(a,{size:12})})]},e))}),(0,_.jsx)(`div`,{className:`flex min-h-160 max-h-210 flex-1 flex-col gap-8 overflow-x-hidden overflow-y-auto rounded-control border border-inner-border bg-surface-1 p-6`,children:O?(0,_.jsx)(`div`,{className:`p-16 text-center text-caption text-text-tertiary`,children:`Loading catalog…`}):k?(0,_.jsxs)(`div`,{className:`p-16 text-center text-caption`,children:[(0,_.jsx)(`p`,{className:`text-text-tertiary`,children:`Failed to load catalog.`}),(0,_.jsx)(`button`,{type:`button`,onClick:A,className:`mt-4`,children:`Retry`})]}):M.length===0?(0,_.jsx)(`div`,{className:`p-16 text-center text-caption text-text-tertiary`,children:`No models found.`}):M.map(([e,t])=>{let n=t[0]?.kind===`model`;return(0,_.jsxs)(`div`,{className:`flex flex-col gap-4 min-w-0`,children:[(0,_.jsxs)(`div`,{className:`flex items-center gap-3 text-badge font-bold tracking-[0.05em] text-text-secondary uppercase`,children:[n?(0,_.jsx)(h,{name:e,icon:e,size:16}):(0,_.jsx)(s,{size:14}),e,(0,_.jsx)(m,{tone:`default`,children:t.length})]}),(0,_.jsx)(`div`,{className:`grid min-w-0 grid-cols-2 gap-4 max-small:gap-2`,children:t.map(e=>{let t=N(e.qualified);return(0,_.jsxs)(`button`,{type:`button`,onClick:()=>P(e.qualified),className:`relative flex w-full min-w-0 cursor-pointer flex-col gap-2 overflow-hidden rounded-control px-6 py-5 text-left max-small:px-3 max-small:py-4 ${t?`border border-accent bg-accent-soft`:`border border-inner-border bg-surface-2`}`,children:[t&&(0,_.jsx)(i,{size:12,className:`absolute top-4 right-4 text-accent`}),(0,_.jsxs)(`span`,{title:e.qualified,className:`font-mono tabular-nums w-full whitespace-normal break-all text-badge font-semibold ${t?`pr-8 text-accent`:`text-text-primary`}`,children:[e.modelId,e.kind===`model`&&e.entry.serviceKind!==`llm`&&(0,_.jsx)(`span`,{title:S(e.entry.serviceKind),className:`ml-3 inline-block cursor-help rounded-tiny border border-orange px-2.5 align-middle font-bold tracking-[0.04em] text-micro text-orange uppercase`,children:e.entry.serviceKind})]}),(0,_.jsx)(`span`,{title:e.qualified,className:`font-mono tabular-nums w-full truncate text-micro text-text-tertiary`,children:e.kind===`alias`||e.kind===`combo`?(0,_.jsx)(`span`,{title:e.providerLabel,children:e.providerLabel}):(0,_.jsxs)(_.Fragment,{children:[e.entry.contextLimit?`${y(e.entry.contextLimit)} ctx`:(0,_.jsx)(`span`,{title:p,className:`cursor-help`,children:`n/a ctx`}),` · `,e.entry.outputLimit?`${y(e.entry.outputLimit)} out`:(0,_.jsx)(`span`,{title:p,className:`cursor-help`,children:`n/a out`}),` · `,(0,_.jsx)(`span`,{style:{color:x(e.entry.wireFamily),fontWeight:700},children:b(e.entry.wireFamily)})]})})]},e.qualified)})})]},e)})})]})})}export{v as n,C as t};
|
|
Binary file
|