cartethyia 2.7.3 → 2.7.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-2TJD37Hb.js → BannedPage-ChG5svFb.js} +1 -1
- package/dist/dashboard/assets/BannedPage-ChG5svFb.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-xefJNRmJ.js → CliToolDetailPage-BIcWQun7.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-Rj9iHQ3F.js → CliToolsPage-Bf2VelDe.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js.gz +0 -0
- package/dist/dashboard/assets/{CombosPage-JEbYJFxw.js → CombosPage-DCgm5g6g.js} +1 -1
- package/dist/dashboard/assets/CombosPage-DCgm5g6g.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js +1 -0
- package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-C4w7vea-.js → LoginPage-U6AKaJcF.js} +1 -1
- package/dist/dashboard/assets/LoginPage-U6AKaJcF.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-BtPcAfot.js → ModelPicker-BMS2t8BA.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-BMS2t8BA.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js +1 -0
- package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js +5 -0
- package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-lsrx7NYg.js +9 -0
- package/dist/dashboard/assets/ProxyPage-lsrx7NYg.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-L0skzML8.js +1 -0
- package/dist/dashboard/assets/QuotaPage-L0skzML8.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js +1 -0
- package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-BQS0Rx_u.js → SetupPage-f0DE1h0-.js} +1 -1
- package/dist/dashboard/assets/SetupPage-f0DE1h0-.js.gz +0 -0
- package/dist/dashboard/assets/{SortableList-BAL5npBC.js → SortableList-ClzpwyET.js} +1 -1
- package/dist/dashboard/assets/SortableList-ClzpwyET.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-TP7voAQ0.js → StudioPage-Cq5tW7hW.js} +3 -3
- package/dist/dashboard/assets/StudioPage-Cq5tW7hW.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js +56 -0
- package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-C38iXoO4.js +1 -0
- package/dist/dashboard/assets/api-keys-C38iXoO4.js.gz +0 -0
- package/dist/dashboard/assets/app-a159k0ub.css +2 -0
- package/dist/dashboard/assets/app-a159k0ub.css.gz +0 -0
- package/dist/dashboard/assets/assert-DbRA8EG2.js +15 -0
- package/dist/dashboard/assets/assert-DbRA8EG2.js.gz +0 -0
- package/dist/dashboard/assets/{badge-B_DMpMJN.js → badge-D9CS6dZk.js} +1 -1
- package/dist/dashboard/assets/badge-D9CS6dZk.js.gz +0 -0
- package/dist/dashboard/assets/{banner-DMrMPmth.js → banner-DympBNq4.js} +1 -1
- package/dist/dashboard/assets/banner-DympBNq4.js.gz +0 -0
- package/dist/dashboard/assets/{card-Be5Uzamx.js → card-ibRWU_uT.js} +1 -1
- package/dist/dashboard/assets/card-ibRWU_uT.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-63qBGaRw.js +1 -0
- package/dist/dashboard/assets/cli-tools-63qBGaRw.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-DOfrVmsi.js → clipboard-button-DJm-9pz7.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-DJm-9pz7.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-C9omdjQt.js → dialog-8fiE6tec.js} +1 -1
- package/dist/dashboard/assets/dialog-8fiE6tec.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-Bm8WKaGY.js → drawer-CqGYeOrd.js} +1 -1
- package/dist/dashboard/assets/drawer-CqGYeOrd.js.gz +0 -0
- package/dist/dashboard/assets/{entry-q5HwZ8nN.js → entry-BSAsYsmV.js} +1 -1
- package/dist/dashboard/assets/entry-BSAsYsmV.js.gz +0 -0
- package/dist/dashboard/assets/entry-CiIQULZD.js +4 -0
- package/dist/dashboard/assets/entry-CiIQULZD.js.gz +0 -0
- package/dist/dashboard/assets/entry-Daz0lJ-W.js +1 -0
- package/dist/dashboard/assets/entry-Daz0lJ-W.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-BTScrQpe.js → formatters-D-MTJxMr.js} +1 -1
- package/dist/dashboard/assets/formatters-D-MTJxMr.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-Orjuzemr.js → github-badge-CPK47xQx.js} +1 -1
- package/dist/dashboard/assets/github-badge-CPK47xQx.js.gz +0 -0
- package/dist/dashboard/assets/{index-cPrX5vn6.js → index-DBNPOfhV.js} +2 -2
- package/dist/dashboard/assets/index-DBNPOfhV.js.gz +0 -0
- package/dist/dashboard/assets/{inline-Dvux8w-0.js → inline-BT0zntEr.js} +1 -1
- package/dist/dashboard/assets/inline-BT0zntEr.js.gz +0 -0
- package/dist/dashboard/assets/{input-CU1Rgf3p.js → input-Da2ZViWg.js} +1 -1
- package/dist/dashboard/assets/input-Da2ZViWg.js.gz +0 -0
- package/dist/dashboard/assets/{layout-C_7A6H55.js → layout-BXNeLw6M.js} +1 -1
- package/dist/dashboard/assets/layout-BXNeLw6M.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-CzFvogbj.js → list-row-BycOrrIV.js} +1 -1
- package/dist/dashboard/assets/list-row-BycOrrIV.js.gz +0 -0
- package/dist/dashboard/assets/live-DCQNZpF7.js +1 -0
- package/dist/dashboard/assets/live-DCQNZpF7.js.gz +0 -0
- package/dist/dashboard/assets/minimize-2-BTmMTK4o.js +1 -0
- package/dist/dashboard/assets/minimize-2-BTmMTK4o.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-JJGQm8TD.js → mutation-Rr0TIvcj.js} +1 -1
- package/dist/dashboard/assets/mutation-Rr0TIvcj.js.gz +0 -0
- package/dist/dashboard/assets/network-9Yj2ZyLo.js +1 -0
- package/dist/dashboard/assets/network-9Yj2ZyLo.js.gz +0 -0
- package/dist/dashboard/assets/{numbers-BNDZnsUE.js → numbers-CEagj6ZZ.js} +2 -2
- package/dist/dashboard/assets/numbers-CEagj6ZZ.js.gz +0 -0
- package/dist/dashboard/assets/{query-keys-CLaNz8Xz.js → query-keys-BloKtaXJ.js} +1 -1
- package/dist/dashboard/assets/query-keys-BloKtaXJ.js.gz +0 -0
- package/dist/dashboard/assets/quota-Caw7wqci.js +1 -0
- package/dist/dashboard/assets/quota-Caw7wqci.js.gz +0 -0
- package/dist/dashboard/assets/routing-CO9zCUus.js +1 -0
- package/dist/dashboard/assets/routing-CO9zCUus.js.gz +0 -0
- package/dist/dashboard/assets/{select-B3iTetHN.js → select-BIYIBkMU.js} +2 -2
- package/dist/dashboard/assets/select-BIYIBkMU.js.gz +0 -0
- package/dist/dashboard/assets/settings-jS3-PNSA.js +1 -0
- package/dist/dashboard/assets/settings-jS3-PNSA.js.gz +0 -0
- package/dist/dashboard/assets/{stack-KrOrm7JZ.js → stack-DfgnFDWY.js} +1 -1
- package/dist/dashboard/assets/stack-DfgnFDWY.js.gz +0 -0
- package/dist/dashboard/assets/{state-DLS7IDMy.js → state-CkfdYWel.js} +1 -1
- package/dist/dashboard/assets/state-CkfdYWel.js.gz +0 -0
- package/dist/dashboard/assets/{switch-42ItvbAs.js → switch-SYm237HG.js} +1 -1
- package/dist/dashboard/assets/switch-SYm237HG.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-Dmnfh1Ue.js → useMutation-BQqCRNIi.js} +1 -1
- package/dist/dashboard/assets/useMutation-BQqCRNIi.js.gz +0 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +55 -54
- package/migrations/full-postgres/0027_qoder_identity_genuine_seed.sql +9 -10
- package/migrations/full-postgres/0032_add_provider_pool_accounts.sql +53 -0
- package/migrations/full-postgres/0033_add_api_key_ban.sql +9 -0
- package/migrations/full-postgres/0034_add_api_key_ban_until.sql +7 -0
- package/migrations/full-postgres/0035_align_pool_account_status_enum.sql +21 -0
- package/migrations/lite-sqlite3/0027_qoder_identity_genuine_seed.sql +9 -10
- package/migrations/lite-sqlite3/0032_add_provider_pool_accounts.sql +44 -0
- package/migrations/lite-sqlite3/0033_add_api_key_ban.sql +8 -0
- package/migrations/lite-sqlite3/0034_add_api_key_ban_until.sql +5 -0
- package/migrations/lite-sqlite3/0035_align_pool_account_status_enum.sql +6 -0
- package/package.json +2 -1
- package/src/boot/app.ts +9 -8
- package/src/config/network.ts +7 -3
- package/src/console/api-keys/contracts.ts +52 -0
- package/src/console/api-keys/routes.ts +93 -4
- package/src/console/audit/routes.ts +3 -3
- package/src/console/auth/account-reset.ts +4 -4
- package/src/console/auth/session.ts +3 -3
- package/src/console/backup/contracts.ts +2 -0
- package/src/console/backup/export-endpoint.ts +6 -6
- package/src/console/backup/routes.ts +3 -3
- package/src/console/cli-tools/routes.ts +3 -3
- package/src/console/console-router-guards.ts +2 -2
- package/src/console/console-router.ts +4 -4
- package/src/console/data-management/routes.ts +3 -3
- package/src/console/domain-registration.ts +2 -2
- package/src/console/observability/contracts.ts +4 -0
- package/src/console/observability/live.ts +3 -3
- package/src/console/observability/logs.ts +3 -3
- package/src/console/observability/performance.ts +3 -3
- package/src/console/observability/routes.ts +3 -3
- package/src/console/observability/store.ts +16 -16
- package/src/console/providers/catalog/contracts.ts +43 -22
- package/src/console/providers/catalog/provider-operations.ts +17 -2
- package/src/console/providers/catalog/routes.ts +3 -3
- package/src/console/providers/catalog/store-helpers.ts +349 -326
- package/src/console/providers/catalog/store.ts +1523 -1292
- package/src/console/providers/detail/routes.ts +3 -3
- package/src/console/providers/oauth/routes.ts +3 -3
- package/src/console/quota/account-quota-tenant.ts +26 -1
- package/src/console/quota/account-test-egress.ts +4 -4
- package/src/console/routing/model/routes.ts +3 -3
- package/src/console/routing/pools/contracts.ts +11 -8
- package/src/console/routing/pools/operations.ts +14 -7
- package/src/console/routing/pools/probe-verdict.ts +8 -3
- package/src/console/routing/pools/relay-deploy.ts +9 -9
- package/src/console/routing/pools/routes.ts +3 -3
- package/src/console/routing/pools/store.ts +21 -1
- package/src/console/settings/routes.ts +3 -3
- package/src/console/share/share-router.ts +12 -12
- package/src/console/share/share-stats.ts +105 -35
- package/src/console/shared/read-through-cache.ts +38 -2
- package/src/console/updates/routes.ts +7 -4
- package/src/network/outbound-fetch.ts +75 -4
- package/src/network/pool/resolver.ts +39 -39
- package/src/network/pool-cooldown-store.ts +71 -16
- package/src/network/pool-health-machine.ts +21 -1
- package/src/network/rate-limit-verdict.ts +68 -0
- package/src/network/ssrf.ts +4 -4
- package/src/observability/event-query.ts +320 -16
- package/src/observability/telemetry-status.ts +1 -1
- package/src/persistence/db-sqlite.ts +7 -2
- package/src/persistence/readiness.ts +18 -6
- package/src/persistence/schema.ts +66 -7
- package/src/persistence/stores/api-key-store.ts +99 -3
- package/src/persistence/stores/telemetry-store.ts +1 -1
- package/src/protocol/codecs.ts +6 -6
- package/src/protocol/gateway-error.ts +6 -3
- package/src/protocol/image-generation.ts +2 -2
- package/src/protocol/response/chat.ts +2 -2
- package/src/protocol/response/codex.ts +1 -1
- package/src/protocol/response/gemini.ts +1 -1
- package/src/protocol/response/messages.ts +5 -5
- package/src/protocol/response/responses.ts +2 -2
- package/src/protocol/sse-decoder.ts +14 -3
- package/src/protocol/translation/capabilities.ts +2 -2
- package/src/protocol/upstream/send-claude-messages.ts +3 -3
- package/src/providers/adapters/base-adapter.ts +1 -1
- package/src/providers/adapters/compatible-adapter.ts +2 -2
- package/src/providers/authentication/oauth-refresh-service.ts +2 -2
- package/src/providers/configured-provider.ts +1 -1
- package/src/providers/discovery/probe-phases.ts +2 -2
- package/src/providers/discovery/probing-service.ts +12 -12
- package/src/providers/integrations/agentrouter/agentrouter.ts +2 -3
- package/src/providers/integrations/alibabacp/alibabacp.ts +2 -2
- package/src/providers/integrations/anthropic.ts +4 -11
- package/src/providers/integrations/antigravity/antigravity.ts +13 -24
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +1 -1
- package/src/providers/integrations/buddy/buddy-discovery-shared.ts +37 -6
- package/src/providers/integrations/buddy/buddy-identity.ts +22 -1
- package/src/providers/integrations/buddy/buddy-media.ts +16 -16
- package/src/providers/integrations/buddy/codebuddy/provider.ts +1 -1
- package/src/providers/integrations/buddy/workbuddy/provider.ts +1 -1
- package/src/providers/integrations/claude/claude-betas.ts +1 -1
- package/src/providers/integrations/claude/claude-credentials.ts +1 -1
- package/src/providers/integrations/claude/claude-slow-lane-retry.ts +1 -1
- package/src/providers/integrations/claude/claude.ts +3 -10
- package/src/providers/integrations/cline/cline.ts +1 -1
- package/src/providers/integrations/cloudflareai/cloudflareai.ts +4 -7
- package/src/providers/integrations/codex/codex-errors.ts +1 -1
- package/src/providers/integrations/codex/codex.ts +14 -15
- package/src/providers/integrations/commandcode/commandcode.ts +4 -4
- package/src/providers/integrations/commandcode/quota.ts +5 -1
- package/src/providers/integrations/cursor/cursor-http2.ts +11 -11
- package/src/providers/integrations/cursor/cursor.ts +15 -15
- package/src/providers/integrations/factory/factory.ts +4 -4
- package/src/providers/integrations/gemini.ts +11 -11
- package/src/providers/integrations/github/github.ts +6 -6
- package/src/providers/integrations/grok/grok.ts +2 -6
- package/src/providers/integrations/inferhub.ts +4 -11
- package/src/providers/integrations/kimi/kimi.ts +4 -11
- package/src/providers/integrations/kiro/kiro.ts +10 -10
- package/src/providers/integrations/muse/muse.ts +6 -9
- package/src/providers/integrations/perplexity.ts +2 -2
- package/src/providers/integrations/qoder/qoder-catalog.ts +47 -9
- package/src/providers/integrations/qoder/qoder-checkin.ts +26 -8
- package/src/providers/integrations/qoder/qoder-native-identity-store.ts +9 -8
- package/src/providers/integrations/qoder/qoder-realm.ts +15 -0
- package/src/providers/integrations/qoder/qoder-umid.ts +12 -13
- package/src/providers/integrations/qoder/qoder-virtual.ts +2 -2
- package/src/providers/integrations/qoder/qoder.ts +188 -22
- package/src/providers/integrations/tokenharbor.ts +2 -2
- package/src/providers/integrations/trae/trae.ts +6 -9
- package/src/providers/integrations/zcode/oauth.ts +1 -1
- package/src/providers/operations/provider-catalog-service.ts +4 -4
- package/src/providers/operations/provider-credential-service.ts +16 -4
- package/src/providers/operations/upstream-deadline.ts +3 -3
- package/src/providers/provider-contracts.ts +47 -3
- package/src/providers/provider-credential-resolver.ts +1 -1
- package/src/providers/provider-registry.ts +4 -4
- package/src/providers/quota/quota-contracts.ts +24 -10
- package/src/providers/quota/refresh.ts +2 -2
- package/src/providers/search/search-provider.ts +5 -5
- package/src/security/abuse.ts +4 -4
- package/src/security/admission/reasons.ts +17 -17
- package/src/security/api-key-auth.ts +4 -4
- package/src/shared/coerce.ts +19 -0
- package/src/transport/dispatch/attempt-finalize.ts +4 -3
- package/src/transport/dispatch/attempt-loop.ts +10 -7
- package/src/transport/dispatch/empty-stream.ts +6 -6
- package/src/transport/dispatch/generation.ts +9 -9
- package/src/transport/dispatch/leases.ts +8 -8
- package/src/transport/dispatch/proxy-request.ts +7 -7
- package/src/transport/dispatch/responses-compact.ts +11 -11
- package/src/transport/dispatch/retry-policy.ts +4 -67
- package/src/transport/dispatch/stream-bridge.ts +1 -1
- package/src/transport/dispatch/streaming/finalize.ts +9 -8
- package/src/transport/dispatch/systemone.ts +8 -8
- package/src/transport/dispatch/token-count.ts +9 -9
- package/src/transport/dispatch/websearch.ts +6 -6
- package/src/transport/drain-endpoint.ts +2 -2
- package/src/transport/failure-policy.ts +13 -6
- package/src/transport/guards/auth-guard.ts +6 -5
- package/src/transport/guards/body-policy.ts +4 -4
- package/src/transport/guards/error-lifecycle.ts +18 -3
- package/src/transport/guards/guard-errors.ts +7 -3
- package/src/transport/guards/headroom-guard.ts +1 -1
- package/src/transport/guards/pipeline.ts +6 -6
- package/src/transport/guards/readiness-guard.ts +16 -2
- package/src/transport/guards/request-context.ts +3 -3
- package/src/transport/request/preparer.ts +20 -20
- package/src/transport/request/state.ts +4 -7
- package/src/transport/routing/route-catalog.ts +97 -2
- package/src/transport/routing/route-model.ts +7 -7
- package/src/transport/routing/router.ts +2 -2
- package/src/transport/shutdown-notice.ts +19 -7
- package/src/transport/surface/completion.ts +1 -1
- package/src/transport/surface/messages/encode.ts +1 -1
- package/dist/dashboard/assets/BannedPage-2TJD37Hb.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-xefJNRmJ.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-Rj9iHQ3F.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-JEbYJFxw.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-C3xx_inM.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-C3xx_inM.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-Dw306gJU.js +0 -1
- package/dist/dashboard/assets/CustomizationPage-Dw306gJU.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-jaBo1TWV.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-jaBo1TWV.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-C4w7vea-.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-BtPcAfot.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-CGC5gNs1.js +0 -1
- package/dist/dashboard/assets/OverviewPage-CGC5gNs1.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-DQcthNGt.js +0 -4
- package/dist/dashboard/assets/ProviderDetailPage-DQcthNGt.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-0OsXRGXe.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-0OsXRGXe.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-DdPc8slb.js +0 -9
- package/dist/dashboard/assets/ProxyPage-DdPc8slb.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-Ci5O4BnH.js +0 -1
- package/dist/dashboard/assets/QuotaPage-Ci5O4BnH.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CDZjTt87.js +0 -1
- package/dist/dashboard/assets/SettingsPage-CDZjTt87.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-BQS0Rx_u.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-BAL5npBC.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-TP7voAQ0.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-Cj_fEzhg.js +0 -56
- package/dist/dashboard/assets/UsagePage-Cj_fEzhg.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-MbI7e7rt.js +0 -1
- package/dist/dashboard/assets/api-keys-MbI7e7rt.js.gz +0 -0
- package/dist/dashboard/assets/app-B2vyPoII.css +0 -2
- package/dist/dashboard/assets/app-B2vyPoII.css.gz +0 -0
- package/dist/dashboard/assets/assert-C0YYuZJ4.js +0 -1
- package/dist/dashboard/assets/assert-C0YYuZJ4.js.gz +0 -0
- package/dist/dashboard/assets/badge-B_DMpMJN.js.gz +0 -0
- package/dist/dashboard/assets/banner-DMrMPmth.js.gz +0 -0
- package/dist/dashboard/assets/card-Be5Uzamx.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-muFRphDu.js +0 -1
- package/dist/dashboard/assets/cli-tools-muFRphDu.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-DOfrVmsi.js.gz +0 -0
- package/dist/dashboard/assets/dialog-C9omdjQt.js.gz +0 -0
- package/dist/dashboard/assets/drawer-Bm8WKaGY.js.gz +0 -0
- package/dist/dashboard/assets/entry-DS90gUks.js +0 -1
- package/dist/dashboard/assets/entry-DS90gUks.js.gz +0 -0
- package/dist/dashboard/assets/entry-j40LJotO.js +0 -4
- package/dist/dashboard/assets/entry-j40LJotO.js.gz +0 -0
- package/dist/dashboard/assets/entry-q5HwZ8nN.js.gz +0 -0
- package/dist/dashboard/assets/formatters-BTScrQpe.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-Orjuzemr.js.gz +0 -0
- package/dist/dashboard/assets/index-cPrX5vn6.js.gz +0 -0
- package/dist/dashboard/assets/inline-Dvux8w-0.js.gz +0 -0
- package/dist/dashboard/assets/input-CU1Rgf3p.js.gz +0 -0
- package/dist/dashboard/assets/layout-C_7A6H55.js.gz +0 -0
- package/dist/dashboard/assets/list-row-CzFvogbj.js.gz +0 -0
- package/dist/dashboard/assets/live-KaG15HLQ.js +0 -1
- package/dist/dashboard/assets/live-KaG15HLQ.js.gz +0 -0
- package/dist/dashboard/assets/minimize-2-D-MN8W8a.js +0 -1
- package/dist/dashboard/assets/minimize-2-D-MN8W8a.js.gz +0 -0
- package/dist/dashboard/assets/mutation-JJGQm8TD.js.gz +0 -0
- package/dist/dashboard/assets/numbers-BNDZnsUE.js.gz +0 -0
- package/dist/dashboard/assets/query-keys-CLaNz8Xz.js.gz +0 -0
- package/dist/dashboard/assets/quota-DZGyp0N5.js +0 -1
- package/dist/dashboard/assets/quota-DZGyp0N5.js.gz +0 -0
- package/dist/dashboard/assets/routing-BEH32QOC.js +0 -1
- package/dist/dashboard/assets/routing-BEH32QOC.js.gz +0 -0
- package/dist/dashboard/assets/select-B3iTetHN.js.gz +0 -0
- package/dist/dashboard/assets/settings-Dt3Eqbnq.js +0 -1
- package/dist/dashboard/assets/settings-Dt3Eqbnq.js.gz +0 -0
- package/dist/dashboard/assets/stack-KrOrm7JZ.js.gz +0 -0
- package/dist/dashboard/assets/state-DLS7IDMy.js.gz +0 -0
- package/dist/dashboard/assets/switch-42ItvbAs.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-Dmnfh1Ue.js.gz +0 -0
- /package/dist/dashboard/assets/{app-wvwCi4Eh.js → app-BayTTy01.js} +0 -0
- /package/dist/dashboard/assets/{app-wvwCi4Eh.js.gz → app-BayTTy01.js.gz} +0 -0
|
@@ -1,74 +1,75 @@
|
|
|
1
1
|
{
|
|
2
|
-
"count":
|
|
2
|
+
"count": 118,
|
|
3
3
|
"assets": [
|
|
4
4
|
"assets/AccountCooldown-Bjo92Qat.js",
|
|
5
|
-
"assets/BannedPage-
|
|
6
|
-
"assets/CliToolDetailPage-
|
|
7
|
-
"assets/CliToolsPage-
|
|
8
|
-
"assets/CombosPage-
|
|
9
|
-
"assets/ConsoleLogPage-
|
|
10
|
-
"assets/CustomizationPage-
|
|
11
|
-
"assets/HealthEventsModal-
|
|
12
|
-
"assets/LoginPage-
|
|
13
|
-
"assets/ModelPicker-
|
|
14
|
-
"assets/OverviewPage-
|
|
15
|
-
"assets/ProviderDetailPage-
|
|
5
|
+
"assets/BannedPage-ChG5svFb.js",
|
|
6
|
+
"assets/CliToolDetailPage-BIcWQun7.js",
|
|
7
|
+
"assets/CliToolsPage-Bf2VelDe.js",
|
|
8
|
+
"assets/CombosPage-DCgm5g6g.js",
|
|
9
|
+
"assets/ConsoleLogPage-a8mD0QF1.js",
|
|
10
|
+
"assets/CustomizationPage-ggeMSjte.js",
|
|
11
|
+
"assets/HealthEventsModal-DzSWzE_j.js",
|
|
12
|
+
"assets/LoginPage-U6AKaJcF.js",
|
|
13
|
+
"assets/ModelPicker-BMS2t8BA.js",
|
|
14
|
+
"assets/OverviewPage-D2D4IiHl.js",
|
|
15
|
+
"assets/ProviderDetailPage-CrNf7j_X.js",
|
|
16
16
|
"assets/ProviderIcon-FB6yS4v1.js",
|
|
17
|
-
"assets/ProvidersPage-
|
|
18
|
-
"assets/ProxyPage-
|
|
19
|
-
"assets/QuotaPage-
|
|
20
|
-
"assets/SettingsPage-
|
|
21
|
-
"assets/SetupPage-
|
|
22
|
-
"assets/SortableList-
|
|
23
|
-
"assets/StudioPage-
|
|
17
|
+
"assets/ProvidersPage-BSDbquc1.js",
|
|
18
|
+
"assets/ProxyPage-lsrx7NYg.js",
|
|
19
|
+
"assets/QuotaPage-L0skzML8.js",
|
|
20
|
+
"assets/SettingsPage-CmAbjRHW.js",
|
|
21
|
+
"assets/SetupPage-f0DE1h0-.js",
|
|
22
|
+
"assets/SortableList-ClzpwyET.js",
|
|
23
|
+
"assets/StudioPage-Cq5tW7hW.js",
|
|
24
24
|
"assets/ToolIcon-K1ckcDKg.js",
|
|
25
|
-
"assets/UsagePage-
|
|
26
|
-
"assets/api-keys-
|
|
27
|
-
"assets/app-
|
|
28
|
-
"assets/app-
|
|
25
|
+
"assets/UsagePage-Dfc9rWQn.js",
|
|
26
|
+
"assets/api-keys-C38iXoO4.js",
|
|
27
|
+
"assets/app-BayTTy01.js",
|
|
28
|
+
"assets/app-a159k0ub.css",
|
|
29
29
|
"assets/arrow-left-CpBxkoxI.js",
|
|
30
30
|
"assets/arrow-right-Dl5yKnp8.js",
|
|
31
31
|
"assets/arrow-up-59ms1iiT.js",
|
|
32
|
-
"assets/assert-
|
|
32
|
+
"assets/assert-DbRA8EG2.js",
|
|
33
33
|
"assets/backup-file-gdKIlg8U.js",
|
|
34
|
-
"assets/badge-
|
|
35
|
-
"assets/banner-
|
|
34
|
+
"assets/badge-D9CS6dZk.js",
|
|
35
|
+
"assets/banner-DympBNq4.js",
|
|
36
36
|
"assets/brain-Cwed_72z.js",
|
|
37
|
-
"assets/card-
|
|
37
|
+
"assets/card-ibRWU_uT.js",
|
|
38
38
|
"assets/chevron-right-BXIy5l3h.js",
|
|
39
39
|
"assets/circle-alert-CntYHV5v.js",
|
|
40
40
|
"assets/circle-check-BU3v_mFW.js",
|
|
41
|
-
"assets/cli-tools-
|
|
42
|
-
"assets/clipboard-button-
|
|
41
|
+
"assets/cli-tools-63qBGaRw.js",
|
|
42
|
+
"assets/clipboard-button-DJm-9pz7.js",
|
|
43
43
|
"assets/database-Dz9N3zGd.js",
|
|
44
|
-
"assets/dialog-
|
|
44
|
+
"assets/dialog-8fiE6tec.js",
|
|
45
45
|
"assets/dialog-actions-B1axpEg7.js",
|
|
46
46
|
"assets/download-dRFo5_Wu.js",
|
|
47
|
-
"assets/drawer-
|
|
48
|
-
"assets/entry-
|
|
49
|
-
"assets/entry-
|
|
50
|
-
"assets/entry-
|
|
47
|
+
"assets/drawer-CqGYeOrd.js",
|
|
48
|
+
"assets/entry-BSAsYsmV.js",
|
|
49
|
+
"assets/entry-CiIQULZD.js",
|
|
50
|
+
"assets/entry-Daz0lJ-W.js",
|
|
51
51
|
"assets/eye-C_hawfH4.js",
|
|
52
52
|
"assets/eye-off-Bk5g2luF.js",
|
|
53
53
|
"assets/fingerprint-pattern-BWuHtbW4.js",
|
|
54
|
-
"assets/formatters-
|
|
54
|
+
"assets/formatters-D-MTJxMr.js",
|
|
55
55
|
"assets/gauge-BbIMy7MB.js",
|
|
56
|
-
"assets/github-badge-
|
|
56
|
+
"assets/github-badge-CPK47xQx.js",
|
|
57
57
|
"assets/image-BPaouViG.js",
|
|
58
|
-
"assets/index-
|
|
59
|
-
"assets/inline-
|
|
60
|
-
"assets/input-
|
|
58
|
+
"assets/index-DBNPOfhV.js",
|
|
59
|
+
"assets/inline-BT0zntEr.js",
|
|
60
|
+
"assets/input-Da2ZViWg.js",
|
|
61
61
|
"assets/jsx-runtime-CXUMngI_.js",
|
|
62
62
|
"assets/key-round-UkavsGxp.js",
|
|
63
63
|
"assets/layers-2-glBbjl.js",
|
|
64
|
-
"assets/layout-
|
|
65
|
-
"assets/list-row-
|
|
66
|
-
"assets/live-
|
|
64
|
+
"assets/layout-BXNeLw6M.js",
|
|
65
|
+
"assets/list-row-BycOrrIV.js",
|
|
66
|
+
"assets/live-DCQNZpF7.js",
|
|
67
67
|
"assets/log-in-BmRjy60c.js",
|
|
68
68
|
"assets/message-circle-CCxGwRvv.js",
|
|
69
|
-
"assets/minimize-2-
|
|
70
|
-
"assets/mutation-
|
|
71
|
-
"assets/
|
|
69
|
+
"assets/minimize-2-BTmMTK4o.js",
|
|
70
|
+
"assets/mutation-Rr0TIvcj.js",
|
|
71
|
+
"assets/network-9Yj2ZyLo.js",
|
|
72
|
+
"assets/numbers-CEagj6ZZ.js",
|
|
72
73
|
"assets/pencil-768ysRAr.js",
|
|
73
74
|
"assets/plus-DIdX_g-x.js",
|
|
74
75
|
"assets/power-off-Yjw9GMJh.js",
|
|
@@ -91,22 +92,22 @@
|
|
|
91
92
|
"assets/providers/parallel.svg",
|
|
92
93
|
"assets/providers/tokenharbor.svg",
|
|
93
94
|
"assets/providers/zai-coding-plan.svg",
|
|
94
|
-
"assets/query-keys-
|
|
95
|
-
"assets/quota-
|
|
95
|
+
"assets/query-keys-BloKtaXJ.js",
|
|
96
|
+
"assets/quota-Caw7wqci.js",
|
|
96
97
|
"assets/refresh-cw-BqJFk4sU.js",
|
|
97
98
|
"assets/rotate-ccw-CufcqITi.js",
|
|
98
99
|
"assets/route-CNSNj9Zm.js",
|
|
99
|
-
"assets/routing-
|
|
100
|
+
"assets/routing-CO9zCUus.js",
|
|
100
101
|
"assets/scaling-nWE4Cx8f.js",
|
|
101
102
|
"assets/search-JOKus9rb.js",
|
|
102
|
-
"assets/select-
|
|
103
|
+
"assets/select-BIYIBkMU.js",
|
|
103
104
|
"assets/settings-2-Ds9DQp0t.js",
|
|
104
|
-
"assets/settings-
|
|
105
|
+
"assets/settings-jS3-PNSA.js",
|
|
105
106
|
"assets/shield-check-DVS0Foqh.js",
|
|
106
|
-
"assets/stack-
|
|
107
|
-
"assets/state-
|
|
107
|
+
"assets/stack-DfgnFDWY.js",
|
|
108
|
+
"assets/state-CkfdYWel.js",
|
|
108
109
|
"assets/stream-retry-5QnVuSSo.js",
|
|
109
|
-
"assets/switch-
|
|
110
|
+
"assets/switch-SYm237HG.js",
|
|
110
111
|
"assets/terminal-0_IjPWTk.js",
|
|
111
112
|
"assets/tokens-COzario9.js",
|
|
112
113
|
"assets/trash-Cppn5c3R.js",
|
|
@@ -114,7 +115,7 @@
|
|
|
114
115
|
"assets/use-modal-focus-By7oJoGd.js",
|
|
115
116
|
"assets/use-reduced-motion-C4B0R6Sz.js",
|
|
116
117
|
"assets/use-timeout-BwcBBRrT.js",
|
|
117
|
-
"assets/useMutation-
|
|
118
|
+
"assets/useMutation-BQqCRNIi.js",
|
|
118
119
|
"assets/wrench-B6E1oEuY.js",
|
|
119
120
|
"assets/zap-C4EeVW4q.js",
|
|
120
121
|
"index.html"
|
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
-- Qoder native identity: genuine SG identity + per-account seed replay.
|
|
2
2
|
--
|
|
3
|
-
-- Research (2026-10-30, see
|
|
4
|
-
--
|
|
5
|
-
--
|
|
6
|
-
--
|
|
7
|
-
--
|
|
3
|
+
-- Research (2026-10-30, see
|
|
4
|
+
-- documentation/archive/research/Qoder-03-umid-and-identity-findings.md)
|
|
5
|
+
-- established that machine identity does not gate the daily-credit campaign.
|
|
6
|
+
-- The seed is retained for deterministic replay of a native identity on
|
|
7
|
+
-- surfaces that may require one; it is not required for campaign eligibility.
|
|
8
8
|
--
|
|
9
9
|
-- Migration 0026 already added `native_identity` (the token triple + optional
|
|
10
10
|
-- seed) with a partial unique index on the extracted machineToken — the
|
|
11
11
|
-- "one device per account" invariant. This migration does not change that
|
|
12
12
|
-- schema; it only documents the contract change on the same column:
|
|
13
13
|
--
|
|
14
|
-
-- `seed`
|
|
15
|
-
--
|
|
16
|
-
--
|
|
17
|
-
--
|
|
18
|
-
-- claim; the store treats a missing/empty seed as "not replay-capable".
|
|
14
|
+
-- `seed` makes a native identity replay-capable (the token can be
|
|
15
|
+
-- re-derived from the seed, never the reverse). Rows minted by the
|
|
16
|
+
-- generated path may have `seed = ""`; that affects native replay only,
|
|
17
|
+
-- not the daily campaign claim.
|
|
19
18
|
--
|
|
20
19
|
-- No DDL is needed: the JSON shape already carries everything. Kept as an
|
|
21
20
|
-- empty migration so the migration numbering records the policy change.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
-- Pool-as-account routing entities for ip-scoped, credential-less providers.
|
|
2
|
+
--
|
|
3
|
+
-- For providers whose rate limit follows the egress address (OpenCode Free),
|
|
4
|
+
-- the pool is the resource that runs out — but it was invisible to account-level
|
|
5
|
+
-- health and routing because the provider owns no `provider_accounts` rows.
|
|
6
|
+
-- This table materializes one row per (pool × ip-scoped provider), carrying the
|
|
7
|
+
-- same health state machine (`status`, `cooldown_until`, …) so a spent egress
|
|
8
|
+
-- is excluded from routing plans exactly like a cooling account, and shows in
|
|
9
|
+
-- the provider's Accounts card with a "Pool" badge.
|
|
10
|
+
--
|
|
11
|
+
-- The row is an account-equivalent, not an account: it resolves no credential,
|
|
12
|
+
-- is skipped by quota-refresh and identity sweeps (they filter on real
|
|
13
|
+
-- credential kinds), and is owned by its pool — deleting the pool cascades the
|
|
14
|
+
-- row. Rows are backfilled here for every existing pool × ip-scoped provider so
|
|
15
|
+
-- no operator action is needed to adopt the feature.
|
|
16
|
+
CREATE TABLE IF NOT EXISTS "provider_pool_accounts" (
|
|
17
|
+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
18
|
+
"tenant_id" uuid NOT NULL REFERENCES "tenants" ("id") ON DELETE CASCADE,
|
|
19
|
+
"network_pool_id" uuid NOT NULL REFERENCES "network_pools" ("id") ON DELETE CASCADE,
|
|
20
|
+
"provider_id" text NOT NULL,
|
|
21
|
+
"label" text NOT NULL,
|
|
22
|
+
"status" text NOT NULL DEFAULT 'active'
|
|
23
|
+
CHECK ("status" IN ('active', 'cooldown', 'disabled')),
|
|
24
|
+
"consecutive_failures" integer NOT NULL DEFAULT 0,
|
|
25
|
+
"last_success_at" timestamptz,
|
|
26
|
+
"last_error" text,
|
|
27
|
+
"last_error_category" text,
|
|
28
|
+
"last_error_at" timestamptz,
|
|
29
|
+
"cooldown_until" timestamptz,
|
|
30
|
+
"last_recovered_at" timestamptz,
|
|
31
|
+
"created_at" timestamptz NOT NULL DEFAULT now(),
|
|
32
|
+
"updated_at" timestamptz NOT NULL DEFAULT now(),
|
|
33
|
+
CONSTRAINT provider_pool_accounts_pool_provider_uidx
|
|
34
|
+
UNIQUE ("network_pool_id", "provider_id", "tenant_id")
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
CREATE INDEX IF NOT EXISTS provider_pool_accounts_tenant_status_idx
|
|
38
|
+
ON "provider_pool_accounts" ("tenant_id", "status");
|
|
39
|
+
CREATE INDEX IF NOT EXISTS provider_pool_accounts_provider_status_idx
|
|
40
|
+
ON "provider_pool_accounts" ("provider_id", "status");
|
|
41
|
+
|
|
42
|
+
-- Backfill: every existing pool × ip-scoped provider gets its row. The
|
|
43
|
+
-- ip-scoped set is OpenCode Free today; the provider id is spelled here so the
|
|
44
|
+
-- rule stays a data fact, not a code constant.
|
|
45
|
+
INSERT INTO "provider_pool_accounts"
|
|
46
|
+
("tenant_id", "network_pool_id", "provider_id", "label")
|
|
47
|
+
SELECT
|
|
48
|
+
p."tenant_id",
|
|
49
|
+
p."id",
|
|
50
|
+
'opencode',
|
|
51
|
+
'Pool ' || left(p."id"::text, 8)
|
|
52
|
+
FROM "network_pools" p
|
|
53
|
+
ON CONFLICT ("network_pool_id", "provider_id", "tenant_id") DO NOTHING;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
-- API-key ban state.
|
|
2
|
+
--
|
|
3
|
+
-- A ban is distinct from revoke (terminal, tombstone-like) and disable
|
|
4
|
+
-- (operator toggle, reversible): it records *why* a key was banned
|
|
5
|
+
-- (`ban_reason`) for the console's "See reason" modal. Unban clears both
|
|
6
|
+
-- columns entirely — no tombstone, no leftover metadata.
|
|
7
|
+
ALTER TABLE "api_keys"
|
|
8
|
+
ADD COLUMN "banned_at" timestamptz,
|
|
9
|
+
ADD COLUMN "ban_reason" text;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
-- API-key ban expiry.
|
|
2
|
+
--
|
|
3
|
+
-- A ban may now carry an optional `banned_until`: a timed ban lifts on its
|
|
4
|
+
-- own (auth checks `banned_at` against `banned_until`), while `null` keeps the
|
|
5
|
+
-- earlier permanent-ban behavior. Unban clears all three ban columns.
|
|
6
|
+
ALTER TABLE "api_keys"
|
|
7
|
+
ADD COLUMN "banned_until" timestamptz;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
-- Align `provider_pool_accounts.status` with the shared `health_status` enum.
|
|
2
|
+
--
|
|
3
|
+
-- The table was created with a `text` column plus an inline CHECK (migration
|
|
4
|
+
-- 0032), while every other health-state column uses the `health_status` enum.
|
|
5
|
+
-- The Drizzle schema declares the enum, so the drift checker reported the text
|
|
6
|
+
-- column and its CHECK as "produced by migrations but not in schema". This
|
|
7
|
+
-- migration rewrites the column to the enum: same allowed values, one
|
|
8
|
+
-- canonical type.
|
|
9
|
+
--
|
|
10
|
+
-- The inline CHECK from 0032 must be dropped first: PostgreSQL rewrites
|
|
11
|
+
-- CHECK expressions during an ALTER TYPE, and the old `status IN ('active',
|
|
12
|
+
-- ...)` expression compares the new enum column against text literals, which
|
|
13
|
+
-- has no operator once the column becomes the enum.
|
|
14
|
+
|
|
15
|
+
ALTER TABLE "provider_pool_accounts" DROP CONSTRAINT "provider_pool_accounts_status_check";
|
|
16
|
+
|
|
17
|
+
ALTER TABLE "provider_pool_accounts"
|
|
18
|
+
ALTER COLUMN "status" DROP DEFAULT,
|
|
19
|
+
ALTER COLUMN "status" SET DATA TYPE "public"."health_status"
|
|
20
|
+
USING "status"::"public"."health_status",
|
|
21
|
+
ALTER COLUMN "status" SET DEFAULT 'active';
|
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
-- Qoder native identity: genuine SG identity + per-account seed replay.
|
|
2
2
|
--
|
|
3
|
-
-- Research (2026-10-30, see
|
|
4
|
-
--
|
|
5
|
-
--
|
|
6
|
-
--
|
|
7
|
-
--
|
|
3
|
+
-- Research (2026-10-30, see
|
|
4
|
+
-- documentation/archive/research/Qoder-03-umid-and-identity-findings.md)
|
|
5
|
+
-- established that machine identity does not gate the daily-credit campaign.
|
|
6
|
+
-- The seed is retained for deterministic replay of a native identity on
|
|
7
|
+
-- surfaces that may require one; it is not required for campaign eligibility.
|
|
8
8
|
--
|
|
9
9
|
-- Migration 0026 already added `native_identity` (the token triple + optional
|
|
10
10
|
-- seed) with a partial unique index on the extracted machineToken — the
|
|
11
11
|
-- "one device per account" invariant. This migration does not change that
|
|
12
12
|
-- schema; it only documents the contract change on the same column:
|
|
13
13
|
--
|
|
14
|
-
-- `seed`
|
|
15
|
-
--
|
|
16
|
-
--
|
|
17
|
-
--
|
|
18
|
-
-- claim; the store treats a missing/empty seed as "not replay-capable".
|
|
14
|
+
-- `seed` makes a native identity replay-capable (the token can be
|
|
15
|
+
-- re-derived from the seed, never the reverse). Rows minted by the
|
|
16
|
+
-- generated path may have `seed = ""`; that affects native replay only,
|
|
17
|
+
-- not the daily campaign claim.
|
|
19
18
|
--
|
|
20
19
|
-- No DDL is needed: the JSON shape already carries everything. Kept as an
|
|
21
20
|
-- empty migration so the migration numbering records the policy change.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
-- Pool-as-account routing entities for ip-scoped, credential-less providers.
|
|
2
|
+
--
|
|
3
|
+
-- Mirror of `full-postgres/0032_add_provider_pool_accounts.sql`; see that file
|
|
4
|
+
-- for the rationale. SQLite representation: `id`/`tenant_id`/`network_pool_id`
|
|
5
|
+
-- are text (uuid), `status` is text with a CHECK, timestamps are text.
|
|
6
|
+
CREATE TABLE IF NOT EXISTS "provider_pool_accounts" (
|
|
7
|
+
"id" text PRIMARY KEY,
|
|
8
|
+
"tenant_id" text NOT NULL REFERENCES "tenants" ("id") ON DELETE CASCADE,
|
|
9
|
+
"network_pool_id" text NOT NULL REFERENCES "network_pools" ("id") ON DELETE CASCADE,
|
|
10
|
+
"provider_id" text NOT NULL,
|
|
11
|
+
"label" text NOT NULL,
|
|
12
|
+
"status" text NOT NULL DEFAULT 'active'
|
|
13
|
+
CHECK ("status" IN ('active', 'cooldown', 'disabled')),
|
|
14
|
+
"consecutive_failures" integer NOT NULL DEFAULT 0,
|
|
15
|
+
"last_success_at" text,
|
|
16
|
+
"last_error" text,
|
|
17
|
+
"last_error_category" text,
|
|
18
|
+
"last_error_at" text,
|
|
19
|
+
"cooldown_until" text,
|
|
20
|
+
"last_recovered_at" text,
|
|
21
|
+
"created_at" text NOT NULL DEFAULT (datetime('now')),
|
|
22
|
+
"updated_at" text NOT NULL DEFAULT (datetime('now')),
|
|
23
|
+
CONSTRAINT provider_pool_accounts_pool_provider_uidx
|
|
24
|
+
UNIQUE ("network_pool_id", "provider_id", "tenant_id")
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
CREATE INDEX IF NOT EXISTS provider_pool_accounts_tenant_status_idx
|
|
28
|
+
ON "provider_pool_accounts" ("tenant_id", "status");
|
|
29
|
+
CREATE INDEX IF NOT EXISTS provider_pool_accounts_provider_status_idx
|
|
30
|
+
ON "provider_pool_accounts" ("provider_id", "status");
|
|
31
|
+
|
|
32
|
+
-- Backfill: every existing pool × ip-scoped provider gets its row. The
|
|
33
|
+
-- ip-scoped set is OpenCode Free today; the provider id is spelled here so the
|
|
34
|
+
-- rule stays a data fact, not a code constant. SQLite's bun driver rejects the
|
|
35
|
+
-- newer `ON CONFLICT DO NOTHING` on an INSERT…SELECT, so the equivalent
|
|
36
|
+
-- `INSERT OR IGNORE` form is used here (Postgres version keeps the standard one).
|
|
37
|
+
INSERT OR IGNORE INTO "provider_pool_accounts"
|
|
38
|
+
("tenant_id", "network_pool_id", "provider_id", "label")
|
|
39
|
+
SELECT
|
|
40
|
+
p."tenant_id",
|
|
41
|
+
p."id",
|
|
42
|
+
'opencode',
|
|
43
|
+
'Pool ' || substr(p."id", 1, 8)
|
|
44
|
+
FROM "network_pools" p;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
-- API-key ban state. Mirror of `full-postgres/0033_add_api_key_ban.sql`;
|
|
2
|
+
-- SQLite stores timestamps as text (ISO), and each `ALTER TABLE ... ADD COLUMN`
|
|
3
|
+
-- may carry exactly one column.
|
|
4
|
+
ALTER TABLE "api_keys"
|
|
5
|
+
ADD COLUMN "banned_at" text;
|
|
6
|
+
|
|
7
|
+
ALTER TABLE "api_keys"
|
|
8
|
+
ADD COLUMN "ban_reason" text;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
-- SQLite mirror of `full-postgres/0035_align_pool_account_status_enum.sql`.
|
|
2
|
+
--
|
|
3
|
+
-- SQLite has no enum type: `provider_pool_accounts.status` is a `text`
|
|
4
|
+
-- column with a CHECK constraint already (migration 0032), which matches the
|
|
5
|
+
-- shared schema's semantics. Nothing to change here; the file exists so both
|
|
6
|
+
-- backends carry the same numbered migration set.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cartethyia",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.4",
|
|
4
4
|
"description": "A powerful, self-hosted reverse proxy for OpenAI- and Anthropic-compatible AI clients, powered by native Bun and Elysia. It serves both protocols from a single server, routes every model across any configured provider, and translates requests and responses in real time — streaming and prompt caching included. Underneath it runs a full delivery engine: per-account health tracking, quota and cooldown enforcement, automatic failover across pools and providers, usage accounting, and telemetry. Point OpenCode, Cline, Claude Code, Codex, or any compatible client at Cartethyia and it keeps its native protocol while the gateway absorbs every provider-specific difference behind it. Runs on embedded SQLite or external PostgreSQL with optional Redis coordination, ships as a global CLI with a system-tray console, and needs only Bun 1.4+ alongside Node.js/npm.",
|
|
5
5
|
"license": "GPL-3.0-only",
|
|
6
6
|
"type": "module",
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"start": "bun run scripts/cli/main.ts start",
|
|
49
49
|
"doctor": "bun run scripts/cli/doctor.ts",
|
|
50
50
|
"probe:egress": "bun run scripts/tools/probe-egress.ts",
|
|
51
|
+
"probe:qoder": "bun run scripts/provider-probing/qoder/probe-qoder.ts",
|
|
51
52
|
"restart": "bun run scripts/build/restart.ts",
|
|
52
53
|
"build:aot": "bun run scripts/build/aot.ts",
|
|
53
54
|
"dev:stack": "concurrently -n BACKEND,DASHBOARD -c cyan,magenta --kill-others-on-fail \"bun run dev:backend\" \"bun run dashboard:dev\"",
|
package/src/boot/app.ts
CHANGED
|
@@ -221,7 +221,7 @@ export function createGatewayApp(deps: GatewayAppDeps) {
|
|
|
221
221
|
(prefix) => pathname === prefix || pathname.startsWith(`${prefix}/`),
|
|
222
222
|
);
|
|
223
223
|
if (reserved) {
|
|
224
|
-
const notFound = new GatewayError("not_found", 404, "No endpoint exists at this path. Check the route against the API reference.");
|
|
224
|
+
const notFound = new GatewayError("not_found", 404, "No endpoint exists at this path (request {requestId}). Check the route against the API reference. Nothing here but dust — check the path 🌙");
|
|
225
225
|
return new Response(JSON.stringify(publicGatewayErrorBody(notFound)), {
|
|
226
226
|
status: notFound.status,
|
|
227
227
|
headers: {
|
|
@@ -244,8 +244,9 @@ export function createGatewayApp(deps: GatewayAppDeps) {
|
|
|
244
244
|
// Shutdown ordering: abort in-flight proxy controllers first so the
|
|
245
245
|
// bounded drain observes cancellation and finalizers run before
|
|
246
246
|
// telemetry flush and pool close. The reason is threaded through so the
|
|
247
|
-
// streaming path emits a drain-aware terminal frame (`
|
|
248
|
-
// vs `shutting_down`) instead of a bare
|
|
247
|
+
// streaming path emits a drain-aware terminal frame (`shutting_down` with
|
|
248
|
+
// reason `restart_for_update` vs plain `shutting_down`) instead of a bare
|
|
249
|
+
// abort that reads as a client drop.
|
|
249
250
|
// Only production has a coordinator to wire; the shell never drains.
|
|
250
251
|
if (deps.mode === "production") {
|
|
251
252
|
const coordinator = deps.shutdownCoordinator;
|
|
@@ -469,7 +470,7 @@ export function createGatewayApp(deps: GatewayAppDeps) {
|
|
|
469
470
|
throw new GatewayError(
|
|
470
471
|
"model_not_found",
|
|
471
472
|
404,
|
|
472
|
-
`The model '${targetId}' does not exist or you do not have access to it
|
|
473
|
+
`The model '${targetId}' does not exist or you do not have access to it (request {requestId}). Try another model — this one's off the menu 📚`,
|
|
473
474
|
);
|
|
474
475
|
return new Response(JSON.stringify(found), {
|
|
475
476
|
headers: {
|
|
@@ -600,14 +601,14 @@ export function createGatewayApp(deps: GatewayAppDeps) {
|
|
|
600
601
|
// for something only the local host may ask. `invalid_request` told an
|
|
601
602
|
// operator to fix the body, when the body was never the problem.
|
|
602
603
|
if (!isLoopbackPeer(peerAddresses.get(request) ?? null))
|
|
603
|
-
throw new GatewayError("policy_rejected", 403, "task trigger is only available from loopback");
|
|
604
|
+
throw new GatewayError("policy_rejected", 403, "task trigger is only available from loopback (request {requestId}). This operation is only allowed from a trusted loopback connection — no hard feelings 🫂");
|
|
604
605
|
if (!tokenMatches(request.headers.get("x-drain-token"), token))
|
|
605
|
-
throw new GatewayError("authentication_failed", 401, "invalid drain token");
|
|
606
|
+
throw new GatewayError("authentication_failed", 401, "invalid drain token (request {requestId}). Wrong key, right door — check the token 🔑");
|
|
606
607
|
const name = params.name;
|
|
607
608
|
if (!tasks.names().includes(name))
|
|
608
|
-
throw new GatewayError("
|
|
609
|
+
throw new GatewayError("not_found", 404, `no such task: ${name} (request {requestId}). Nothing here but dust — check the task name 🌙`);
|
|
609
610
|
if (tasks.isRunning(name))
|
|
610
|
-
throw new GatewayError("invalid_request", 409, `${name} is already running
|
|
611
|
+
throw new GatewayError("invalid_request", 409, `${name} is already running (request {requestId}). One at a time — wait for it to finish ⏳`);
|
|
611
612
|
void tasks.runNow(name);
|
|
612
613
|
return new Response(JSON.stringify({ task: name, status: "started" }), {
|
|
613
614
|
status: 202,
|
package/src/config/network.ts
CHANGED
|
@@ -111,10 +111,14 @@ export const DEFAULT_BOUNDS = {
|
|
|
111
111
|
* the worker sweeps (`runGrowingWaves`, at most 5 concurrent), auth, and
|
|
112
112
|
* readiness.
|
|
113
113
|
*
|
|
114
|
-
*
|
|
114
|
+
* 16 leaves headroom over that without letting one process hold a large
|
|
115
115
|
* share of the server's `max_connections` (Postgres defaults to 100), which
|
|
116
116
|
* matters because the gateway is designed to run several processes behind
|
|
117
|
-
* `reusePort` — see `assertPoolFitsServerCapacity`.
|
|
117
|
+
* `reusePort` — see `assertPoolFitsServerCapacity`. With well-optimized
|
|
118
|
+
* queries a 16-connection pool sustains a hundred concurrent requests: the
|
|
119
|
+
* connections are held for microseconds per query, not for the whole
|
|
120
|
+
* request. If a burst still queues on the pool, fix the query/consumer
|
|
121
|
+
* that holds connections, rather than raising the ceiling.
|
|
118
122
|
*/
|
|
119
|
-
maxPostgresPoolSize:
|
|
123
|
+
maxPostgresPoolSize: 16,
|
|
120
124
|
} as const;
|
|
@@ -101,6 +101,11 @@ export interface ApiKeyResponse {
|
|
|
101
101
|
readonly sharePopupBody?: string;
|
|
102
102
|
readonly createdAt: string;
|
|
103
103
|
readonly revokedAt?: string;
|
|
104
|
+
/** Ban state; see the schema columns for the contract. */
|
|
105
|
+
readonly bannedAt?: string;
|
|
106
|
+
readonly banReason?: string;
|
|
107
|
+
/** When the ban lifts on its own; absent means permanent. */
|
|
108
|
+
readonly bannedUntil?: string;
|
|
104
109
|
readonly tokensConsumed: number;
|
|
105
110
|
}
|
|
106
111
|
/** Creation result; plaintext is returned only when a personal key is minted. */
|
|
@@ -113,6 +118,50 @@ export interface UpdateApiKeyResponse extends ApiKeyResponse {
|
|
|
113
118
|
readonly secret?: string;
|
|
114
119
|
}
|
|
115
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Ban reasons offered by the console dropdown. The first two are the operator's
|
|
123
|
+
* own: "Instant Ban — Suka suka Gw" for the arbitrary ban, and a template for
|
|
124
|
+
* suspicious traffic. The rest are generic, and an operator can still type a
|
|
125
|
+
* custom reason — the dropdown is a convenience, not a closed set.
|
|
126
|
+
*/
|
|
127
|
+
export const API_KEY_BAN_REASONS: readonly { readonly value: string; readonly label: string }[] = [
|
|
128
|
+
{ value: "instant_arbitrary", label: "Instant Ban — Suka suka Gw" },
|
|
129
|
+
{ value: "traffic_suspicious", label: "Your traffic looks suspicious" },
|
|
130
|
+
{ value: "abuse", label: "Abuse of the gateway" },
|
|
131
|
+
{ value: "policy_violation", label: "Policy violation" },
|
|
132
|
+
{ value: "compromised", label: "Key may be compromised" },
|
|
133
|
+
{ value: "quota_exceeded", label: "Quota abused" },
|
|
134
|
+
{ value: "other", label: "Other / custom" },
|
|
135
|
+
] as const;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Ban durations the console offers, in whole minutes. `null` is permanent.
|
|
139
|
+
* Mirrored by the dashboard (`dashboard/src/lib/ban-durations.ts`) so the UI
|
|
140
|
+
* and the API agree on one list.
|
|
141
|
+
*/
|
|
142
|
+
export const BAN_DURATION_CHOICES: readonly { readonly minutes: number | null; readonly label: string }[] = [
|
|
143
|
+
{ minutes: 60, label: "1 hour" },
|
|
144
|
+
{ minutes: 6 * 60, label: "6 hours" },
|
|
145
|
+
{ minutes: 12 * 60, label: "12 hours" },
|
|
146
|
+
{ minutes: 24 * 60, label: "1 day" },
|
|
147
|
+
{ minutes: 7 * 24 * 60, label: "7 days" },
|
|
148
|
+
{ minutes: null, label: "Permanent" },
|
|
149
|
+
];
|
|
150
|
+
|
|
151
|
+
/** What a bare "ban this key" click uses: the shortest, most reversible choice. */
|
|
152
|
+
export const DEFAULT_BAN_MINUTES: number = 60;
|
|
153
|
+
|
|
154
|
+
/** Body accepted by the ban endpoint. */
|
|
155
|
+
export interface BanApiKeyRequest {
|
|
156
|
+
readonly reason: string;
|
|
157
|
+
readonly reasonLabel?: string;
|
|
158
|
+
/**
|
|
159
|
+
* Ban duration in whole minutes from {@link BAN_DURATION_CHOICES}; `null`
|
|
160
|
+
* means permanent. Absent = {@link DEFAULT_BAN_MINUTES}.
|
|
161
|
+
*/
|
|
162
|
+
readonly minutes?: number | null;
|
|
163
|
+
}
|
|
164
|
+
|
|
116
165
|
/** API-key route dependency boundary. */
|
|
117
166
|
export interface ApiKeyConfig {
|
|
118
167
|
readonly store: ApiKeyStore;
|
|
@@ -400,6 +449,9 @@ export function sanitizeApiKeyResponse(record: ApiKeyRecord): ApiKeyResponse {
|
|
|
400
449
|
: { clientRouterDenylist: record.clientRouterDenylist }),
|
|
401
450
|
createdAt: record.createdAt.toISOString(),
|
|
402
451
|
...(record.revokedAt === undefined ? {} : { revokedAt: record.revokedAt.toISOString() }),
|
|
452
|
+
...(record.bannedAt === undefined ? {} : { bannedAt: record.bannedAt.toISOString() }),
|
|
453
|
+
...(record.banReason === undefined ? {} : { banReason: record.banReason }),
|
|
454
|
+
...(record.bannedUntil === undefined ? {} : { bannedUntil: record.bannedUntil.toISOString() }),
|
|
403
455
|
tokensConsumed: record.tokensConsumed,
|
|
404
456
|
};
|
|
405
457
|
}
|