cartethyia 2.7.7 → 2.7.9
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 +0 -1
- package/README.md +29 -59
- package/dist/dashboard/assets/BannedPage-BxoG1MrL.js +1 -0
- package/dist/dashboard/assets/BannedPage-BxoG1MrL.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-CLUhmjUy.js → CliToolDetailPage-ixwgKk53.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-ixwgKk53.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-CQmrqqrk.js → CliToolsPage-BmttNV3U.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-BmttNV3U.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-BE3qwwfC.js +1 -0
- package/dist/dashboard/assets/CombosPage-BE3qwwfC.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-CRZdmMIX.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-CRZdmMIX.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-Dv_FJ6Er.js +1 -0
- package/dist/dashboard/assets/CustomizationPage-Dv_FJ6Er.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-CoVrw2sM.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-CoVrw2sM.js.gz +0 -0
- package/dist/dashboard/assets/{InjectSystemPromptPanel-C-e2AMz6.js → InjectSystemPromptPanel-BOoO8rpz.js} +2 -2
- package/dist/dashboard/assets/InjectSystemPromptPanel-BOoO8rpz.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-J8wPbmfT.js → LoginPage-ChJuNNVN.js} +1 -1
- package/dist/dashboard/assets/LoginPage-ChJuNNVN.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-C6WlpU5E.js → ModelPicker-BZAJtPg1.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-BZAJtPg1.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-BE40s3u5.js +1 -0
- package/dist/dashboard/assets/OverviewPage-BE40s3u5.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-sfLoQtPV.js +8 -0
- package/dist/dashboard/assets/ProviderDetailPage-sfLoQtPV.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-CQ6bPUf_.js +1 -0
- package/dist/dashboard/assets/ProviderIcon-CQ6bPUf_.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-DcepmPWo.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-DcepmPWo.js.gz +0 -0
- package/dist/dashboard/assets/{ProxyPage-BBTRBAQ3.js → ProxyPage-BJ87Pp0U.js} +1 -1
- package/dist/dashboard/assets/ProxyPage-BJ87Pp0U.js.gz +0 -0
- package/dist/dashboard/assets/{QuotaPage-B2WqMSG1.js → QuotaPage-gkSyRAKa.js} +1 -1
- package/dist/dashboard/assets/QuotaPage-gkSyRAKa.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-C5HPWqEv.js +1 -0
- package/dist/dashboard/assets/SettingsPage-C5HPWqEv.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-LND6_Ss7.js → SetupPage-CFNcATJJ.js} +1 -1
- package/dist/dashboard/assets/SetupPage-CFNcATJJ.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-BzNnm6Bi.js → StudioPage-YoD4tY5l.js} +1 -1
- package/dist/dashboard/assets/StudioPage-YoD4tY5l.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-DxjaAhb_.js +56 -0
- package/dist/dashboard/assets/UsagePage-DxjaAhb_.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-BTlkq_Uk.js +1 -0
- package/dist/dashboard/assets/api-keys-BTlkq_Uk.js.gz +0 -0
- package/dist/dashboard/assets/app-BX2nnKqs.css +2 -0
- package/dist/dashboard/assets/app-BX2nnKqs.css.gz +0 -0
- package/dist/dashboard/assets/assert-CndthXFQ.js +1 -0
- package/dist/dashboard/assets/assert-CndthXFQ.js.gz +0 -0
- package/dist/dashboard/assets/{badge-BaX2GTrn.js → badge-DdbT_7Am.js} +1 -1
- package/dist/dashboard/assets/badge-DdbT_7Am.js.gz +0 -0
- package/dist/dashboard/assets/ban-durations-B58FyRxC.js +1 -0
- package/dist/dashboard/assets/ban-durations-B58FyRxC.js.gz +0 -0
- package/dist/dashboard/assets/{banner-DFH4Bkte.js → banner-CsGN-nbm.js} +1 -1
- package/dist/dashboard/assets/banner-CsGN-nbm.js.gz +0 -0
- package/dist/dashboard/assets/brand/bansos_page.webp +0 -0
- package/dist/dashboard/assets/brand/denia_bansos.webp +0 -0
- package/dist/dashboard/assets/brand/favicon.webp +0 -0
- package/dist/dashboard/assets/brand/login_register.webp +0 -0
- package/dist/dashboard/assets/brand/login_register_initial.webp +0 -0
- package/dist/dashboard/assets/brand/og_bansos.webp +0 -0
- package/dist/dashboard/assets/brand/og_image.webp +0 -0
- package/dist/dashboard/assets/brand/sidebar_ico.webp +0 -0
- package/dist/dashboard/assets/{card-CxDw0lK6.js → card-mgZ_TLLZ.js} +1 -1
- package/dist/dashboard/assets/card-mgZ_TLLZ.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-A1-v8-03.js +1 -0
- package/dist/dashboard/assets/cli-tools-A1-v8-03.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-DfS1ntiq.js → clipboard-button-CJ4Zty89.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-CJ4Zty89.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-BK-NRr5u.js → dialog-B5L2jPQ_.js} +1 -1
- package/dist/dashboard/assets/dialog-B5L2jPQ_.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-QqPul6sC.js → drawer-DkjUouAj.js} +1 -1
- package/dist/dashboard/assets/drawer-DkjUouAj.js.gz +0 -0
- package/dist/dashboard/assets/{entry-BMlYJVFj.js → entry-B-LVLV6o.js} +1 -1
- package/dist/dashboard/assets/entry-B-LVLV6o.js.gz +0 -0
- package/dist/dashboard/assets/entry-CAhdYl3B.js +1 -0
- package/dist/dashboard/assets/entry-CAhdYl3B.js.gz +0 -0
- package/dist/dashboard/assets/entry-D2xD0Tmd.js +4 -0
- package/dist/dashboard/assets/entry-D2xD0Tmd.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-DP-5E01-.js → formatters-CrsdLVsn.js} +1 -1
- package/dist/dashboard/assets/formatters-CrsdLVsn.js.gz +0 -0
- package/dist/dashboard/assets/{index-D36vCZht.js → index-BU64Lqvp.js} +2 -2
- package/dist/dashboard/assets/index-BU64Lqvp.js.gz +0 -0
- package/dist/dashboard/assets/{inline-CHJQwxLa.js → inline-B2aDLyyg.js} +1 -1
- package/dist/dashboard/assets/inline-B2aDLyyg.js.gz +0 -0
- package/dist/dashboard/assets/{input-BlmlizVV.js → input-BCVkdR9d.js} +1 -1
- package/dist/dashboard/assets/input-BCVkdR9d.js.gz +0 -0
- package/dist/dashboard/assets/{layout-Bz93I-yE.js → layout-DNcAdMK-.js} +1 -1
- package/dist/dashboard/assets/layout-DNcAdMK-.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-Ml16KCfu.js → list-row-DgrIH0hw.js} +1 -1
- package/dist/dashboard/assets/list-row-DgrIH0hw.js.gz +0 -0
- package/dist/dashboard/assets/{live-7_QpnYGu.js → live-Cae01iu8.js} +1 -1
- package/dist/dashboard/assets/live-Cae01iu8.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-GU7XdEjz.js → mutation-BQ0ccH_A.js} +1 -1
- package/dist/dashboard/assets/mutation-BQ0ccH_A.js.gz +0 -0
- package/dist/dashboard/assets/{network-CuMDaTzT.js → network-CRib7H_2.js} +1 -1
- package/dist/dashboard/assets/network-CRib7H_2.js.gz +0 -0
- package/dist/dashboard/assets/{numbers-CoqpaDMx.js → numbers-BKQE8Xpz.js} +1 -1
- package/dist/dashboard/assets/numbers-BKQE8Xpz.js.gz +0 -0
- package/dist/dashboard/assets/{pagination-B5aOoweZ.js → pagination-ecPvG_8g.js} +1 -1
- package/dist/dashboard/assets/pagination-ecPvG_8g.js.gz +0 -0
- package/dist/dashboard/assets/providers/devin.svg +9 -0
- package/dist/dashboard/assets/providers/devin.svg.gz +0 -0
- package/dist/dashboard/assets/providers/windsurf.svg +10 -0
- package/dist/dashboard/assets/providers/windsurf.svg.gz +0 -0
- package/dist/dashboard/assets/{query-keys-YSoQOUh6.js → query-keys-IKLh0YFd.js} +1 -1
- package/dist/dashboard/assets/query-keys-IKLh0YFd.js.gz +0 -0
- package/dist/dashboard/assets/routing-Ckm1UXuC.js +1 -0
- package/dist/dashboard/assets/routing-Ckm1UXuC.js.gz +0 -0
- package/dist/dashboard/assets/{select-C9Ssle9b.js → select-ChoFWyKz.js} +1 -1
- package/dist/dashboard/assets/select-ChoFWyKz.js.gz +0 -0
- package/dist/dashboard/assets/settings-CKjFPlKW.js +1 -0
- package/dist/dashboard/assets/settings-CKjFPlKW.js.gz +0 -0
- package/dist/dashboard/assets/{stack-j7cXjgYX.js → stack-DWCwU3fh.js} +1 -1
- package/dist/dashboard/assets/stack-DWCwU3fh.js.gz +0 -0
- package/dist/dashboard/assets/{state-DHmEnLa2.js → state-Cg3VL5J_.js} +1 -1
- package/dist/dashboard/assets/state-Cg3VL5J_.js.gz +0 -0
- package/dist/dashboard/assets/story/25817331.webp +0 -0
- package/dist/dashboard/assets/story/Shorekeeper.webp +0 -0
- package/dist/dashboard/assets/story/cartethyia-god.webp +0 -0
- package/dist/dashboard/assets/story/fleurdelys_plus.webp +0 -0
- package/dist/dashboard/assets/story/jinhsi-blossom.webp +0 -0
- package/dist/dashboard/assets/story/phrolova-a.webp +0 -0
- package/dist/dashboard/assets/story/requestdeniawokkjpg.webp +0 -0
- package/dist/dashboard/assets/story/wuthering-waves-hiyuki-aemeath-hiyuki-rover.webp +0 -0
- package/dist/dashboard/assets/{switch-DmZcyTIC.js → switch-Db70BMix.js} +1 -1
- package/dist/dashboard/assets/switch-Db70BMix.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-CbCP4w1o.js → useMutation-BqdE0LuM.js} +1 -1
- package/dist/dashboard/assets/useMutation-BqdE0LuM.js.gz +0 -0
- package/dist/dashboard/console-backgrounds.webp +0 -0
- package/dist/dashboard/console-backgrounds_initial.webp +0 -0
- package/dist/dashboard/donation/qris-risun-marke.webp +0 -0
- package/dist/dashboard/index.html +3 -3
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +57 -55
- package/migrations/full-postgres/0044_add_models_warning_metadata.sql +10 -0
- package/migrations/full-postgres/0045_migrate_workbuddy_to_codebuddy.sql +156 -0
- package/migrations/lite-sqlite3/0044_add_models_warning_metadata.sql +11 -0
- package/migrations/lite-sqlite3/0045_migrate_workbuddy_to_codebuddy.sql +156 -0
- package/package.json +1 -1
- package/src/boot/dependencies.ts +11 -3
- package/src/config/cooldowns.ts +1 -1
- package/src/config/spec.ts +0 -6
- package/src/console/api-keys/routes.ts +11 -4
- package/src/console/console-router.ts +7 -0
- package/src/console/dashboard-assets.ts +5 -5
- package/src/console/domain-registration.ts +27 -0
- package/src/console/ip-bans/routes.ts +99 -0
- package/src/console/providers/catalog/catalog-projections.ts +5 -2
- package/src/console/providers/catalog/contracts.ts +123 -0
- package/src/console/providers/catalog/model-operations.ts +122 -10
- package/src/console/providers/catalog/provider-operations.ts +116 -8
- package/src/console/providers/catalog/public-model-store.ts +0 -0
- package/src/console/providers/catalog/routes.ts +64 -4
- package/src/console/providers/catalog/store-helpers.ts +30 -1
- package/src/console/providers/catalog/store.ts +243 -3
- package/src/console/quota/account-quota-global.ts +89 -1
- package/src/console/quota/native-identity.ts +2 -0
- package/src/console/settings/contracts.ts +18 -0
- package/src/console/settings/store.ts +32 -0
- package/src/console/system-prompt/contracts.ts +0 -1
- package/src/main.ts +1 -0
- package/src/observability/event-query.ts +18 -4
- package/src/persistence/dialect.ts +12 -0
- package/src/persistence/schema.ts +20 -2
- package/src/protocol/dsml.ts +1 -1
- package/src/protocol/gateway-error.ts +1 -0
- package/src/protocol/reasoning.ts +1 -1
- package/src/protocol/request/chat.ts +4 -4
- package/src/protocol/request/messages.ts +1 -1
- package/src/protocol/response/gemini.ts +6 -1
- package/src/protocol/sse-decoder.ts +25 -8
- package/src/protocol/stream-error-frames.ts +13 -3
- package/src/protocol/translation/quirks.ts +1 -1
- package/src/protocol/translation/tool-repair.ts +2 -2
- package/src/protocol/upstream/send-claude-messages.ts +15 -4
- package/src/protocol/upstream-parsing.ts +1 -1
- package/src/providers/adapters/compatible-adapter.ts +7 -5
- package/src/providers/authentication/oauth-refresh-service.ts +149 -36
- package/src/providers/default-registry.ts +68 -3
- package/src/providers/discovery/discovery-types.ts +12 -0
- package/src/providers/discovery/probe-phases.ts +61 -30
- package/src/providers/discovery/probing-service.ts +279 -164
- package/src/providers/integrations/antigravity/antigravity-protocol.ts +117 -2
- package/src/providers/integrations/antigravity/antigravity.ts +13 -2
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +72 -34
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +18 -18
- package/src/providers/integrations/buddy/buddy-checkin.ts +17 -39
- package/src/providers/integrations/buddy/buddy-conversation-headers.ts +2 -2
- package/src/providers/integrations/buddy/buddy-discovery-shared.ts +5 -5
- package/src/providers/integrations/buddy/buddy-identity.ts +70 -0
- package/src/providers/integrations/buddy/buddy-media.ts +4 -5
- package/src/providers/integrations/buddy/buddy-oauth-shared.ts +2 -2
- package/src/providers/integrations/buddy/buddy-quota-shared.ts +1 -1
- package/src/providers/integrations/buddy/buddy-usage-shared.ts +1 -3
- package/src/providers/integrations/buddy/capabilities.ts +4 -41
- package/src/providers/integrations/buddy/codebuddy/cn-catalog.ts +2 -1
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +20 -1
- package/src/providers/integrations/buddy/codebuddy/provider.ts +2 -2
- package/src/providers/integrations/buddy/codebuddy/shared.ts +6 -10
- package/src/providers/integrations/cursor/cursor-catalog.ts +2 -0
- package/src/providers/integrations/cursor/cursor-exec.ts +738 -0
- package/src/providers/integrations/cursor/cursor-http2.ts +2 -0
- package/src/providers/integrations/cursor/cursor-request.ts +9 -8
- package/src/providers/integrations/cursor/cursor-shared.ts +19 -1
- package/src/providers/integrations/cursor/cursor.ts +50 -30
- package/src/providers/integrations/cursor/protocol/tool-canonical.ts +26 -100
- package/src/providers/integrations/devin/devin-auth.ts +353 -0
- package/src/providers/integrations/devin/devin-catalog.ts +207 -0
- package/src/providers/integrations/devin/devin-http.ts +91 -0
- package/src/providers/integrations/devin/devin-quota.ts +214 -0
- package/src/providers/integrations/devin/devin-shared.ts +111 -0
- package/src/providers/integrations/devin/devin.ts +539 -0
- package/src/providers/integrations/devin/protocol/devin-proto.ts +584 -0
- package/src/providers/integrations/devin/protocol/framing.ts +124 -0
- package/src/providers/integrations/factory/factory.ts +2 -2
- package/src/providers/integrations/freepi/freepi-catalog.ts +60 -0
- package/src/providers/integrations/freepi/freepi-discovery.ts +82 -0
- package/src/providers/integrations/freepi/freepi-headers.ts +21 -0
- package/src/providers/integrations/freepi/freepi-oauth.ts +142 -0
- package/src/providers/integrations/freepi/freepi-protocol.ts +242 -0
- package/src/providers/integrations/freepi/freepi-quota.ts +126 -0
- package/src/providers/integrations/freepi/freepi-realm.ts +35 -0
- package/src/providers/integrations/freepi/freepi.ts +292 -0
- package/src/providers/integrations/gemini.ts +2 -2
- package/src/providers/integrations/hermes/hermes-oauth.ts +94 -0
- package/src/providers/integrations/hermes.ts +4 -12
- package/src/providers/integrations/opencode/fingerprint.ts +1 -1
- package/src/providers/integrations/perch/perch-catalog.ts +105 -0
- package/src/providers/integrations/perch/perch-oauth.ts +170 -0
- package/src/providers/integrations/perch/perch-protocol.ts +211 -0
- package/src/providers/integrations/perch/perch-quota.ts +137 -0
- package/src/providers/integrations/perch/perch-realm.ts +82 -0
- package/src/providers/integrations/perch/perch-system-prompt.ts +13 -0
- package/src/providers/integrations/perch/perch.ts +315 -0
- package/src/providers/integrations/perch/saffron-system-prompt.txt +8 -0
- package/src/providers/integrations/qoder/chat-proto.ts +328 -328
- package/src/providers/integrations/qoder/claim-qoder-credits.ts +1 -1
- package/src/providers/integrations/qoder/qoder-adapter.ts +89 -28
- package/src/providers/integrations/qoder/qoder-cache.ts +32 -0
- package/src/providers/integrations/qoder/qoder-catalog.ts +6 -1
- package/src/providers/integrations/qoder/qoder-checkin.ts +2 -2
- package/src/providers/integrations/qoder/qoder-grpc.ts +209 -5
- package/src/providers/integrations/qoder/qoder-ide.ts +5 -1
- package/src/providers/integrations/qoder/qoder-identity-cache.ts +184 -0
- package/src/providers/integrations/qoder/qoder-native-identity-store.ts +83 -18
- package/src/providers/integrations/qoder/qoder-realm.ts +9 -6
- package/src/providers/integrations/qoder/qoder-umid.ts +109 -342
- package/src/providers/integrations/qoder/qoder-virtual.ts +33 -1
- package/src/providers/integrations/qoder/qoder.ts +3 -6
- package/src/providers/integrations/trae/trae-catalog.ts +9 -14
- package/src/providers/integrations/trae/trae-headers.ts +36 -11
- package/src/providers/integrations/trae/trae-oauth.ts +14 -17
- package/src/providers/integrations/trae/trae-protocol.ts +3 -6
- package/src/providers/integrations/trae/trae-quota.ts +103 -21
- package/src/providers/integrations/trae/trae-realm.ts +36 -15
- package/src/providers/integrations/trae/trae.ts +97 -7
- package/src/providers/operations/account-error-classification.ts +66 -1
- package/src/providers/operations/account-health-service.ts +192 -26
- package/src/providers/operations/account-refresh-status.ts +284 -0
- package/src/providers/operations/account-tier.ts +67 -0
- package/src/providers/operations/byok-wire-profile.ts +9 -12
- package/src/providers/operations/client-versions.ts +37 -99
- package/src/providers/operations/cooldown-rules.ts +61 -0
- package/src/providers/operations/provider-catalog-seeder.ts +4 -0
- package/src/providers/provider-contracts.ts +29 -0
- package/src/providers/provider-metadata.ts +59 -4
- package/src/providers/provider-model-definition.ts +36 -0
- package/src/providers/quota/refresh.ts +21 -0
- package/src/security/abuse.ts +71 -0
- package/src/transport/dispatch/proxy-request.ts +1 -0
- package/src/transport/dispatch/streaming/stream.ts +1 -0
- package/src/transport/dispatch/upstream.ts +4 -1
- package/src/transport/failure-policy.ts +113 -7
- package/src/transport/routing/router.ts +25 -1
- package/src/transport/surface/messages/adapter.ts +2 -1
- package/src/transport/surface/responses/encode.ts +12 -1
- package/src/workers/daily-checkin.ts +1 -1
- package/src/workers/quota-refresh-sweep.ts +1 -1
- package/src/workers/scheduler.ts +22 -4
- package/dist/dashboard/assets/BannedPage-B05aF8Dg.js +0 -1
- package/dist/dashboard/assets/BannedPage-B05aF8Dg.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-CLUhmjUy.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-CQmrqqrk.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-CHG_1Wr3.js +0 -1
- package/dist/dashboard/assets/CombosPage-CHG_1Wr3.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-DPgbklad.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-DPgbklad.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-pnfAqr_-.js +0 -1
- package/dist/dashboard/assets/CustomizationPage-pnfAqr_-.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-DGJuU2A6.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-DGJuU2A6.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-C-e2AMz6.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-J8wPbmfT.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-C6WlpU5E.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-BSQnF2Lp.js +0 -1
- package/dist/dashboard/assets/OverviewPage-BSQnF2Lp.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-DK_e0FJz.js +0 -7
- package/dist/dashboard/assets/ProviderDetailPage-DK_e0FJz.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js +0 -1
- package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-D_a3r6Gc.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-D_a3r6Gc.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-BBTRBAQ3.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-B2WqMSG1.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-D7xp5Ij_.js +0 -1
- package/dist/dashboard/assets/SettingsPage-D7xp5Ij_.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-LND6_Ss7.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-BzNnm6Bi.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-C1TbL24N.js +0 -56
- package/dist/dashboard/assets/UsagePage-C1TbL24N.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-KvX83mRe.js +0 -1
- package/dist/dashboard/assets/api-keys-KvX83mRe.js.gz +0 -0
- package/dist/dashboard/assets/app-mweRcsKp.css +0 -2
- package/dist/dashboard/assets/app-mweRcsKp.css.gz +0 -0
- package/dist/dashboard/assets/assert-CidQVoNo.js +0 -1
- package/dist/dashboard/assets/assert-CidQVoNo.js.gz +0 -0
- package/dist/dashboard/assets/badge-BaX2GTrn.js.gz +0 -0
- package/dist/dashboard/assets/banner-DFH4Bkte.js.gz +0 -0
- package/dist/dashboard/assets/brand/bansos_page.png +0 -0
- package/dist/dashboard/assets/brand/denia_bansos.jpg +0 -0
- package/dist/dashboard/assets/brand/favicon.jpg +0 -0
- package/dist/dashboard/assets/brand/login_register.jpg +0 -0
- package/dist/dashboard/assets/brand/login_register_initial.jpg +0 -0
- package/dist/dashboard/assets/brand/og_bansos.jpg +0 -0
- package/dist/dashboard/assets/brand/og_image.jpg +0 -0
- package/dist/dashboard/assets/brand/sidebar_ico.jpg +0 -0
- package/dist/dashboard/assets/card-CxDw0lK6.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js +0 -1
- package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-DfS1ntiq.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BK-NRr5u.js.gz +0 -0
- package/dist/dashboard/assets/drawer-QqPul6sC.js.gz +0 -0
- package/dist/dashboard/assets/entry-BMlYJVFj.js.gz +0 -0
- package/dist/dashboard/assets/entry-Dmx7gBiN.js +0 -1
- package/dist/dashboard/assets/entry-Dmx7gBiN.js.gz +0 -0
- package/dist/dashboard/assets/entry-vqGb8yVz.js +0 -4
- package/dist/dashboard/assets/entry-vqGb8yVz.js.gz +0 -0
- package/dist/dashboard/assets/formatters-DP-5E01-.js.gz +0 -0
- package/dist/dashboard/assets/index-D36vCZht.js.gz +0 -0
- package/dist/dashboard/assets/inline-CHJQwxLa.js.gz +0 -0
- package/dist/dashboard/assets/input-BlmlizVV.js.gz +0 -0
- package/dist/dashboard/assets/layout-Bz93I-yE.js.gz +0 -0
- package/dist/dashboard/assets/list-row-Ml16KCfu.js.gz +0 -0
- package/dist/dashboard/assets/live-7_QpnYGu.js.gz +0 -0
- package/dist/dashboard/assets/minimize-2-BTmMTK4o.js +0 -1
- package/dist/dashboard/assets/minimize-2-BTmMTK4o.js.gz +0 -0
- package/dist/dashboard/assets/mutation-GU7XdEjz.js.gz +0 -0
- package/dist/dashboard/assets/network-CuMDaTzT.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CoqpaDMx.js.gz +0 -0
- package/dist/dashboard/assets/pagination-B5aOoweZ.js.gz +0 -0
- package/dist/dashboard/assets/query-keys-YSoQOUh6.js.gz +0 -0
- package/dist/dashboard/assets/routing-fnCsxgNO.js +0 -1
- package/dist/dashboard/assets/routing-fnCsxgNO.js.gz +0 -0
- package/dist/dashboard/assets/select-C9Ssle9b.js.gz +0 -0
- package/dist/dashboard/assets/settings-BxslsErn.js +0 -1
- package/dist/dashboard/assets/settings-BxslsErn.js.gz +0 -0
- package/dist/dashboard/assets/stack-j7cXjgYX.js.gz +0 -0
- package/dist/dashboard/assets/state-DHmEnLa2.js.gz +0 -0
- package/dist/dashboard/assets/switch-DmZcyTIC.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-CbCP4w1o.js.gz +0 -0
- package/dist/dashboard/console-backgrounds.jpg +0 -0
- package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
- package/dist/dashboard/donation/qris-risun-marke.png +0 -0
- package/src/providers/integrations/buddy/turing-device-token.ts +0 -262
- package/src/providers/integrations/buddy/workbuddy/cn-oauth.ts +0 -30
- package/src/providers/integrations/buddy/workbuddy/cn-provider.ts +0 -58
- package/src/providers/integrations/buddy/workbuddy/oauth.ts +0 -31
- package/src/providers/integrations/buddy/workbuddy/provider.ts +0 -209
- package/src/providers/integrations/buddy/workbuddy/quota.ts +0 -161
- package/src/providers/integrations/buddy/workbuddy/shared.ts +0 -285
- /package/dist/dashboard/assets/{app-DRnQLhsc.js → app-B7kIWFm5.js} +0 -0
- /package/dist/dashboard/assets/{app-DRnQLhsc.js.gz → app-B7kIWFm5.js.gz} +0 -0
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manual account-status observation: refresh an OAuth token on demand and
|
|
3
|
+
* report the account's status AFTER the attempt — never assume it.
|
|
4
|
+
*
|
|
5
|
+
* Area 04 (D21–D26): a refresh is not a health verdict. This module is the
|
|
6
|
+
* explicit "refresh token" affordance for accounts the OAuth sweep excludes
|
|
7
|
+
* (`disabled`), and it is where the invariant nobody else had to is held:
|
|
8
|
+
* `statusAfter` is always re-read from the database, so no caller can ever
|
|
9
|
+
* report `"active"` just because a refresh succeeded.
|
|
10
|
+
*/
|
|
11
|
+
import { randomUUID } from "node:crypto";
|
|
12
|
+
import { eq } from "drizzle-orm";
|
|
13
|
+
import type { CartethyiaDatabase } from "../../persistence/db-handle";
|
|
14
|
+
import { healthEvents, providerAccounts, providerOauthStates } from "../../persistence/schema";
|
|
15
|
+
import { pushStructuredConsoleLog } from "../../observability/log-ring";
|
|
16
|
+
import type { OAuthRefreshService, OAuthTokenRefresher } from "../authentication/oauth-refresh-service";
|
|
17
|
+
|
|
18
|
+
export type RefreshAccountStatusCode =
|
|
19
|
+
| "ok"
|
|
20
|
+
| "account_missing"
|
|
21
|
+
| "not_oauth"
|
|
22
|
+
| "static_token"
|
|
23
|
+
| "no_refresher"
|
|
24
|
+
| "refresh_failed";
|
|
25
|
+
|
|
26
|
+
export interface RefreshAccountStatusOutcome {
|
|
27
|
+
readonly ok: boolean;
|
|
28
|
+
readonly tokenRefreshed: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Account status re-read from the DB after the attempt. Never assumed from
|
|
31
|
+
* the input row and never `"active"` by fiat — a refresh cannot re-enable.
|
|
32
|
+
*/
|
|
33
|
+
readonly statusAfter: string;
|
|
34
|
+
readonly code: RefreshAccountStatusCode;
|
|
35
|
+
/** IS0 timestamp of the fresh access-token expiry, when known. */
|
|
36
|
+
readonly expiresAt?: string;
|
|
37
|
+
/** ISO timestamp of the attempt, when it happened. */
|
|
38
|
+
readonly refreshedAt?: string;
|
|
39
|
+
/** Structured reason when `ok` is false (`not_oauth`, `static_token`, …). */
|
|
40
|
+
readonly error?: string;
|
|
41
|
+
/** Human detail (failure message) when available. */
|
|
42
|
+
readonly category?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface RefreshAccountStatusDeps {
|
|
46
|
+
readonly db: CartethyiaDatabase;
|
|
47
|
+
readonly refreshService: Pick<OAuthRefreshService, "ensureFreshAccessToken">;
|
|
48
|
+
readonly resolveRefresher: (providerId: string) => Promise<OAuthTokenRefresher | undefined>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface LoadedAccount {
|
|
52
|
+
readonly id: string;
|
|
53
|
+
readonly providerId: string;
|
|
54
|
+
readonly credentialKind: string;
|
|
55
|
+
readonly status: string;
|
|
56
|
+
readonly staticToken: boolean;
|
|
57
|
+
readonly refreshCiphertext: Buffer | null;
|
|
58
|
+
readonly expiresAt: Date | null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function loadAccount(
|
|
62
|
+
db: CartethyiaDatabase,
|
|
63
|
+
accountId: string,
|
|
64
|
+
): Promise<LoadedAccount | undefined> {
|
|
65
|
+
const selectBuilder = db
|
|
66
|
+
.select({
|
|
67
|
+
id: providerAccounts.id,
|
|
68
|
+
providerId: providerAccounts.providerId,
|
|
69
|
+
credentialKind: providerAccounts.credentialKind,
|
|
70
|
+
status: providerAccounts.status,
|
|
71
|
+
staticToken: providerAccounts.staticToken,
|
|
72
|
+
refreshCiphertext: providerOauthStates.refreshCiphertext,
|
|
73
|
+
expiresAt: providerOauthStates.expiresAt,
|
|
74
|
+
})
|
|
75
|
+
.from(providerAccounts);
|
|
76
|
+
const query =
|
|
77
|
+
"leftJoin" in selectBuilder
|
|
78
|
+
? selectBuilder.leftJoin(
|
|
79
|
+
providerOauthStates,
|
|
80
|
+
eq(providerOauthStates.providerAccountId, providerAccounts.id),
|
|
81
|
+
)
|
|
82
|
+
: selectBuilder;
|
|
83
|
+
const rows = await query.where(eq(providerAccounts.id, accountId)).limit(1);
|
|
84
|
+
return rows[0];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function readStatusAfter(
|
|
88
|
+
db: CartethyiaDatabase,
|
|
89
|
+
accountId: string,
|
|
90
|
+
): Promise<{ status: string; expiresAt: Date | null } | undefined> {
|
|
91
|
+
const selectBuilder = db
|
|
92
|
+
.select({
|
|
93
|
+
status: providerAccounts.status,
|
|
94
|
+
expiresAt: providerOauthStates.expiresAt,
|
|
95
|
+
})
|
|
96
|
+
.from(providerAccounts);
|
|
97
|
+
const query =
|
|
98
|
+
"leftJoin" in selectBuilder
|
|
99
|
+
? selectBuilder.leftJoin(
|
|
100
|
+
providerOauthStates,
|
|
101
|
+
eq(providerOauthStates.providerAccountId, providerAccounts.id),
|
|
102
|
+
)
|
|
103
|
+
: selectBuilder;
|
|
104
|
+
const rows = await query.where(eq(providerAccounts.id, accountId)).limit(1);
|
|
105
|
+
return rows[0];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Non-mutating health event: records the attempt, never changes status. */
|
|
109
|
+
async function writeRefreshHealthEvent(
|
|
110
|
+
db: CartethyiaDatabase,
|
|
111
|
+
accountId: string,
|
|
112
|
+
fromStatus: string,
|
|
113
|
+
toStatus: string,
|
|
114
|
+
reason: string,
|
|
115
|
+
): Promise<void> {
|
|
116
|
+
if (typeof db.insert !== "function") return;
|
|
117
|
+
await db.insert(healthEvents).values({
|
|
118
|
+
id: randomUUID(),
|
|
119
|
+
entityKind: "account",
|
|
120
|
+
accountId,
|
|
121
|
+
fromStatus: fromStatus as "active" | "cooldown" | "disabled" | null,
|
|
122
|
+
toStatus: toStatus as "active" | "cooldown" | "disabled",
|
|
123
|
+
reason,
|
|
124
|
+
errorCategory: null,
|
|
125
|
+
createdAt: new Date(),
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Refreshes an OAuth account's token on demand and reports the resulting
|
|
131
|
+
* status. Works for any status, `disabled` included — the account stays
|
|
132
|
+
* `disabled`; only an operator can re-enable it.
|
|
133
|
+
*
|
|
134
|
+
* Rejections are structured (`{ ok: false, code: "not_oauth" | "static_token" |
|
|
135
|
+
* ... }`), and every outcome carries `statusAfter` re-read from the DB.
|
|
136
|
+
*/
|
|
137
|
+
export async function refreshAccountStatus(
|
|
138
|
+
deps: RefreshAccountStatusDeps,
|
|
139
|
+
accountId: string,
|
|
140
|
+
opts: { readonly origin?: "ui" | "sweep" | "dispatch" } = {},
|
|
141
|
+
): Promise<RefreshAccountStatusOutcome> {
|
|
142
|
+
const origin = opts.origin ?? "ui";
|
|
143
|
+
const account = await loadAccount(deps.db, accountId);
|
|
144
|
+
if (!account) {
|
|
145
|
+
return {
|
|
146
|
+
ok: false,
|
|
147
|
+
tokenRefreshed: false,
|
|
148
|
+
statusAfter: "disabled",
|
|
149
|
+
code: "account_missing",
|
|
150
|
+
error: "Account not found",
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
const statusBefore = account.status;
|
|
154
|
+
|
|
155
|
+
if (account.credentialKind !== "oauth") {
|
|
156
|
+
return {
|
|
157
|
+
ok: false,
|
|
158
|
+
tokenRefreshed: false,
|
|
159
|
+
statusAfter: statusBefore,
|
|
160
|
+
code: "not_oauth",
|
|
161
|
+
error: "not_oauth",
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
if (account.staticToken || account.refreshCiphertext == null) {
|
|
165
|
+
// No refresh grant (or an explicit static marker): there is nothing to
|
|
166
|
+
// re-mint, and forcing the service to try would mutate the row into a
|
|
167
|
+
// static token. Refuse instead of changing the account.
|
|
168
|
+
return {
|
|
169
|
+
ok: false,
|
|
170
|
+
tokenRefreshed: false,
|
|
171
|
+
statusAfter: statusBefore,
|
|
172
|
+
code: "static_token",
|
|
173
|
+
error: "static_token",
|
|
174
|
+
...(account.refreshCiphertext == null && !account.staticToken
|
|
175
|
+
? { category: "No refresh token on record" }
|
|
176
|
+
: {}),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const refresher = await deps.resolveRefresher(account.providerId);
|
|
181
|
+
if (!refresher) {
|
|
182
|
+
return {
|
|
183
|
+
ok: false,
|
|
184
|
+
tokenRefreshed: false,
|
|
185
|
+
statusAfter: statusBefore,
|
|
186
|
+
code: "no_refresher",
|
|
187
|
+
error: "no_refresher",
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// `ensureFreshAccessToken` deliberately swallows a failing refresher and
|
|
192
|
+
// returns null (it must not break a dispatch). For this explicit affordance
|
|
193
|
+
// the operator needs the reason, so keep the last failure message here
|
|
194
|
+
// rather than generalizing it — same "message as-is" rule as area 08.
|
|
195
|
+
let refreshFailureMessage: string | undefined;
|
|
196
|
+
const reportingRefresher: OAuthTokenRefresher = {
|
|
197
|
+
refresh: async (...args) => {
|
|
198
|
+
try {
|
|
199
|
+
return await refresher.refresh(...args);
|
|
200
|
+
} catch (error) {
|
|
201
|
+
refreshFailureMessage = error instanceof Error ? error.message : String(error);
|
|
202
|
+
throw error;
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const startedAt = new Date();
|
|
208
|
+
try {
|
|
209
|
+
const refreshed = await deps.refreshService.ensureFreshAccessToken(accountId, reportingRefresher, {
|
|
210
|
+
force: true,
|
|
211
|
+
origin,
|
|
212
|
+
});
|
|
213
|
+
// D25: status is always re-read after the attempt — never assumed.
|
|
214
|
+
const after = await readStatusAfter(deps.db, accountId);
|
|
215
|
+
const statusAfter = after?.status ?? statusBefore;
|
|
216
|
+
if (refreshed !== null) {
|
|
217
|
+
pushStructuredConsoleLog("info", "OAuth token refreshed", {
|
|
218
|
+
event: "token_refresh",
|
|
219
|
+
accountId,
|
|
220
|
+
providerId: account.providerId,
|
|
221
|
+
details: {
|
|
222
|
+
origin,
|
|
223
|
+
statusBefore,
|
|
224
|
+
statusAfter,
|
|
225
|
+
expiresAt: after?.expiresAt?.toISOString() ?? "",
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
await writeRefreshHealthEvent(
|
|
229
|
+
deps.db,
|
|
230
|
+
accountId,
|
|
231
|
+
statusBefore,
|
|
232
|
+
statusAfter,
|
|
233
|
+
"Token refreshed — status unchanged",
|
|
234
|
+
);
|
|
235
|
+
return {
|
|
236
|
+
ok: true,
|
|
237
|
+
tokenRefreshed: true,
|
|
238
|
+
statusAfter,
|
|
239
|
+
code: "ok",
|
|
240
|
+
...(after?.expiresAt ? { expiresAt: after.expiresAt.toISOString() } : {}),
|
|
241
|
+
refreshedAt: startedAt.toISOString(),
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
// `ensureFreshAccessToken` returned null: the refresh failed (transient
|
|
245
|
+
// upstream error, lease loss) or the credential became static mid-flight.
|
|
246
|
+
// Either way the account's own status is the truth, re-read above.
|
|
247
|
+
pushStructuredConsoleLog("warn", "OAuth token refresh did not complete", {
|
|
248
|
+
event: "token_refresh",
|
|
249
|
+
accountId,
|
|
250
|
+
providerId: account.providerId,
|
|
251
|
+
errorCode: "refresh_failed",
|
|
252
|
+
details: { origin, statusBefore, statusAfter },
|
|
253
|
+
});
|
|
254
|
+
return {
|
|
255
|
+
ok: false,
|
|
256
|
+
tokenRefreshed: false,
|
|
257
|
+
statusAfter,
|
|
258
|
+
code: "refresh_failed",
|
|
259
|
+
error: "refresh_failed",
|
|
260
|
+
...(refreshFailureMessage
|
|
261
|
+
? { category: refreshFailureMessage.slice(0, 500) }
|
|
262
|
+
: { category: "Token refresh did not complete" }),
|
|
263
|
+
};
|
|
264
|
+
} catch (error) {
|
|
265
|
+
const after = await readStatusAfter(deps.db, accountId);
|
|
266
|
+
const statusAfter = after?.status ?? statusBefore;
|
|
267
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
268
|
+
pushStructuredConsoleLog("error", "OAuth token refresh failed", {
|
|
269
|
+
event: "token_refresh",
|
|
270
|
+
accountId,
|
|
271
|
+
providerId: account.providerId,
|
|
272
|
+
errorCode: "refresh_failed",
|
|
273
|
+
details: { origin, statusBefore, statusAfter, error: message.slice(0, 500) },
|
|
274
|
+
});
|
|
275
|
+
return {
|
|
276
|
+
ok: false,
|
|
277
|
+
tokenRefreshed: false,
|
|
278
|
+
statusAfter,
|
|
279
|
+
code: "refresh_failed",
|
|
280
|
+
error: "refresh_failed",
|
|
281
|
+
category: message.slice(0, 500),
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Account tier derivation from the plan string the quota collector reported.
|
|
3
|
+
*
|
|
4
|
+
* The plan string (`provider_accounts.quota_plan`) is a raw, per-provider
|
|
5
|
+
* label; this module normalizes it to the three-tier taxonomy the cooldown
|
|
6
|
+
* rules consume (`free | paid | pro | null`, D14). It is a pure function of
|
|
7
|
+
* the provider id and the plan text — no database access, no state.
|
|
8
|
+
*
|
|
9
|
+
* The mapping is deliberately conservative, following the same posture as
|
|
10
|
+
* `plan-gate.ts`: a tier is only claimed when the string proves it. "free"
|
|
11
|
+
* requires a free marker in the string, "pro" requires a team/enterprise/org
|
|
12
|
+
* signal, and "paid" is only assigned for providers whose plan strings are
|
|
13
|
+
* evidenced (Trae, the buddy family, Cursor). Everything else — an unknown
|
|
14
|
+
* provider, an unrecognized plan, a `null` plan — resolves to `null`, which
|
|
15
|
+
* keeps the legacy behavior intact (unknown tier ⇒ no rule applies ⇒ the old
|
|
16
|
+
* constants drive the cooldown).
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** The three-tier taxonomy for cooldown rules. `null` = unknown. */
|
|
20
|
+
export type AccountTier = "free" | "paid" | "pro" | null;
|
|
21
|
+
|
|
22
|
+
const TRAE_PROVIDER_IDS: ReadonlySet<string> = new Set(["trae", "traecn"]);
|
|
23
|
+
const BUDDY_PROVIDER_IDS: ReadonlySet<string> = new Set([
|
|
24
|
+
"cb",
|
|
25
|
+
"cbcn",
|
|
26
|
+
]);
|
|
27
|
+
const QODER_PROVIDER_IDS: ReadonlySet<string> = new Set(["qoder", "qodercn"]);
|
|
28
|
+
|
|
29
|
+
/** Team/enterprise/org signals that prove a "pro" tier. */
|
|
30
|
+
const PRO_PLAN_MARKERS: readonly string[] = ["team", "enterprise", "org", "pro"];
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Maps a provider's plan string to a tier.
|
|
34
|
+
*
|
|
35
|
+
* `providerId` is normalized case-insensitively; a `null`/empty plan is
|
|
36
|
+
* unknown (`null`). The "free" check runs before any provider branch because a
|
|
37
|
+
* proven-free label is the strongest signal (e.g. CodeBuddy's
|
|
38
|
+
* `personal_free_user`, Trae's `"Free Plan Subscription"`).
|
|
39
|
+
*/
|
|
40
|
+
export function mapPlanToTier(providerId: string, plan: string | null): AccountTier {
|
|
41
|
+
const lower = (plan ?? "").trim().toLowerCase();
|
|
42
|
+
if (lower.length === 0) return null;
|
|
43
|
+
// Free is only claimed when the string proves it — the same marker set the
|
|
44
|
+
// routing plan-gate uses, so the two cannot disagree about an account.
|
|
45
|
+
if (lower.includes("free")) return "free";
|
|
46
|
+
|
|
47
|
+
if (QODER_PROVIDER_IDS.has(providerId.toLowerCase())) {
|
|
48
|
+
// Qoder's `userType` is `personal_standard` by default; team/enterprise/
|
|
49
|
+
// org suffixes prove a higher tier. `personal_standard` itself is not
|
|
50
|
+
// verified against the taxonomy, so it stays unknown rather than guessed.
|
|
51
|
+
if (PRO_PLAN_MARKERS.some((marker) => lower.includes(marker))) return "pro";
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (BUDDY_PROVIDER_IDS.has(providerId.toLowerCase())) {
|
|
56
|
+
if (lower.includes("enterprise") || lower.includes("team")) return "pro";
|
|
57
|
+
return "paid";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Trae (paid plans other than the free subscription) and Cursor (a non-free
|
|
61
|
+
// `membershipType`/`planName` is a paying plan by construction).
|
|
62
|
+
if (TRAE_PROVIDER_IDS.has(providerId.toLowerCase())) return "paid";
|
|
63
|
+
if (providerId.toLowerCase() === "cursor") return "paid";
|
|
64
|
+
|
|
65
|
+
// Unknown provider: no claim. Legacy behavior applies.
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
@@ -17,9 +17,6 @@ import type { WireFamily } from "../../protocol/canonical-model";
|
|
|
17
17
|
import type { CompatibilityProfile } from "../provider-metadata";
|
|
18
18
|
import { BARE_ROOT_ENDPOINTS, type AuthHeaderShape } from "../../protocol/codecs";
|
|
19
19
|
|
|
20
|
-
/** The two wires an OpenAI-compatible root conventionally serves. */
|
|
21
|
-
const OPENAI_WIRE_FAMILIES: readonly WireFamily[] = ["chat", "responses"];
|
|
22
|
-
|
|
23
20
|
export interface ByokWireProfile {
|
|
24
21
|
/** Header shape the upstream expects for the operator's credential. */
|
|
25
22
|
readonly authHeaderShape: AuthHeaderShape;
|
|
@@ -47,10 +44,14 @@ export function byokAuthHeaderShape(families: readonly WireFamily[]): AuthHeader
|
|
|
47
44
|
*
|
|
48
45
|
* Operator truth wins: an explicit `endpoint_paths_by_wire_family` names
|
|
49
46
|
* exactly the families the provider serves. Without one, the provider serves
|
|
50
|
-
* the family it was created with
|
|
51
|
-
* `responses`
|
|
52
|
-
*
|
|
53
|
-
*
|
|
47
|
+
* exactly the family it was created with — a `chat` default serves only chat,
|
|
48
|
+
* a `responses` default only responses, a `messages` default only messages.
|
|
49
|
+
*
|
|
50
|
+
* A `chat` default used to additionally serve `responses`, and discovery then
|
|
51
|
+
* reclassified gpt-5/gpt-6 ids as responses even when the operator picked
|
|
52
|
+
* "Chat completions" — the model ended up on `/v1/responses` (which the
|
|
53
|
+
* upstream may not serve) and the probe timed out. The operator's selection
|
|
54
|
+
* is the contract; nothing is inferred beyond it.
|
|
54
55
|
*/
|
|
55
56
|
export function resolveByokWireProfile(
|
|
56
57
|
wireFamilyDefault: WireFamily | null | undefined,
|
|
@@ -62,11 +63,7 @@ export function resolveByokWireProfile(
|
|
|
62
63
|
: [];
|
|
63
64
|
const defaultFamily: WireFamily = wireFamilyDefault ?? "chat";
|
|
64
65
|
const supportedWireFamilies: readonly WireFamily[] =
|
|
65
|
-
declaredFamilies.length > 0
|
|
66
|
-
? declaredFamilies
|
|
67
|
-
: defaultFamily === "chat"
|
|
68
|
-
? OPENAI_WIRE_FAMILIES
|
|
69
|
-
: [defaultFamily];
|
|
66
|
+
declaredFamilies.length > 0 ? declaredFamilies : [defaultFamily];
|
|
70
67
|
const endpointPathsByWireFamily: Partial<Record<WireFamily, string>> = {};
|
|
71
68
|
for (const family of supportedWireFamilies) {
|
|
72
69
|
const path = declaredPaths?.[family] ?? BARE_ROOT_ENDPOINTS[family];
|
|
@@ -77,7 +77,7 @@ async function clineClientVersion(response: Response): Promise<string | null> {
|
|
|
77
77
|
* spells it.
|
|
78
78
|
*
|
|
79
79
|
* Cursor's `x-cursor-client-version` is not the IDE semver on its own: the wire
|
|
80
|
-
* value is `cli-<version>-<sha7>` (observed live as `cli-2026.
|
|
80
|
+
* value is `cli-<version>-<sha7>` (observed live as `cli-2026.11.05-86047df`).
|
|
81
81
|
* The download API is the only source that returns both halves — `version` and
|
|
82
82
|
* `commitSha` — so they are joined here rather than guessed. A semver alone
|
|
83
83
|
* would be a different identity than the one the IDE sends.
|
|
@@ -109,18 +109,6 @@ async function kimiVersion(response: Response): Promise<string | null> {
|
|
|
109
109
|
return isSemverish(data.info?.version) ? data.info.version.trim() : null;
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
async function workbuddyDesktopVersion(response: Response): Promise<string | null> {
|
|
113
|
-
try {
|
|
114
|
-
const data = (await response.json()) as { version?: unknown; productVersion?: unknown };
|
|
115
|
-
const candidate = data.productVersion ?? data.version;
|
|
116
|
-
return typeof candidate === "string" && /^\d+\.\d+\.\d+(?:\.\d+)?(?:[-+][\w.-]+)?$/.test(candidate.trim())
|
|
117
|
-
? candidate.trim()
|
|
118
|
-
: null;
|
|
119
|
-
} catch {
|
|
120
|
-
return null;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
112
|
async function kiroCaskVersion(response: Response): Promise<string | null> {
|
|
125
113
|
try {
|
|
126
114
|
const data = (await response.json()) as { version?: unknown };
|
|
@@ -232,7 +220,7 @@ const KIRO_NODE_VERSION = "24.18.0";
|
|
|
232
220
|
export const VERSION_SOURCES = {
|
|
233
221
|
antigravity: {
|
|
234
222
|
key: "antigravity",
|
|
235
|
-
fallback: "2.
|
|
223
|
+
fallback: "2.29.0",
|
|
236
224
|
sources: [
|
|
237
225
|
{
|
|
238
226
|
url: "https://antigravity-hub-auto-updater-974169037036.us-central1.run.app/manifest/latest-arm64-mac.yml",
|
|
@@ -261,21 +249,21 @@ export const VERSION_SOURCES = {
|
|
|
261
249
|
// under `@opencode/cli`, and this feed is what its own installer reads.
|
|
262
250
|
// Fallback = the current latest release; raise it whenever the package
|
|
263
251
|
// moves so a cold process does not present a stale client build.
|
|
264
|
-
fallback: "2.2.
|
|
252
|
+
fallback: "2.2.6",
|
|
265
253
|
minVersion: "2.0.0",
|
|
266
254
|
sources: [{ url: "https://opencode.ai/update/api/latest/cli/npm" }],
|
|
267
255
|
},
|
|
268
256
|
commandcode: {
|
|
269
257
|
key: "commandcode",
|
|
270
|
-
// npm `command-code` latest as of 2026-
|
|
258
|
+
// npm `command-code` latest as of 2026-11-07. The CLI sends this as
|
|
271
259
|
// `x-command-code-version`, so a stale fallback presents a client build
|
|
272
260
|
// the upstream no longer ships — raise it whenever the package moves.
|
|
273
|
-
fallback: "1.88.
|
|
261
|
+
fallback: "1.88.5",
|
|
274
262
|
sources: [{ url: "https://registry.npmjs.org/command-code/latest" }],
|
|
275
263
|
},
|
|
276
264
|
grok: {
|
|
277
265
|
key: "grok",
|
|
278
|
-
fallback: "1.0.
|
|
266
|
+
fallback: "1.0.77",
|
|
279
267
|
sources: [
|
|
280
268
|
{ url: "https://storage.googleapis.com/grok-build-public-artifacts/cli/stable" },
|
|
281
269
|
{ url: "https://registry.npmjs.org/@xai-official/grok/latest" },
|
|
@@ -304,45 +292,9 @@ export const VERSION_SOURCES = {
|
|
|
304
292
|
},
|
|
305
293
|
codex: {
|
|
306
294
|
key: "codex",
|
|
307
|
-
fallback: "0.
|
|
295
|
+
fallback: "0.167.0",
|
|
308
296
|
sources: [{ url: "https://registry.npmjs.org/@openai/codex/latest" }],
|
|
309
297
|
},
|
|
310
|
-
workbuddyClient: {
|
|
311
|
-
key: "workbuddy-client",
|
|
312
|
-
fallback: "5.9.4.42593613",
|
|
313
|
-
sources: [
|
|
314
|
-
{
|
|
315
|
-
url: "https://www.workbuddy.ai/v2/update?platform=workbuddy-win32-x64-user",
|
|
316
|
-
extract: workbuddyDesktopVersion,
|
|
317
|
-
},
|
|
318
|
-
{
|
|
319
|
-
url: "https://www.workbuddy.ai/v2/update?platform=workbuddy-darwin-arm64",
|
|
320
|
-
extract: workbuddyDesktopVersion,
|
|
321
|
-
},
|
|
322
|
-
],
|
|
323
|
-
},
|
|
324
|
-
workbuddyCnClient: {
|
|
325
|
-
key: "workbuddy-cn-client",
|
|
326
|
-
fallback: "5.9.3.42398134",
|
|
327
|
-
sources: [
|
|
328
|
-
{
|
|
329
|
-
url: "https://www.workbuddy.cn/v2/update?platform=workbuddy-win32-x64-user",
|
|
330
|
-
extract: workbuddyDesktopVersion,
|
|
331
|
-
},
|
|
332
|
-
{
|
|
333
|
-
url: "https://www.workbuddy.cn/v2/update?platform=workbuddy-darwin-arm64",
|
|
334
|
-
extract: workbuddyDesktopVersion,
|
|
335
|
-
},
|
|
336
|
-
],
|
|
337
|
-
},
|
|
338
|
-
workbuddyCli: {
|
|
339
|
-
key: "workbuddy",
|
|
340
|
-
fallback: "2.178.1",
|
|
341
|
-
sources: [
|
|
342
|
-
{ url: "https://registry.npmjs.org/@tencent-ai/codebuddy-code/latest" },
|
|
343
|
-
{ url: "https://registry.npmmirror.com/@tencent-ai/codebuddy-code/latest" },
|
|
344
|
-
],
|
|
345
|
-
},
|
|
346
298
|
kimiCli: {
|
|
347
299
|
key: "kimi-cli",
|
|
348
300
|
fallback: "1.52.0",
|
|
@@ -350,7 +302,7 @@ export const VERSION_SOURCES = {
|
|
|
350
302
|
},
|
|
351
303
|
kiro: {
|
|
352
304
|
key: "kiro",
|
|
353
|
-
fallback: "1.4.
|
|
305
|
+
fallback: "1.4.121",
|
|
354
306
|
sources: [
|
|
355
307
|
{
|
|
356
308
|
// Kiro's downloads page exposes no stable version string. Homebrew
|
|
@@ -364,7 +316,7 @@ export const VERSION_SOURCES = {
|
|
|
364
316
|
},
|
|
365
317
|
codebuddy: {
|
|
366
318
|
key: "codebuddy",
|
|
367
|
-
fallback: "2.
|
|
319
|
+
fallback: "2.181.0",
|
|
368
320
|
sources: [
|
|
369
321
|
{ url: "https://registry.npmjs.org/@tencent-ai/codebuddy-code/latest" },
|
|
370
322
|
{ url: "https://registry.npmmirror.com/@tencent-ai/codebuddy-code/latest" },
|
|
@@ -372,7 +324,7 @@ export const VERSION_SOURCES = {
|
|
|
372
324
|
},
|
|
373
325
|
codebuddyCn: {
|
|
374
326
|
key: "codebuddy-cn",
|
|
375
|
-
fallback: "4.12.
|
|
327
|
+
fallback: "4.12.3.42658816",
|
|
376
328
|
sources: [
|
|
377
329
|
{
|
|
378
330
|
// Official CN builds are listed by Homebrew's maintained cask feed;
|
|
@@ -387,7 +339,7 @@ export const VERSION_SOURCES = {
|
|
|
387
339
|
// Current `@anthropic-ai/claude-code` release, so the billing
|
|
388
340
|
// `cc_version=` suffix and the `claude-cli/` User-Agent stay on what
|
|
389
341
|
// upstream ships.
|
|
390
|
-
fallback: "2.1.
|
|
342
|
+
fallback: "2.1.319",
|
|
391
343
|
sources: [{ url: "https://registry.npmjs.org/@anthropic-ai/claude-code/latest" }],
|
|
392
344
|
},
|
|
393
345
|
claudeSdk: {
|
|
@@ -404,7 +356,7 @@ export const VERSION_SOURCES = {
|
|
|
404
356
|
},
|
|
405
357
|
github: {
|
|
406
358
|
key: "github-copilot-chat",
|
|
407
|
-
fallback: "0.43.
|
|
359
|
+
fallback: "0.43.2026040705",
|
|
408
360
|
sources: [
|
|
409
361
|
{
|
|
410
362
|
url: "https://api.github.com/repos/microsoft/vscode-copilot-chat/releases/latest",
|
|
@@ -414,7 +366,7 @@ export const VERSION_SOURCES = {
|
|
|
414
366
|
},
|
|
415
367
|
kilo: {
|
|
416
368
|
key: "kilo-code",
|
|
417
|
-
fallback: "7.8.
|
|
369
|
+
fallback: "7.8.27",
|
|
418
370
|
sources: [{ url: "https://open-vsx.org/api/kilocode/kilo-code" }],
|
|
419
371
|
},
|
|
420
372
|
muse: {
|
|
@@ -436,12 +388,27 @@ export const VERSION_SOURCES = {
|
|
|
436
388
|
},
|
|
437
389
|
],
|
|
438
390
|
},
|
|
391
|
+
freepi: {
|
|
392
|
+
key: "freepi",
|
|
393
|
+
// The free-pi CLI version — the `x-client-version` the gateway reports.
|
|
394
|
+
// Fallback = the unpacked 0.6.14 bundle; npm discovery replaces it.
|
|
395
|
+
fallback: "0.6.14",
|
|
396
|
+
sources: [{ url: "https://registry.npmjs.org/free-pi-cli/latest" }],
|
|
397
|
+
},
|
|
398
|
+
perch: {
|
|
399
|
+
key: "perch",
|
|
400
|
+
// The official Perch CLI, spawned by the adapter for hosted inference.
|
|
401
|
+
fallback: "2.4.125",
|
|
402
|
+
sources: [{ url: "https://registry.npmjs.org/perchai-cli/latest" }],
|
|
403
|
+
},
|
|
439
404
|
cursor: {
|
|
440
405
|
key: "cursor",
|
|
441
|
-
// Fallback = the last observed wire version
|
|
442
|
-
// one monitor tick; it is only what a cold
|
|
443
|
-
// successful fetch.
|
|
444
|
-
|
|
406
|
+
// Fallback = the last observed wire version (CLI ~2026.11.07-306d153).
|
|
407
|
+
// Discovery replaces it within one monitor tick; it is only what a cold
|
|
408
|
+
// process sends before the first successful fetch. Kept equal to
|
|
409
|
+
// `FALLBACK_CLIENT_VERSION` in the cursor adapter so the badge and the
|
|
410
|
+
// request header always send one identity.
|
|
411
|
+
fallback: "cli-3.29.9-306d153",
|
|
445
412
|
sources: [
|
|
446
413
|
{
|
|
447
414
|
url: "https://cursor.com/api/download?platform=win32-x64-user&releaseTrack=stable",
|
|
@@ -455,7 +422,7 @@ export const VERSION_SOURCES = {
|
|
|
455
422
|
// comment this replaced claimed no semver existed and pinned a date tag
|
|
456
423
|
// (`v2026.9.24`) as the version — a tag, not a release, and not the
|
|
457
424
|
// identity the client sends.
|
|
458
|
-
fallback: "0.
|
|
425
|
+
fallback: "0.23.0",
|
|
459
426
|
sources: [
|
|
460
427
|
{
|
|
461
428
|
url: "https://api.github.com/repos/NousResearch/hermes-agent/releases/latest",
|
|
@@ -465,7 +432,7 @@ export const VERSION_SOURCES = {
|
|
|
465
432
|
},
|
|
466
433
|
zcode: {
|
|
467
434
|
key: "zcode",
|
|
468
|
-
fallback: "3.
|
|
435
|
+
fallback: "3.17.2",
|
|
469
436
|
sources: [
|
|
470
437
|
{
|
|
471
438
|
url: "https://api.github.com/repos/zai-org/ZCode/releases/latest",
|
|
@@ -480,6 +447,8 @@ const resolvers = {
|
|
|
480
447
|
qoder: createClientVersionResolver({ ...VERSION_SOURCES.qoder, client: "qoder" }),
|
|
481
448
|
hermes: createClientVersionResolver({ ...VERSION_SOURCES.hermes, client: "hermes" }),
|
|
482
449
|
trae: createClientVersionResolver({ ...VERSION_SOURCES.trae, client: "trae" }),
|
|
450
|
+
freepi: createClientVersionResolver({ ...VERSION_SOURCES.freepi, client: "freepi" }),
|
|
451
|
+
perch: createClientVersionResolver({ ...VERSION_SOURCES.perch, client: "perch" }),
|
|
483
452
|
cursor: createClientVersionResolver({ ...VERSION_SOURCES.cursor, client: "cursor" }),
|
|
484
453
|
opencode: createClientVersionResolver({ ...VERSION_SOURCES.opencode, client: "opencode" }),
|
|
485
454
|
commandcode: createClientVersionResolver({ ...VERSION_SOURCES.commandcode, client: "command-code" }),
|
|
@@ -488,9 +457,6 @@ const resolvers = {
|
|
|
488
457
|
clineSdk: createClientVersionResolver({ ...VERSION_SOURCES.clineSdk, client: "cline-sdk" }),
|
|
489
458
|
clineCli: createClientVersionResolver({ ...VERSION_SOURCES.clineCli, client: "cline-cli" }),
|
|
490
459
|
codex: createClientVersionResolver({ ...VERSION_SOURCES.codex, client: "codex" }),
|
|
491
|
-
workbuddyClient: createClientVersionResolver({ ...VERSION_SOURCES.workbuddyClient, client: "workbuddy" }),
|
|
492
|
-
workbuddyCnClient: createClientVersionResolver({ ...VERSION_SOURCES.workbuddyCnClient, client: "workbuddy-cn" }),
|
|
493
|
-
workbuddyCli: createClientVersionResolver({ ...VERSION_SOURCES.workbuddyCli, client: "codebuddy" }),
|
|
494
460
|
kimiCli: createClientVersionResolver({ ...VERSION_SOURCES.kimiCli, client: "kimi" }),
|
|
495
461
|
codebuddy: createClientVersionResolver({ ...VERSION_SOURCES.codebuddy, client: "codebuddy" }),
|
|
496
462
|
codebuddyCn: createClientVersionResolver({ ...VERSION_SOURCES.codebuddyCn, client: "codebuddy" }),
|
|
@@ -520,8 +486,6 @@ const PROVIDER_VERSION_RESOLVERS: Readonly<Record<string, ClientVersionResolver>
|
|
|
520
486
|
traecn: resolvers.trae,
|
|
521
487
|
cursor: resolvers.cursor,
|
|
522
488
|
commandcode: resolvers.commandcode,
|
|
523
|
-
workbuddy: resolvers.workbuddyClient,
|
|
524
|
-
workbuddycn: resolvers.workbuddyCnClient,
|
|
525
489
|
cb: resolvers.codebuddy,
|
|
526
490
|
cbcn: resolvers.codebuddyCn,
|
|
527
491
|
kimi: resolvers.kimiCli,
|
|
@@ -641,37 +605,11 @@ export const _resetClineCliVersionCache = accessor("clineCli").reset;
|
|
|
641
605
|
|
|
642
606
|
export const getCodexVersion = accessor("codex").get;
|
|
643
607
|
|
|
644
|
-
export const getWorkBuddyClientVersion = accessor("workbuddyClient").get;
|
|
645
|
-
export const getWorkBuddyCnClientVersion = accessor("workbuddyCnClient").get;
|
|
646
|
-
export const getWorkBuddyCliVersion = accessor("workbuddyCli").get;
|
|
647
|
-
|
|
648
|
-
export function buildWorkBuddyUserAgent(
|
|
649
|
-
identity = "WB",
|
|
650
|
-
clientVersion = getWorkBuddyClientVersion(),
|
|
651
|
-
cliVersion = getWorkBuddyCliVersion(),
|
|
652
|
-
): string {
|
|
653
|
-
// 1:1 with codebuddy2api. The CLI profile is `CLI/<v> CodeBuddy/<v>` and the
|
|
654
|
-
// WorkBuddy profile is `WorkBuddy/<wb> workbuddy-ai/<wb> CLI/<cli>`, which
|
|
655
|
-
// sends NO SDK headers — two separate profiles, not one string with a switch.
|
|
656
|
-
// Whether SDK headers ride along is decided by `workbuddyHeaders`, not here, so
|
|
657
|
-
// this builder only decides the UA text.
|
|
658
|
-
//
|
|
659
|
-
// The second token is the wire spelling `workbuddy-ai`, NOT the display
|
|
660
|
-
// brand `WorkBuddy AI`. This is what an official client actually sends; the
|
|
661
|
-
// product is branded with a space in the UI and in docs, and copying that
|
|
662
|
-
// branding into the header produced a UA no real client emits — which the
|
|
663
|
-
// intl gateway answers with 403 `11140`, the same refusal it gives a
|
|
664
|
-
// wrong-brand header. Product name ≠ wire token.
|
|
665
|
-
if (identity === "CLI") return `CLI/${cliVersion} CodeBuddy/${cliVersion}`;
|
|
666
|
-
return `WorkBuddy/${clientVersion} workbuddy-ai/${clientVersion} CLI/${cliVersion}`;
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
export const _resetWorkBuddyCnClientVersionCache = accessor("workbuddyCnClient").reset;
|
|
670
|
-
|
|
671
608
|
export const getKimiCliVersion = accessor("kimiCli").get;
|
|
672
609
|
|
|
673
610
|
export const getCodeBuddyVersion = accessor("codebuddy").get;
|
|
674
611
|
export const getCodeBuddyCnVersion = accessor("codebuddyCn").get;
|
|
612
|
+
|
|
675
613
|
/**
|
|
676
614
|
* The CodeBuddy User-Agent.
|
|
677
615
|
*
|