cartethyia 2.7.8 → 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-BRWKPQPf.js → CliToolDetailPage-ixwgKk53.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-ixwgKk53.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-BP-Y5iHN.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-Cv-2Ol_K.js → InjectSystemPromptPanel-BOoO8rpz.js} +2 -2
- package/dist/dashboard/assets/InjectSystemPromptPanel-BOoO8rpz.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-bKfz_b1K.js → LoginPage-ChJuNNVN.js} +1 -1
- package/dist/dashboard/assets/LoginPage-ChJuNNVN.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-B-WQxx_H.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-emlTuxz6.js → ProxyPage-BJ87Pp0U.js} +1 -1
- package/dist/dashboard/assets/ProxyPage-BJ87Pp0U.js.gz +0 -0
- package/dist/dashboard/assets/{QuotaPage-CMJKNYWb.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-CUZrKs9r.js → SetupPage-CFNcATJJ.js} +1 -1
- package/dist/dashboard/assets/SetupPage-CFNcATJJ.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-jWmUdDpy.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-DyI_mJfi.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-B-yXtCqb.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-_HwAEqFF.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-ByjCk0o9.js → clipboard-button-CJ4Zty89.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-CJ4Zty89.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-BZhOlfz-.js → dialog-B5L2jPQ_.js} +1 -1
- package/dist/dashboard/assets/dialog-B5L2jPQ_.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-BADqIRYP.js → drawer-DkjUouAj.js} +1 -1
- package/dist/dashboard/assets/drawer-DkjUouAj.js.gz +0 -0
- package/dist/dashboard/assets/{entry-COaC-irN.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-DcHJyRiu.js → formatters-CrsdLVsn.js} +1 -1
- package/dist/dashboard/assets/formatters-CrsdLVsn.js.gz +0 -0
- package/dist/dashboard/assets/{index-CgKmFngv.js → index-BU64Lqvp.js} +2 -2
- package/dist/dashboard/assets/index-BU64Lqvp.js.gz +0 -0
- package/dist/dashboard/assets/{inline-B2BirKry.js → inline-B2aDLyyg.js} +1 -1
- package/dist/dashboard/assets/inline-B2aDLyyg.js.gz +0 -0
- package/dist/dashboard/assets/{input-D-qV9dKY.js → input-BCVkdR9d.js} +1 -1
- package/dist/dashboard/assets/input-BCVkdR9d.js.gz +0 -0
- package/dist/dashboard/assets/{layout-B7tVWhtV.js → layout-DNcAdMK-.js} +1 -1
- package/dist/dashboard/assets/layout-DNcAdMK-.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-CtkGE3bF.js → list-row-DgrIH0hw.js} +1 -1
- package/dist/dashboard/assets/list-row-DgrIH0hw.js.gz +0 -0
- package/dist/dashboard/assets/{live-DGizF06-.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-C9UE-bOr.js → network-CRib7H_2.js} +1 -1
- package/dist/dashboard/assets/network-CRib7H_2.js.gz +0 -0
- package/dist/dashboard/assets/{numbers-BnAd22a_.js → numbers-BKQE8Xpz.js} +1 -1
- package/dist/dashboard/assets/numbers-BKQE8Xpz.js.gz +0 -0
- package/dist/dashboard/assets/{pagination-BAywyMzS.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-BCOcv8WK.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-ByKt3Q1g.js → stack-DWCwU3fh.js} +1 -1
- package/dist/dashboard/assets/stack-DWCwU3fh.js.gz +0 -0
- package/dist/dashboard/assets/{state-DXbGTx7N.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-ZeTsW623.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 +210 -6
- 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/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-DeIbIiBU.js +0 -1
- package/dist/dashboard/assets/BannedPage-DeIbIiBU.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-BRWKPQPf.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-BP-Y5iHN.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-B7JHozVa.js +0 -1
- package/dist/dashboard/assets/CombosPage-B7JHozVa.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-GX7iGGcu.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-GX7iGGcu.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-HVonbGEa.js +0 -1
- package/dist/dashboard/assets/CustomizationPage-HVonbGEa.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-xaut4OJ9.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-xaut4OJ9.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-Cv-2Ol_K.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-bKfz_b1K.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-B-WQxx_H.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-Z9pJZFo8.js +0 -1
- package/dist/dashboard/assets/OverviewPage-Z9pJZFo8.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-CMeDBxR1.js +0 -7
- package/dist/dashboard/assets/ProviderDetailPage-CMeDBxR1.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-BXPdOpG4.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-BXPdOpG4.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-emlTuxz6.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-CMJKNYWb.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-BSy7EwGo.js +0 -1
- package/dist/dashboard/assets/SettingsPage-BSy7EwGo.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-CUZrKs9r.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-jWmUdDpy.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-5F9tGVGG.js +0 -56
- package/dist/dashboard/assets/UsagePage-5F9tGVGG.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-BEsU8hKs.css +0 -2
- package/dist/dashboard/assets/app-BEsU8hKs.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-DyI_mJfi.js.gz +0 -0
- package/dist/dashboard/assets/banner-B-yXtCqb.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-_HwAEqFF.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-ByjCk0o9.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BZhOlfz-.js.gz +0 -0
- package/dist/dashboard/assets/drawer-BADqIRYP.js.gz +0 -0
- package/dist/dashboard/assets/entry-COaC-irN.js.gz +0 -0
- package/dist/dashboard/assets/entry-Ca7LSCet.js +0 -4
- package/dist/dashboard/assets/entry-Ca7LSCet.js.gz +0 -0
- package/dist/dashboard/assets/entry-PDd27uCd.js +0 -1
- package/dist/dashboard/assets/entry-PDd27uCd.js.gz +0 -0
- package/dist/dashboard/assets/formatters-DcHJyRiu.js.gz +0 -0
- package/dist/dashboard/assets/index-CgKmFngv.js.gz +0 -0
- package/dist/dashboard/assets/inline-B2BirKry.js.gz +0 -0
- package/dist/dashboard/assets/input-D-qV9dKY.js.gz +0 -0
- package/dist/dashboard/assets/layout-B7tVWhtV.js.gz +0 -0
- package/dist/dashboard/assets/list-row-CtkGE3bF.js.gz +0 -0
- package/dist/dashboard/assets/live-DGizF06-.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-C9UE-bOr.js.gz +0 -0
- package/dist/dashboard/assets/numbers-BnAd22a_.js.gz +0 -0
- package/dist/dashboard/assets/pagination-BAywyMzS.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-BCOcv8WK.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-ByKt3Q1g.js.gz +0 -0
- package/dist/dashboard/assets/state-DXbGTx7N.js.gz +0 -0
- package/dist/dashboard/assets/switch-ZeTsW623.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-CTxoqyYB.js → app-B7kIWFm5.js} +0 -0
- /package/dist/dashboard/assets/{app-CTxoqyYB.js.gz → app-B7kIWFm5.js.gz} +0 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Devin authentication: OAuth login flows and credential import.
|
|
3
|
+
*
|
|
4
|
+
* Three paths are supported:
|
|
5
|
+
*
|
|
6
|
+
* 1. **Devin CLI** (`app.devin.ai`): PKCE S256 browser-code flow. Authorize
|
|
7
|
+
* at `https://app.devin.ai/auth/cli/continue`, exchange at
|
|
8
|
+
* `https://api.devin.ai/auth/cli/token` (body JSON, non-standard).
|
|
9
|
+
* 2. **Windsurf Enterprise** (`windsurf.com`): PKCE S256 loopback flow on
|
|
10
|
+
* port 59654, exchanging via `ExchangePKCEAuthorizationCode` to a raw
|
|
11
|
+
* seat key (`sk-ws-…`, stripped from the `gsk-ws-…` wire form).
|
|
12
|
+
* 3. **CLI adopt**: reads the official Devin CLI `credentials.toml`
|
|
13
|
+
* (`windsurf_api_key`) — no network, no prompts.
|
|
14
|
+
*
|
|
15
|
+
* All three store under the `devin` credential slot (`store-as "devin"`).
|
|
16
|
+
*
|
|
17
|
+
* Ported from oh-my-pi (`registry/oauth/devin-windsurf.ts` +
|
|
18
|
+
* `devin-cli.ts`, MIT) and opencode-devin-provider (`oauth.ts`, MIT).
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
22
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
23
|
+
import { join } from "node:path";
|
|
24
|
+
import {
|
|
25
|
+
base64url,
|
|
26
|
+
decodeJwt,
|
|
27
|
+
} from "jose";
|
|
28
|
+
import type {
|
|
29
|
+
OAuthAuthorizeRequest,
|
|
30
|
+
OAuthCodeExchangeContext,
|
|
31
|
+
OAuthExchangeResult,
|
|
32
|
+
OAuthImportInput,
|
|
33
|
+
OAuthLoginClient,
|
|
34
|
+
} from "../../authentication/oauth-flow-store";
|
|
35
|
+
import { callDevinUnary } from "./devin-http";
|
|
36
|
+
import { DevinMessageTypes } from "./protocol/devin-proto";
|
|
37
|
+
import {
|
|
38
|
+
DEVIN_DEFAULT_BASE_URL,
|
|
39
|
+
DEVIN_EXCHANGE_PATH,
|
|
40
|
+
DEVIN_GET_USER_STATUS_PATH,
|
|
41
|
+
} from "./devin-shared";
|
|
42
|
+
|
|
43
|
+
// ── PKCE ──
|
|
44
|
+
|
|
45
|
+
export function generatePKCE(): { verifier: string; challenge: string } {
|
|
46
|
+
const verifier = base64url.encode(randomBytes(96));
|
|
47
|
+
const challenge = base64url.encode(createHash("sha256").update(verifier).digest());
|
|
48
|
+
return { verifier, challenge };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ── Identity resolution ──
|
|
52
|
+
|
|
53
|
+
/** JWT claim keys tried in order when deriving an email/identity label. */
|
|
54
|
+
const IDENTITY_CLAIM_KEYS = [
|
|
55
|
+
"email",
|
|
56
|
+
"preferred_username",
|
|
57
|
+
"name",
|
|
58
|
+
"sub",
|
|
59
|
+
"user_id",
|
|
60
|
+
"userId",
|
|
61
|
+
"session_id",
|
|
62
|
+
] as const;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Derives a display label from a session token without any network: decode
|
|
66
|
+
* the JWT payload and read the first present identity claim. A token that is
|
|
67
|
+
* not a JWT (raw `sk-ws-…` seat key) yields nothing.
|
|
68
|
+
*/
|
|
69
|
+
export function identityLabelFromDevinCredential(credential: string): string | undefined {
|
|
70
|
+
const key = credential.trim();
|
|
71
|
+
if (!key) return undefined;
|
|
72
|
+
const jwt = key.startsWith("devin-session-token$")
|
|
73
|
+
? key.slice("devin-session-token$".length)
|
|
74
|
+
: key;
|
|
75
|
+
if (jwt.split(".").length !== 3) return undefined;
|
|
76
|
+
let payload: Record<string, unknown>;
|
|
77
|
+
try {
|
|
78
|
+
payload = decodeJwt(jwt) as Record<string, unknown>;
|
|
79
|
+
} catch {
|
|
80
|
+
// The token is opaque — no claims to read. Callers fall back to the
|
|
81
|
+
// network identity below.
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
for (const claim of IDENTITY_CLAIM_KEYS) {
|
|
85
|
+
const value = payload[claim];
|
|
86
|
+
if (typeof value === "string" && value.length > 0 && value !== "unauthenticated") {
|
|
87
|
+
return value;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** A single identity string from `GetUserStatus`, if the upstream reports one. */
|
|
94
|
+
export function identityFromDevinUserStatus(planInfo: unknown): string | undefined {
|
|
95
|
+
if (typeof planInfo !== "object" || planInfo === null) return undefined;
|
|
96
|
+
const info = planInfo as {
|
|
97
|
+
userStatus?: { email?: string; teamId?: string };
|
|
98
|
+
planInfo?: { devinInfo?: { accountDisplayName?: string; orgId?: string } };
|
|
99
|
+
};
|
|
100
|
+
const email = info.userStatus?.email?.trim();
|
|
101
|
+
if (email) return email;
|
|
102
|
+
const displayName = info.planInfo?.devinInfo?.accountDisplayName?.trim();
|
|
103
|
+
if (displayName) return displayName;
|
|
104
|
+
const orgId = info.planInfo?.devinInfo?.orgId?.trim();
|
|
105
|
+
if (orgId) return orgId;
|
|
106
|
+
const teamId = info.userStatus?.teamId?.trim();
|
|
107
|
+
if (teamId) return teamId;
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Resolves the human-friendly label for a Devin credential: JWT claims first
|
|
113
|
+
* (free), then `GetUserStatus` (network). Never throws — a failed lookup
|
|
114
|
+
* returns `undefined`, leaving the caller's default label in place.
|
|
115
|
+
*/
|
|
116
|
+
export async function resolveDevinAccountLabel(
|
|
117
|
+
credential: string,
|
|
118
|
+
fetcher: typeof fetch = fetch,
|
|
119
|
+
): Promise<string | undefined> {
|
|
120
|
+
const jwtLabel = identityLabelFromDevinCredential(credential);
|
|
121
|
+
if (jwtLabel) return jwtLabel;
|
|
122
|
+
try {
|
|
123
|
+
const response = await callDevinUnary<Record<string, unknown>>(
|
|
124
|
+
DEVIN_GET_USER_STATUS_PATH,
|
|
125
|
+
DevinMessageTypes.GetUserStatusRequest,
|
|
126
|
+
DevinMessageTypes.GetUserStatusResponse,
|
|
127
|
+
{ metadata: { apiKey: credential } },
|
|
128
|
+
{ baseUrl: DEVIN_DEFAULT_BASE_URL, fetchImpl: fetcher, signal: AbortSignal.timeout(10_000) },
|
|
129
|
+
);
|
|
130
|
+
return identityFromDevinUserStatus(response);
|
|
131
|
+
} catch {
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ── Devin CLI flow (app.devin.ai) ──
|
|
137
|
+
|
|
138
|
+
const DEVIN_AUTHORIZE_URL = "https://app.devin.ai/auth/cli/continue";
|
|
139
|
+
const DEVIN_TOKEN_URL = "https://api.devin.ai/auth/cli/token";
|
|
140
|
+
const DEVIN_BROWSER_REDIRECT_URI = "http://127.0.0.1:59653/callback";
|
|
141
|
+
|
|
142
|
+
export const devinOAuthClient: OAuthLoginClient = {
|
|
143
|
+
supportsDeviceCode: false,
|
|
144
|
+
supportsBrowserCode: true,
|
|
145
|
+
browserRedirectUri: DEVIN_BROWSER_REDIRECT_URI,
|
|
146
|
+
|
|
147
|
+
buildAuthorizeUrl(request: OAuthAuthorizeRequest): string {
|
|
148
|
+
const params = new URLSearchParams({
|
|
149
|
+
redirect_uri: DEVIN_BROWSER_REDIRECT_URI,
|
|
150
|
+
state: request.state,
|
|
151
|
+
prompt: "select_account",
|
|
152
|
+
code_challenge: request.codeChallenge,
|
|
153
|
+
code_challenge_method: "S256",
|
|
154
|
+
});
|
|
155
|
+
return `${DEVIN_AUTHORIZE_URL}?${params.toString()}`;
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
async exchangeCode(
|
|
159
|
+
code: string,
|
|
160
|
+
codeVerifier: string,
|
|
161
|
+
_redirectUri: string,
|
|
162
|
+
_state?: string,
|
|
163
|
+
_context?: OAuthCodeExchangeContext,
|
|
164
|
+
): Promise<OAuthExchangeResult> {
|
|
165
|
+
const response = await fetch(DEVIN_TOKEN_URL, {
|
|
166
|
+
method: "POST",
|
|
167
|
+
headers: { "content-type": "application/json", accept: "application/json" },
|
|
168
|
+
body: JSON.stringify({ code, code_verifier: codeVerifier }),
|
|
169
|
+
});
|
|
170
|
+
if (!response.ok) {
|
|
171
|
+
const text = await response.text().catch(() => "");
|
|
172
|
+
throw new Error(`Devin token exchange failed: HTTP ${response.status} ${text}`);
|
|
173
|
+
}
|
|
174
|
+
const body = (await response.json()) as { token?: string };
|
|
175
|
+
const token = body.token;
|
|
176
|
+
if (!token) throw new Error("Devin token exchange returned no token");
|
|
177
|
+
|
|
178
|
+
// Parse JWT exp, fallback to 1 year.
|
|
179
|
+
let expiresAt: Date;
|
|
180
|
+
try {
|
|
181
|
+
const claims = decodeJwt(token);
|
|
182
|
+
expiresAt = claims.exp ? new Date(claims.exp * 1000) : new Date(Date.now() + 365 * 24 * 3600 * 1000);
|
|
183
|
+
} catch {
|
|
184
|
+
expiresAt = new Date(Date.now() + 365 * 24 * 3600 * 1000);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// The exchange only returns a token; the label comes from the token's
|
|
188
|
+
// own claims (when present) or `GetUserStatus`. Never fail the login on
|
|
189
|
+
// a label lookup.
|
|
190
|
+
const accountLabel = await resolveDevinAccountLabel(token).catch(() => undefined);
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
access: token,
|
|
194
|
+
refresh: "",
|
|
195
|
+
expiresAt,
|
|
196
|
+
...(accountLabel ? { accountLabel } : {}),
|
|
197
|
+
};
|
|
198
|
+
},
|
|
199
|
+
|
|
200
|
+
async importCredential(input: OAuthImportInput): Promise<OAuthExchangeResult> {
|
|
201
|
+
const credential = input.credential.trim();
|
|
202
|
+
if (!credential) throw new Error("Devin credential is empty");
|
|
203
|
+
// The key itself is the identity; resolve a label when the material can
|
|
204
|
+
// provide one (JWT claims or `GetUserStatus`), never failing on it.
|
|
205
|
+
const accountLabel = await resolveDevinAccountLabel(credential).catch(() => undefined);
|
|
206
|
+
return {
|
|
207
|
+
access: credential,
|
|
208
|
+
refresh: "",
|
|
209
|
+
expiresAt: new Date(8640000000000000),
|
|
210
|
+
...(accountLabel ? { accountLabel } : {}),
|
|
211
|
+
...(input.accountLabel ? { accountLabel: input.accountLabel } : {}),
|
|
212
|
+
};
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
// ── Windsurf Enterprise flow (windsurf.com/editor/signin) ──
|
|
217
|
+
|
|
218
|
+
const WINDSURF_AUTHORIZE_URL = "https://windsurf.com/editor/signin";
|
|
219
|
+
|
|
220
|
+
/** Loopback port the official Devin CLI uses for the Windsurf flow. */
|
|
221
|
+
export const WINDSURF_CALLBACK_PORT = 59654;
|
|
222
|
+
/** Loopback hostname for the Windsurf flow. */
|
|
223
|
+
export const WINDSURF_PREFERRED_HOSTNAME = "127.0.0.1";
|
|
224
|
+
|
|
225
|
+
export const devinWindsurfOAuthClient: OAuthLoginClient = {
|
|
226
|
+
supportsDeviceCode: false,
|
|
227
|
+
supportsBrowserCode: true,
|
|
228
|
+
|
|
229
|
+
buildAuthorizeUrl(request: OAuthAuthorizeRequest): string {
|
|
230
|
+
const params = new URLSearchParams({
|
|
231
|
+
response_type: "code",
|
|
232
|
+
redirect_uri: request.redirectUri,
|
|
233
|
+
state: request.state,
|
|
234
|
+
code_challenge: request.codeChallenge,
|
|
235
|
+
code_challenge_method: "S256",
|
|
236
|
+
prompt: "login",
|
|
237
|
+
redirect_parameters_type: "query",
|
|
238
|
+
});
|
|
239
|
+
return `${WINDSURF_AUTHORIZE_URL}?${params.toString()}`;
|
|
240
|
+
},
|
|
241
|
+
|
|
242
|
+
async exchangeCode(
|
|
243
|
+
code: string,
|
|
244
|
+
codeVerifier: string,
|
|
245
|
+
_redirectUri: string,
|
|
246
|
+
_state?: string,
|
|
247
|
+
_context?: OAuthCodeExchangeContext,
|
|
248
|
+
): Promise<OAuthExchangeResult> {
|
|
249
|
+
const response = await callDevinUnary<{
|
|
250
|
+
apiKey?: string;
|
|
251
|
+
name?: string;
|
|
252
|
+
apiServerUrl?: string;
|
|
253
|
+
devinWebappHost?: string;
|
|
254
|
+
devinApiUrl?: string;
|
|
255
|
+
}>(
|
|
256
|
+
DEVIN_EXCHANGE_PATH,
|
|
257
|
+
DevinMessageTypes.ExchangePKCEAuthorizationCodeRequest,
|
|
258
|
+
DevinMessageTypes.ExchangePKCEAuthorizationCodeResponse,
|
|
259
|
+
{ authorizationCode: code, codeVerifier },
|
|
260
|
+
{ baseUrl: DEVIN_DEFAULT_BASE_URL },
|
|
261
|
+
);
|
|
262
|
+
const apiKey = response.apiKey;
|
|
263
|
+
if (!apiKey) throw new Error("Windsurf code exchange returned no API key");
|
|
264
|
+
// The exchange returns the `g`-prefixed wire form (`gsk-ws-…`); RPCs
|
|
265
|
+
// authenticate with the raw key.
|
|
266
|
+
const key = apiKey.startsWith("gsk-") ? apiKey.slice(1) : apiKey;
|
|
267
|
+
// Prefer the upstream-reported name; fall back to the seat key's identity
|
|
268
|
+
// claims or `GetUserStatus` — never fail the login on a label lookup.
|
|
269
|
+
const accountLabel =
|
|
270
|
+
response.name?.trim() || (await resolveDevinAccountLabel(key).catch(() => undefined));
|
|
271
|
+
return {
|
|
272
|
+
access: key,
|
|
273
|
+
refresh: "",
|
|
274
|
+
expiresAt: new Date(8640000000000000), // seat key: never expires
|
|
275
|
+
...(accountLabel ? { accountLabel } : {}),
|
|
276
|
+
};
|
|
277
|
+
},
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
// ── CLI adopt (credentials.toml) ──
|
|
281
|
+
|
|
282
|
+
const KNOWN_API_HOSTS = new Set(["server.codeium.com", "server.enterprise.windsurf.com"]);
|
|
283
|
+
|
|
284
|
+
/** Candidate `credentials.toml` paths per platform. */
|
|
285
|
+
export function devinCliCredentialPaths(): string[] {
|
|
286
|
+
const paths: string[] = [];
|
|
287
|
+
if (process.platform === "win32") {
|
|
288
|
+
const appData = process.env.APPDATA;
|
|
289
|
+
if (appData) paths.push(join(appData, "devin", "credentials.toml"));
|
|
290
|
+
paths.push(join(process.env.HOME ?? "", ".local", "share", "devin", "credentials.toml"));
|
|
291
|
+
} else {
|
|
292
|
+
const xdg = process.env.XDG_DATA_HOME;
|
|
293
|
+
if (xdg) paths.push(join(xdg, "devin", "credentials.toml"));
|
|
294
|
+
paths.push(join(process.env.HOME ?? "", ".local", "share", "devin", "credentials.toml"));
|
|
295
|
+
if (process.platform === "darwin") {
|
|
296
|
+
paths.push(join(process.env.HOME ?? "", "Library", "Application Support", "devin", "credentials.toml"));
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return [...new Set(paths)];
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/** Parses a Devin CLI `credentials.toml` and returns the Windsurf seat key. */
|
|
303
|
+
export function readDevinCliCredentials(file: string): string {
|
|
304
|
+
const text = readFileSync(file, "utf8");
|
|
305
|
+
// Minimal TOML parse: key = "value" lines.
|
|
306
|
+
const record: Record<string, string> = {};
|
|
307
|
+
for (const line of text.split(/\r?\n/)) {
|
|
308
|
+
const trimmed = line.trim();
|
|
309
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
310
|
+
const eq = trimmed.indexOf("=");
|
|
311
|
+
if (eq < 0) continue;
|
|
312
|
+
const key = trimmed.slice(0, eq).trim();
|
|
313
|
+
let value = trimmed.slice(eq + 1).trim();
|
|
314
|
+
if (value.startsWith('"') && value.endsWith('"')) value = value.slice(1, -1);
|
|
315
|
+
record[key] = value;
|
|
316
|
+
}
|
|
317
|
+
const key = record["windsurf_api_key"]?.trim() ?? "";
|
|
318
|
+
if (!key) throw new Error(`Devin CLI credentials at ${file} have no windsurf_api_key`);
|
|
319
|
+
const serverUrl = record["api_server_url"]?.trim();
|
|
320
|
+
if (serverUrl) {
|
|
321
|
+
let host: string | undefined;
|
|
322
|
+
try {
|
|
323
|
+
host = new URL(serverUrl).host;
|
|
324
|
+
} catch {
|
|
325
|
+
host = undefined;
|
|
326
|
+
}
|
|
327
|
+
if (!host || !KNOWN_API_HOSTS.has(host)) {
|
|
328
|
+
throw new Error(`Devin CLI credentials at ${file} point at unsupported API server`);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return key;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/** Reads the first existing credentials file and returns its seat key. */
|
|
335
|
+
export function loginDevinCliHook(): Promise<string> {
|
|
336
|
+
for (const file of devinCliCredentialPaths()) {
|
|
337
|
+
if (!existsSync(file)) continue;
|
|
338
|
+
try {
|
|
339
|
+
return Promise.resolve(readDevinCliCredentials(file));
|
|
340
|
+
} catch (error) {
|
|
341
|
+
// First existing file that fails validation wins; no fallback probing.
|
|
342
|
+
return Promise.reject(error);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return Promise.reject(
|
|
346
|
+
new Error(
|
|
347
|
+
`No Devin CLI credentials found (probed: ${devinCliCredentialPaths().join(", ")}). Run \`devin auth login\`, then retry.`,
|
|
348
|
+
),
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// ── Import credential (raw API key / session token) ──
|
|
353
|
+
// `devinOAuthClient.importCredential` handles pasted keys; no separate client.
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Devin model catalog: live discovery via `GetCliModelConfigs` plus a static
|
|
3
|
+
* seed for boot. Ported from oh-my-pi (`discovery/devin.ts`, MIT) with the
|
|
4
|
+
* Cartethyia `defineModel` shape.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { defineModel, type ModelDefinition } from "../../provider-model-definition";
|
|
8
|
+
import { callDevinUnary } from "./devin-http";
|
|
9
|
+
import {
|
|
10
|
+
DEVIN_CHAT_MESSAGE_PATH,
|
|
11
|
+
DEVIN_GET_CLI_MODEL_CONFIGS_PATH,
|
|
12
|
+
DEFAULT_CONTEXT_WINDOW,
|
|
13
|
+
DEFAULT_MAX_TOKENS,
|
|
14
|
+
DEVIN_DEFAULT_BASE_URL,
|
|
15
|
+
DEVIN_INTERNAL_MODEL_DISPLAYS,
|
|
16
|
+
devinDiscoveryMetadata,
|
|
17
|
+
} from "./devin-shared";
|
|
18
|
+
import { DevinMessageTypes } from "./protocol/devin-proto";
|
|
19
|
+
|
|
20
|
+
const DISCOVERY_TIMEOUT_MS = 15_000;
|
|
21
|
+
|
|
22
|
+
/** Static seed shown before discovery succeeds (boot-safe, synchronous). */
|
|
23
|
+
export const DEVIN_MODELS: readonly ModelDefinition[] = [
|
|
24
|
+
defineModel({
|
|
25
|
+
id: "swe-1-6-fast",
|
|
26
|
+
providerId: "devin",
|
|
27
|
+
wireFamily: "chat",
|
|
28
|
+
endpoint: DEVIN_CHAT_MESSAGE_PATH,
|
|
29
|
+
ctx: DEFAULT_CONTEXT_WINDOW,
|
|
30
|
+
out: 128_000,
|
|
31
|
+
// No vision: paired live control showed both available models either
|
|
32
|
+
// ignore or reject inline images. Declaring it would route images into a
|
|
33
|
+
// guaranteed failure.
|
|
34
|
+
reasoning: true,
|
|
35
|
+
toolCall: true,
|
|
36
|
+
}),
|
|
37
|
+
// `swe-1-6` removed: not present in the live `GetCliModelConfigs` roster on
|
|
38
|
+
// this account (1003 of 1010 configs are disabled) and a chat dispatch to
|
|
39
|
+
// it failed with `failed_precondition`.
|
|
40
|
+
// Operator-declared lanes. The upstream catalog has not been fetched live
|
|
41
|
+
// yet, so these rows are pinned from the measured spec rather than generated.
|
|
42
|
+
defineModel({
|
|
43
|
+
id: "swe-1-6-slow",
|
|
44
|
+
providerId: "devin",
|
|
45
|
+
wireFamily: "chat",
|
|
46
|
+
endpoint: DEVIN_CHAT_MESSAGE_PATH,
|
|
47
|
+
ctx: 200_000,
|
|
48
|
+
out: 128_000,
|
|
49
|
+
reasoning: true,
|
|
50
|
+
toolCall: true,
|
|
51
|
+
}),
|
|
52
|
+
defineModel({
|
|
53
|
+
id: "swe-2-flash-medium",
|
|
54
|
+
providerId: "devin",
|
|
55
|
+
wireFamily: "chat",
|
|
56
|
+
endpoint: DEVIN_CHAT_MESSAGE_PATH,
|
|
57
|
+
// Live `GetCliModelConfigs` (2026-11-07): ctx=262144, maxOutput=131072,
|
|
58
|
+
// features = thinking + tools. This uid is the account default.
|
|
59
|
+
ctx: 262_144,
|
|
60
|
+
out: 131_072,
|
|
61
|
+
reasoning: true,
|
|
62
|
+
toolCall: true,
|
|
63
|
+
}),
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
interface RawModelConfig {
|
|
67
|
+
modelUid?: string;
|
|
68
|
+
label?: string;
|
|
69
|
+
disabled?: boolean;
|
|
70
|
+
supportsImages?: boolean;
|
|
71
|
+
maxTokens?: number;
|
|
72
|
+
description?: string;
|
|
73
|
+
isNew?: boolean;
|
|
74
|
+
isBeta?: boolean;
|
|
75
|
+
isRecommended?: boolean;
|
|
76
|
+
isDefaultModelInFamily?: boolean;
|
|
77
|
+
modelInfo?: {
|
|
78
|
+
maxTokens?: number;
|
|
79
|
+
maxOutputTokens?: number;
|
|
80
|
+
displayOption?: number;
|
|
81
|
+
harnessUids?: string[];
|
|
82
|
+
isModelRouter?: boolean;
|
|
83
|
+
modelFeatures?: { supportsImages?: boolean; supportsToolCalls?: boolean; supportsThinking?: boolean };
|
|
84
|
+
};
|
|
85
|
+
modelDimensions?: { label?: string; value?: number; denominator?: string; kind?: number }[];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const REASONING_LABEL_PATTERN = /think|thinking|minimal|high|medium|low|xhigh|max|reasoning/i;
|
|
89
|
+
const NO_REASONING_LABEL_PATTERN = /\bno thinking\b/i;
|
|
90
|
+
|
|
91
|
+
function supportsThinking(config: RawModelConfig): boolean {
|
|
92
|
+
const features = config.modelInfo?.modelFeatures;
|
|
93
|
+
if (features) return features.supportsThinking === true;
|
|
94
|
+
const label = config.label ?? "";
|
|
95
|
+
if (NO_REASONING_LABEL_PATTERN.test(label)) return false;
|
|
96
|
+
return REASONING_LABEL_PATTERN.test(label);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function modelCost(config: RawModelConfig): { input: number; output: number; cache_read: number } {
|
|
100
|
+
let input = 0;
|
|
101
|
+
let output = 0;
|
|
102
|
+
let cache_read = 0;
|
|
103
|
+
const dims = config.modelDimensions ?? [];
|
|
104
|
+
for (const dim of dims) {
|
|
105
|
+
if (dim.kind !== 1 && dim.kind !== 2) continue; // COST / COST_FUZZY
|
|
106
|
+
if ((dim.label ?? "").toLowerCase() === "sidekick") break;
|
|
107
|
+
let value = dim.value ?? 0;
|
|
108
|
+
const denom = dim.denominator ?? "";
|
|
109
|
+
const m = denom.match(/(\d+(?:\.\d+)?)\s*([kmb])?/i);
|
|
110
|
+
let divisor = 1_000_000;
|
|
111
|
+
if (m) {
|
|
112
|
+
const num = Number(m[1]);
|
|
113
|
+
const suffix = m[2]?.toLowerCase();
|
|
114
|
+
if (suffix === "k") divisor = num * 1_000;
|
|
115
|
+
else if (suffix === "m") divisor = num * 1_000_000;
|
|
116
|
+
else if (suffix === "b") divisor = num * 1_000_000_000;
|
|
117
|
+
else divisor = num;
|
|
118
|
+
}
|
|
119
|
+
value = (value * 1_000_000) / divisor;
|
|
120
|
+
const label = (dim.label ?? "").toLowerCase();
|
|
121
|
+
if (label.includes("input")) input = Math.round(value * 1e6) / 1e6;
|
|
122
|
+
else if (label.includes("output")) output = Math.round(value * 1e6) / 1e6;
|
|
123
|
+
else if (label.includes("cached")) cache_read = Math.round(value * 1e6) / 1e6;
|
|
124
|
+
}
|
|
125
|
+
return { input, output, cache_read };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface DevinDiscoveryInput {
|
|
129
|
+
readonly credential: string;
|
|
130
|
+
readonly fetcher?: typeof fetch;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Live discovery: GetCliModelConfigs -> ModelDefinition rows. */
|
|
134
|
+
export async function discoverDevinModels(
|
|
135
|
+
input: DevinDiscoveryInput,
|
|
136
|
+
): Promise<readonly ModelDefinition[] | null> {
|
|
137
|
+
const { credential, fetcher } = input;
|
|
138
|
+
const request = {
|
|
139
|
+
metadata: devinDiscoveryMetadata(credential),
|
|
140
|
+
};
|
|
141
|
+
const response = await callDevinUnary<{ clientModelConfigs?: RawModelConfig[]; defaultOverrideModelConfig?: { modelUid?: string } }>(
|
|
142
|
+
DEVIN_GET_CLI_MODEL_CONFIGS_PATH,
|
|
143
|
+
DevinMessageTypes.GetCliModelConfigsRequest,
|
|
144
|
+
DevinMessageTypes.GetCliModelConfigsResponse,
|
|
145
|
+
request,
|
|
146
|
+
{
|
|
147
|
+
baseUrl: DEVIN_DEFAULT_BASE_URL,
|
|
148
|
+
...(fetcher === undefined ? {} : { fetchImpl: fetcher }),
|
|
149
|
+
signal: AbortSignal.timeout(DISCOVERY_TIMEOUT_MS),
|
|
150
|
+
},
|
|
151
|
+
);
|
|
152
|
+
const configs = response.clientModelConfigs ?? [];
|
|
153
|
+
const defaultUid = response.defaultOverrideModelConfig?.modelUid?.trim();
|
|
154
|
+
|
|
155
|
+
const rows: ModelDefinition[] = [];
|
|
156
|
+
const seen = new Set<string>();
|
|
157
|
+
for (const config of configs) {
|
|
158
|
+
if (config.disabled) continue;
|
|
159
|
+
const uid = config.modelUid?.trim();
|
|
160
|
+
if (!uid || seen.has(uid)) continue;
|
|
161
|
+
const displayOption = config.modelInfo?.displayOption ?? 0;
|
|
162
|
+
if (DEVIN_INTERNAL_MODEL_DISPLAYS.has(displayOption)) continue;
|
|
163
|
+
seen.add(uid);
|
|
164
|
+
|
|
165
|
+
const features = config.modelInfo?.modelFeatures;
|
|
166
|
+
// Operator-pinned lanes, confirmed against live `GetCliModelConfigs`:
|
|
167
|
+
// `swe-2-flash-medium` = 262144/131072 (account default),
|
|
168
|
+
// `swe-1-6-slow` = 200000/128000.
|
|
169
|
+
const pinnedContext = uid === "swe-2-flash-medium" ? 262_144 : uid === "swe-1-6-slow" ? 200_000 : undefined;
|
|
170
|
+
const pinnedOutput = uid === "swe-2-flash-medium" ? 131_072 : undefined;
|
|
171
|
+
const contextLimit =
|
|
172
|
+
pinnedContext ?? ((config.maxTokens ?? 0) > 0 ? config.maxTokens! : (config.modelInfo?.maxTokens ?? 0) > 0 ? config.modelInfo!.maxTokens! : DEFAULT_CONTEXT_WINDOW);
|
|
173
|
+
const outputLimit = pinnedOutput ?? ((config.modelInfo?.maxOutputTokens ?? 0) > 0 ? config.modelInfo!.maxOutputTokens! : DEFAULT_MAX_TOKENS);
|
|
174
|
+
// `supportsImages` from the server is NOT an entitlement: live paired
|
|
175
|
+
// control showed `swe-1-6-slow` (supportsImages=true) answering the same
|
|
176
|
+
// "cannot see" with and without an ImageData payload. Never advertise
|
|
177
|
+
// vision for this provider; the adapter drops image parts entirely.
|
|
178
|
+
const cost = modelCost(config);
|
|
179
|
+
|
|
180
|
+
rows.push(
|
|
181
|
+
defineModel({
|
|
182
|
+
id: uid,
|
|
183
|
+
providerId: "devin",
|
|
184
|
+
wireFamily: "chat",
|
|
185
|
+
endpoint: DEVIN_CHAT_MESSAGE_PATH,
|
|
186
|
+
ctx: contextLimit,
|
|
187
|
+
out: outputLimit,
|
|
188
|
+
reasoning: supportsThinking(config),
|
|
189
|
+
toolCall: features ? features.supportsToolCalls !== false : true,
|
|
190
|
+
free: cost.input === 0 && cost.output === 0,
|
|
191
|
+
...(config.isNew ? { warnings: [{ kind: "is_new", message: "New model", severity: "info" as const }] } : {}),
|
|
192
|
+
...(config.isRecommended ? { warnings: [{ kind: "recommended", message: "Recommended by Devin", severity: "info" as const }] } : {}),
|
|
193
|
+
}),
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Account default wins: promote its row to default (first in list).
|
|
198
|
+
if (defaultUid && seen.has(defaultUid)) {
|
|
199
|
+
const idx = rows.findIndex((r) => r.modelId === defaultUid);
|
|
200
|
+
if (idx > 0) {
|
|
201
|
+
const [row] = rows.splice(idx, 1);
|
|
202
|
+
rows.unshift(row!);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return rows;
|
|
207
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Devin unary Connect-RPC client (`application/proto`).
|
|
3
|
+
*
|
|
4
|
+
* Responses may arrive as raw protobuf or gzipped protobuf (Connect servers
|
|
5
|
+
* compress when the client advertises `connect-accept-encoding: gzip`), so
|
|
6
|
+
* decode always has a gunzip fallback. Ported from oh-my-pi
|
|
7
|
+
* (`packages/catalog/src/wire/devin-proto.ts` + `discovery/devin.ts`, MIT).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { gunzipSync } from "node:zlib";
|
|
11
|
+
import type { Type } from "protobufjs";
|
|
12
|
+
import { GatewayError } from "../../../protocol/gateway-error";
|
|
13
|
+
import { DEVIN_CONNECT_UA } from "./devin-shared";
|
|
14
|
+
|
|
15
|
+
export interface DevinUnaryOptions {
|
|
16
|
+
readonly baseUrl?: string;
|
|
17
|
+
readonly fetchImpl?: typeof fetch;
|
|
18
|
+
readonly signal?: AbortSignal;
|
|
19
|
+
readonly timeoutMs?: number;
|
|
20
|
+
/** Include `connect-go` User-Agent (chat path) rather than plain unary. */
|
|
21
|
+
readonly connectGoUA?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Calls one unary RPC and decodes the response message. */
|
|
25
|
+
export async function callDevinUnary<T extends object>(
|
|
26
|
+
path: string,
|
|
27
|
+
requestType: Type,
|
|
28
|
+
responseType: Type,
|
|
29
|
+
request: object,
|
|
30
|
+
options: DevinUnaryOptions = {},
|
|
31
|
+
): Promise<T> {
|
|
32
|
+
const baseUrl = (options.baseUrl ?? "https://server.codeium.com").replace(/\/+$/, "");
|
|
33
|
+
const fetcher = options.fetchImpl ?? fetch;
|
|
34
|
+
const body = Buffer.from(requestType.encode(request).finish());
|
|
35
|
+
|
|
36
|
+
const headers: Record<string, string> = {
|
|
37
|
+
"content-type": "application/proto",
|
|
38
|
+
"connect-protocol-version": "1",
|
|
39
|
+
accept: "*/*",
|
|
40
|
+
};
|
|
41
|
+
if (options.connectGoUA) headers["user-agent"] = DEVIN_CONNECT_UA;
|
|
42
|
+
|
|
43
|
+
const signal =
|
|
44
|
+
options.signal === undefined
|
|
45
|
+
? options.timeoutMs !== undefined
|
|
46
|
+
? AbortSignal.timeout(options.timeoutMs)
|
|
47
|
+
: undefined
|
|
48
|
+
: options.timeoutMs !== undefined
|
|
49
|
+
? AbortSignal.any([options.signal, AbortSignal.timeout(options.timeoutMs)])
|
|
50
|
+
: options.signal;
|
|
51
|
+
|
|
52
|
+
const response = await fetcher(`${baseUrl}${path}`, {
|
|
53
|
+
method: "POST",
|
|
54
|
+
headers,
|
|
55
|
+
body,
|
|
56
|
+
...(signal === undefined ? {} : { signal }),
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const buf = Buffer.from(await response.arrayBuffer());
|
|
60
|
+
if (!response.ok) {
|
|
61
|
+
const text = buf.subarray(0, 2048).toString("utf8");
|
|
62
|
+
throw new GatewayError(
|
|
63
|
+
"upstream_unprocessable",
|
|
64
|
+
response.status,
|
|
65
|
+
`Devin upstream error ${response.status}: ${text} (request {requestId})`,
|
|
66
|
+
{ providerId: "devin", upstreamCode: String(response.status) },
|
|
67
|
+
"upstream",
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return decodeDevinUnary<T>(responseType, buf);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Decodes a unary response, trying raw protobuf then gunzip. */
|
|
75
|
+
export function decodeDevinUnary<T extends object>(type: Type, buf: Buffer): T {
|
|
76
|
+
try {
|
|
77
|
+
return type.decode(buf) as T;
|
|
78
|
+
} catch {
|
|
79
|
+
try {
|
|
80
|
+
return type.decode(gunzipSync(buf)) as T;
|
|
81
|
+
} catch {
|
|
82
|
+
throw new GatewayError(
|
|
83
|
+
"internal_error",
|
|
84
|
+
500,
|
|
85
|
+
"Devin upstream returned an undecodable response (request {requestId})",
|
|
86
|
+
{ providerId: "devin" },
|
|
87
|
+
"upstream",
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|