cartethyia 2.8.0 → 2.8.1
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 +3 -3
- package/dist/dashboard/assets/{BannedPage-Cc0vKpMG.js → BannedPage-Dq530awD.js} +1 -1
- package/dist/dashboard/assets/BannedPage-Dq530awD.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-Bqj2aa1T.js → CliToolDetailPage-DZzaS-7S.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-DZzaS-7S.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-CXsaOAO2.js → CliToolsPage-DaZZ3cqP.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-DaZZ3cqP.js.gz +0 -0
- package/dist/dashboard/assets/{CombosPage-BeLpJ4ma.js → CombosPage-BlC_xdkX.js} +1 -1
- package/dist/dashboard/assets/CombosPage-BlC_xdkX.js.gz +0 -0
- package/dist/dashboard/assets/{ConsoleLogPage-DnV0uZ3m.js → ConsoleLogPage-D-rqeVtG.js} +1 -1
- package/dist/dashboard/assets/ConsoleLogPage-D-rqeVtG.js.gz +0 -0
- package/dist/dashboard/assets/{CustomizationPage-CBxAjo63.js → CustomizationPage-7xT5yg2E.js} +1 -1
- package/dist/dashboard/assets/CustomizationPage-7xT5yg2E.js.gz +0 -0
- package/dist/dashboard/assets/{HealthEventsModal-B5yEE0k6.js → HealthEventsModal-Clq8E9cW.js} +1 -1
- package/dist/dashboard/assets/HealthEventsModal-Clq8E9cW.js.gz +0 -0
- package/dist/dashboard/assets/{InjectSystemPromptPanel-BWFLECmz.js → InjectSystemPromptPanel-6Teoq9Se.js} +1 -1
- package/dist/dashboard/assets/InjectSystemPromptPanel-6Teoq9Se.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-CgoPc5B9.js → LoginPage-CZuI0MAR.js} +1 -1
- package/dist/dashboard/assets/LoginPage-CZuI0MAR.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-D9bFQern.js → ModelPicker-C1hNTlkr.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-C1hNTlkr.js.gz +0 -0
- package/dist/dashboard/assets/{OverviewPage-B_p4tL8k.js → OverviewPage-0yfhSI1C.js} +1 -1
- package/dist/dashboard/assets/OverviewPage-0yfhSI1C.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-xqTxdQ4c.js +8 -0
- package/dist/dashboard/assets/ProviderDetailPage-xqTxdQ4c.js.gz +0 -0
- package/dist/dashboard/assets/{ProviderIcon-D_0PiZtg.js → ProviderIcon-Ddhm1InL.js} +1 -1
- package/dist/dashboard/assets/ProviderIcon-Ddhm1InL.js.gz +0 -0
- package/dist/dashboard/assets/{ProvidersPage-CLI2bJTK.js → ProvidersPage-C3fYuu6i.js} +1 -1
- package/dist/dashboard/assets/ProvidersPage-C3fYuu6i.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-CHtJtZ3u.js +9 -0
- package/dist/dashboard/assets/ProxyPage-CHtJtZ3u.js.gz +0 -0
- package/dist/dashboard/assets/{QuotaPage-DA-a1iSN.js → QuotaPage-D1RV_2BX.js} +1 -1
- package/dist/dashboard/assets/QuotaPage-D1RV_2BX.js.gz +0 -0
- package/dist/dashboard/assets/{SettingsPage-CctTga2C.js → SettingsPage-BzZAvYSX.js} +1 -1
- package/dist/dashboard/assets/SettingsPage-BzZAvYSX.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-febLU4r1.js → SetupPage--s-EfEIL.js} +1 -1
- package/dist/dashboard/assets/SetupPage--s-EfEIL.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-M6fvR5Wz.js → StudioPage-BjXFGSK4.js} +1 -1
- package/dist/dashboard/assets/StudioPage-BjXFGSK4.js.gz +0 -0
- package/dist/dashboard/assets/{UsagePage-nyeuRqz9.js → UsagePage-CP8F8BN7.js} +1 -1
- package/dist/dashboard/assets/UsagePage-CP8F8BN7.js.gz +0 -0
- package/dist/dashboard/assets/{api-keys-BFOlvrJC.js → api-keys-2hc12Uz2.js} +1 -1
- package/dist/dashboard/assets/api-keys-2hc12Uz2.js.gz +0 -0
- package/dist/dashboard/assets/app-DGXbNfAR.css +2 -0
- package/dist/dashboard/assets/app-DGXbNfAR.css.gz +0 -0
- package/dist/dashboard/assets/{assert-CqTLw5e7.js → assert-BMcj1xZE.js} +1 -1
- package/dist/dashboard/assets/assert-BMcj1xZE.js.gz +0 -0
- package/dist/dashboard/assets/{badge-C83EqLB0.js → badge-DkRUuVfz.js} +1 -1
- package/dist/dashboard/assets/badge-DkRUuVfz.js.gz +0 -0
- package/dist/dashboard/assets/{banner-D1xn7pIe.js → banner-CDjW841Q.js} +1 -1
- package/dist/dashboard/assets/banner-CDjW841Q.js.gz +0 -0
- package/dist/dashboard/assets/{card-DU3Lq5Lm.js → card-Bto5UlL9.js} +1 -1
- package/dist/dashboard/assets/card-Bto5UlL9.js.gz +0 -0
- package/dist/dashboard/assets/{cli-tools-Dw1U_drK.js → cli-tools-DaH6g9hL.js} +1 -1
- package/dist/dashboard/assets/cli-tools-DaH6g9hL.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-CEL5Sg_J.js → clipboard-button-BYKBCzjk.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-BYKBCzjk.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-BjFR5UHy.js → dialog-DL0Zftwv.js} +1 -1
- package/dist/dashboard/assets/dialog-DL0Zftwv.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-HvBW0AjI.js → drawer-Dpx_UXaS.js} +1 -1
- package/dist/dashboard/assets/drawer-Dpx_UXaS.js.gz +0 -0
- package/dist/dashboard/assets/{entry-CECVELyy.js → entry-BV7-1NoM.js} +1 -1
- package/dist/dashboard/assets/entry-BV7-1NoM.js.gz +0 -0
- package/dist/dashboard/assets/{entry-DYYofQ7W.js → entry-CGvdNU6r.js} +1 -1
- package/dist/dashboard/assets/entry-CGvdNU6r.js.gz +0 -0
- package/dist/dashboard/assets/{entry-BSu3gFlt.js → entry-DScKYvZj.js} +3 -3
- package/dist/dashboard/assets/entry-DScKYvZj.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-DLW4i8zK.js → formatters-BS3DZlZj.js} +1 -1
- package/dist/dashboard/assets/formatters-BS3DZlZj.js.gz +0 -0
- package/dist/dashboard/assets/{index-1-fCqe64.js → index-SQnvvTWX.js} +2 -2
- package/dist/dashboard/assets/index-SQnvvTWX.js.gz +0 -0
- package/dist/dashboard/assets/{inline-DGvU2oLK.js → inline-Dl-cuLK0.js} +1 -1
- package/dist/dashboard/assets/inline-Dl-cuLK0.js.gz +0 -0
- package/dist/dashboard/assets/{input-iNnO1X2K.js → input-Bfx0EzRc.js} +1 -1
- package/dist/dashboard/assets/input-Bfx0EzRc.js.gz +0 -0
- package/dist/dashboard/assets/{layout-0q8UGypb.js → layout-BxhbQ_JS.js} +1 -1
- package/dist/dashboard/assets/layout-BxhbQ_JS.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-C0w2bQjJ.js → list-row-D49Y4ptn.js} +1 -1
- package/dist/dashboard/assets/list-row-D49Y4ptn.js.gz +0 -0
- package/dist/dashboard/assets/{live-M5aJ_xCa.js → live-CbVTdX9V.js} +1 -1
- package/dist/dashboard/assets/live-CbVTdX9V.js.gz +0 -0
- package/dist/dashboard/assets/{network-BKeQGT3K.js → network-JGvDveof.js} +1 -1
- package/dist/dashboard/assets/network-JGvDveof.js.gz +0 -0
- package/dist/dashboard/assets/{numbers-CivAk3da.js → numbers-Dw6kiew-.js} +1 -1
- package/dist/dashboard/assets/numbers-Dw6kiew-.js.gz +0 -0
- package/dist/dashboard/assets/{pagination-CpzvI5ep.js → pagination-iUoI8GeE.js} +1 -1
- package/dist/dashboard/assets/pagination-iUoI8GeE.js.gz +0 -0
- package/dist/dashboard/assets/providers/sensenova-intl.png +0 -0
- package/dist/dashboard/assets/providers/sensenova.png +0 -0
- package/dist/dashboard/assets/{routing-BFQX2VBc.js → routing-8jZr-3WE.js} +1 -1
- package/dist/dashboard/assets/routing-8jZr-3WE.js.gz +0 -0
- package/dist/dashboard/assets/{select-CXbck0jG.js → select-oO1FxMVb.js} +1 -1
- package/dist/dashboard/assets/select-oO1FxMVb.js.gz +0 -0
- package/dist/dashboard/assets/{settings-C_XvMHh9.js → settings-CLr3kdIx.js} +1 -1
- package/dist/dashboard/assets/settings-CLr3kdIx.js.gz +0 -0
- package/dist/dashboard/assets/{stack-DggRiCxE.js → stack-Y3pDteZr.js} +1 -1
- package/dist/dashboard/assets/stack-Y3pDteZr.js.gz +0 -0
- package/dist/dashboard/assets/{state-ClFzfbYp.js → state-CArX_Iak.js} +1 -1
- package/dist/dashboard/assets/state-CArX_Iak.js.gz +0 -0
- package/dist/dashboard/assets/{switch-CTIFxKqS.js → switch-DwlwmDO-.js} +1 -1
- package/dist/dashboard/assets/switch-DwlwmDO-.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-BClG3yWj.js → useMutation-DJ9GYEgf.js} +1 -1
- package/dist/dashboard/assets/useMutation-DJ9GYEgf.js.gz +0 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +51 -51
- package/migrations/full-postgres/0047_add_provider_device_flow_proxy.sql +17 -0
- package/migrations/lite-sqlite3/0047_add_provider_device_flow_proxy.sql +19 -0
- package/package.json +1 -1
- package/src/boot/app.ts +1 -1
- package/src/boot/dependencies.ts +4 -6
- package/src/config/providers.ts +6 -5
- package/src/config/spec.ts +8 -8
- package/src/console/backup/nine-router.ts +71 -10
- package/src/console/backup/service.ts +8 -1
- package/src/console/console-router.ts +1 -1
- package/src/console/domain-registration.ts +7 -2
- package/src/console/inferhub/client.ts +1 -1
- package/src/console/observability/live.ts +2 -2
- package/src/console/providers/catalog/contracts.ts +9 -3
- package/src/console/providers/catalog/model-operations.ts +2 -3
- package/src/console/providers/catalog/provider-operations.ts +1 -1
- package/src/console/providers/catalog/routes.ts +2 -2
- package/src/console/providers/catalog/store.ts +1914 -1907
- package/src/console/providers/detail/routes.ts +1 -0
- package/src/console/providers/detail/store.ts +4 -0
- package/src/console/providers/oauth/callback-listener.ts +30 -11
- package/src/console/providers/oauth/device-egress.ts +123 -0
- package/src/console/providers/oauth/routes.ts +123 -16
- package/src/console/quota/account-quota-tenant.ts +5 -5
- package/src/console/quota/account-test-egress.ts +2 -2
- package/src/console/quota/daily-growth-job.ts +1 -1
- package/src/console/routing/pools/contracts.ts +1 -1
- package/src/console/routing/pools/operations.ts +1 -1
- package/src/console/routing/pools/probe-verdict.ts +2 -2
- package/src/console/routing/pools/relay-deploy.ts +1 -1
- package/src/console/routing/pools/store.ts +4 -4
- package/src/network/client.ts +67 -0
- package/src/network/egress/bidi-session.ts +322 -0
- package/src/network/egress/dial.ts +201 -0
- package/src/network/egress/factory.ts +191 -0
- package/src/network/{outbound-fetch.ts → egress/fetch.ts} +195 -29
- package/src/network/egress/tls-profile.ts +123 -0
- package/src/network/egress/ws.ts +175 -0
- package/src/network/{types.ts → pool/agent-config.ts} +1 -1
- package/src/network/pool/agent.ts +2 -2
- package/src/network/{pool-cooldown-store.ts → pool/cooldown-store.ts} +7 -7
- package/src/network/{pool-health-machine.ts → pool/health.ts} +205 -6
- package/src/network/pool/resolver.ts +10 -849
- package/src/network/pool/selector.ts +2 -2
- package/src/network/ssrf.ts +1 -1
- package/src/network/{retry.ts → util/retry.ts} +1 -1
- package/src/observability/telemetry-buffer.ts +1 -1
- package/src/persistence/schema.ts +9 -0
- package/src/persistence/ttl-cache.ts +1 -1
- package/src/protocol/codecs.ts +58 -1
- package/src/protocol/request/messages.ts +31 -18
- package/src/protocol/response/messages.ts +34 -7
- package/src/protocol/translation/quirks.ts +15 -0
- package/src/protocol/upstream/send-claude-messages.ts +2 -2
- package/src/providers/adapters/compatible-adapter.ts +5 -0
- package/src/providers/authentication/oauth-client.ts +40 -6
- package/src/providers/authentication/oauth-device-flow.ts +7 -3
- package/src/providers/authentication/oauth-flow-store.ts +82 -1
- package/src/providers/authentication/oauth-refresh-service.ts +766 -739
- package/src/providers/default-registry.ts +76 -0
- package/src/providers/discovery/discovery-types.ts +7 -0
- package/src/providers/discovery/probe-phases.ts +6 -2
- package/src/providers/discovery/probing-service.ts +7 -0
- package/src/providers/integrations/antigravity/antigravity-protocol.ts +11 -0
- package/src/providers/integrations/buddy/account.ts +969 -0
- package/src/providers/integrations/buddy/capabilities.ts +10 -10
- package/src/providers/integrations/buddy/{buddy-catalog-shared.ts → catalog.ts} +198 -136
- package/src/providers/integrations/buddy/{buddy-chat-shared.ts → chat.ts} +5 -5
- package/src/providers/integrations/buddy/codebuddy/index.ts +429 -0
- package/src/providers/integrations/buddy/codebuddy/shared.ts +4 -4
- package/src/providers/integrations/buddy/{buddy-identity.ts → headers.ts} +138 -140
- package/src/providers/integrations/buddy/lifecycle.ts +1018 -0
- package/src/providers/integrations/buddy/{buddy-media.ts → media.ts} +1 -1
- package/src/providers/integrations/cline/cline-oauth.ts +10 -5
- package/src/providers/integrations/cline/cline.ts +3 -4
- package/src/providers/integrations/clixad/clixad-oauth.ts +98 -0
- package/src/providers/integrations/clixad/clixad-quota.ts +132 -0
- package/src/providers/integrations/clixad/provider.ts +145 -0
- package/src/providers/integrations/codex/codex-oauth.ts +8 -5
- package/src/providers/integrations/cursor/cursor-auth.ts +1 -1
- package/src/providers/integrations/cursor/cursor-hardening.ts +279 -0
- package/src/providers/integrations/cursor/cursor-http2.ts +605 -6
- package/src/providers/integrations/cursor/cursor-shared.ts +1 -1
- package/src/providers/integrations/cursor/cursor.ts +485 -70
- package/src/providers/integrations/deepai/deepai-catalog.ts +149 -0
- package/src/providers/integrations/deepai/deepai-common.ts +145 -0
- package/src/providers/integrations/deepai/deepai-oauth.ts +129 -0
- package/src/providers/integrations/deepai/deepai-quota.ts +97 -0
- package/src/providers/integrations/deepai/provider.ts +42 -0
- package/src/providers/integrations/devin/devin-auth.ts +7 -0
- package/src/providers/integrations/factory/factory-oauth.ts +16 -7
- package/src/providers/integrations/freebuff/freebuff.ts +2 -2
- package/src/providers/integrations/freepi/freepi-catalog.ts +5 -0
- package/src/providers/integrations/freepi/freepi-discovery.ts +2 -2
- package/src/providers/integrations/freepi/freepi-headers.ts +2 -2
- package/src/providers/integrations/freepi/freepi-protocol.ts +11 -2
- package/src/providers/integrations/freepi/freepi-realm.ts +0 -3
- package/src/providers/integrations/freepi/freepi-session.ts +158 -0
- package/src/providers/integrations/freepi/freepi.ts +80 -54
- package/src/providers/integrations/github/github-discovery.ts +30 -13
- package/src/providers/integrations/github/github-oauth.ts +217 -44
- package/src/providers/integrations/github/github-quota.ts +22 -5
- package/src/providers/integrations/github/github.ts +40 -32
- package/src/providers/integrations/grok/grok-oauth.ts +4 -4
- package/src/providers/integrations/hermes/hermes-oauth.ts +3 -3
- package/src/providers/integrations/hermes/hermes.ts +34 -5
- package/src/providers/integrations/kilo/kilo-oauth.ts +4 -4
- package/src/providers/integrations/kilo/kilo.ts +1 -1
- package/src/providers/integrations/kimi/kimi-oauth.ts +3 -3
- package/src/providers/integrations/kiro/kiro-oauth.ts +2 -2
- package/src/providers/integrations/muse/muse-oauth.ts +5 -5
- package/src/providers/integrations/opencode/fingerprint.ts +1 -1
- package/src/providers/integrations/openrouter/provider.ts +1 -1
- package/src/providers/integrations/perch/perch-protocol.ts +0 -3
- package/src/providers/integrations/perch/perch.ts +3 -3
- package/src/providers/integrations/qoder/claim-qoder-credits.ts +1 -1
- package/src/providers/integrations/qoder/qoder-adapter.ts +19 -113
- package/src/providers/integrations/qoder/qoder-auth.ts +8 -1
- package/src/providers/integrations/qoder/qoder-catalog.ts +1 -1
- package/src/providers/integrations/qoder/qoder-chat.ts +25 -8
- package/src/providers/integrations/qoder/qoder-checkin.ts +1 -1
- package/src/providers/integrations/qoder/qoder-cosy.ts +37 -9
- package/src/providers/integrations/qoder/qoder-oauth.ts +9 -2
- package/src/providers/integrations/qoder/qoder-realm.ts +13 -36
- package/src/providers/integrations/qoder/qoder-sse.ts +447 -68
- package/src/providers/integrations/qoder/qoder-types.ts +9 -0
- package/src/providers/integrations/qoder/qoder.ts +3 -25
- package/src/providers/integrations/sensenova/provider.ts +144 -0
- package/src/providers/integrations/trae/trae-quota.ts +15 -1
- package/src/providers/integrations/vercel/provider.ts +117 -0
- package/src/providers/integrations/vercel/vercel-quota.ts +96 -0
- package/src/providers/integrations/xai/xai-oauth.ts +5 -5
- package/src/providers/integrations/zcode-start-plan/gifts.ts +1 -1
- package/src/providers/integrations/zcode-start-plan/oauth.ts +3 -3
- package/src/providers/integrations/zro/zro.ts +2 -1
- package/src/providers/operations/account-refresh-status.ts +14 -0
- package/src/providers/operations/client-versions.ts +95 -30
- package/src/providers/operations/oauth-refresh-lead.ts +46 -22
- package/src/providers/operations/provider-caches.ts +1 -1
- package/src/providers/operations/provider-credential-service.ts +22 -25
- package/src/providers/operations/session-resolution.ts +17 -1
- package/src/providers/provider-contracts.ts +2 -2
- package/src/providers/provider-metadata.ts +30 -0
- package/src/providers/quota/quota-contracts.ts +2 -0
- package/src/providers/quota/refresh.ts +25 -3
- package/src/security/ip-boundary.ts +1 -1
- package/src/transport/dispatch/attempt-finalize.ts +1 -1
- package/src/transport/dispatch/attempt-loop.ts +3 -3
- package/src/transport/dispatch/handler-deps.ts +1 -1
- package/src/transport/dispatch/leases.ts +1 -1
- package/src/transport/dispatch/retry-policy.ts +1 -1
- package/src/transport/dispatch/streaming/finalize.ts +2 -2
- package/src/transport/dispatch/streaming/stream.ts +1 -1
- package/src/transport/dispatch/upstream.ts +1 -1
- package/src/transport/shutdown-notice.ts +1 -1
- package/src/workers/buddy-growth-tasks.ts +8 -8
- package/src/workers/daily-checkin.ts +4 -4
- package/src/workers/quota-refresh-sweep.ts +1 -1
- package/src/workers/zcode-gift-claim.ts +1 -1
- package/dist/dashboard/assets/BannedPage-Cc0vKpMG.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-Bqj2aa1T.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-CXsaOAO2.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-BeLpJ4ma.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-DnV0uZ3m.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-CBxAjo63.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-B5yEE0k6.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-BWFLECmz.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-CgoPc5B9.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-D9bFQern.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-B_p4tL8k.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-rs5E2rxv.js +0 -8
- package/dist/dashboard/assets/ProviderDetailPage-rs5E2rxv.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-D_0PiZtg.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-CLI2bJTK.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-BwNC_-Cx.js +0 -9
- package/dist/dashboard/assets/ProxyPage-BwNC_-Cx.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-DA-a1iSN.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CctTga2C.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-febLU4r1.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-M6fvR5Wz.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-nyeuRqz9.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-BFOlvrJC.js.gz +0 -0
- package/dist/dashboard/assets/app-Dx2QgsNj.css +0 -2
- package/dist/dashboard/assets/app-Dx2QgsNj.css.gz +0 -0
- package/dist/dashboard/assets/assert-CqTLw5e7.js.gz +0 -0
- package/dist/dashboard/assets/badge-C83EqLB0.js.gz +0 -0
- package/dist/dashboard/assets/banner-D1xn7pIe.js.gz +0 -0
- package/dist/dashboard/assets/card-DU3Lq5Lm.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-Dw1U_drK.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-CEL5Sg_J.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BjFR5UHy.js.gz +0 -0
- package/dist/dashboard/assets/drawer-HvBW0AjI.js.gz +0 -0
- package/dist/dashboard/assets/entry-BSu3gFlt.js.gz +0 -0
- package/dist/dashboard/assets/entry-CECVELyy.js.gz +0 -0
- package/dist/dashboard/assets/entry-DYYofQ7W.js.gz +0 -0
- package/dist/dashboard/assets/formatters-DLW4i8zK.js.gz +0 -0
- package/dist/dashboard/assets/index-1-fCqe64.js.gz +0 -0
- package/dist/dashboard/assets/inline-DGvU2oLK.js.gz +0 -0
- package/dist/dashboard/assets/input-iNnO1X2K.js.gz +0 -0
- package/dist/dashboard/assets/layout-0q8UGypb.js.gz +0 -0
- package/dist/dashboard/assets/list-row-C0w2bQjJ.js.gz +0 -0
- package/dist/dashboard/assets/live-M5aJ_xCa.js.gz +0 -0
- package/dist/dashboard/assets/network-BKeQGT3K.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CivAk3da.js.gz +0 -0
- package/dist/dashboard/assets/pagination-CpzvI5ep.js.gz +0 -0
- package/dist/dashboard/assets/routing-BFQX2VBc.js.gz +0 -0
- package/dist/dashboard/assets/select-CXbck0jG.js.gz +0 -0
- package/dist/dashboard/assets/settings-C_XvMHh9.js.gz +0 -0
- package/dist/dashboard/assets/stack-DggRiCxE.js.gz +0 -0
- package/dist/dashboard/assets/state-ClFzfbYp.js.gz +0 -0
- package/dist/dashboard/assets/switch-CTIFxKqS.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-BClG3yWj.js.gz +0 -0
- package/src/console/providers/catalog/discover-constants.ts +0 -6
- package/src/network/pool-health.ts +0 -198
- package/src/providers/integrations/buddy/buddy-checkin.ts +0 -521
- package/src/providers/integrations/buddy/buddy-cli-headers.ts +0 -76
- package/src/providers/integrations/buddy/buddy-conversation-headers.ts +0 -130
- package/src/providers/integrations/buddy/buddy-discovery-shared.ts +0 -234
- package/src/providers/integrations/buddy/buddy-growth-shared.ts +0 -496
- package/src/providers/integrations/buddy/buddy-oauth-shared.ts +0 -341
- package/src/providers/integrations/buddy/buddy-quota-shared.ts +0 -417
- package/src/providers/integrations/buddy/buddy-server-tools.ts +0 -240
- package/src/providers/integrations/buddy/buddy-usage-shared.ts +0 -221
- package/src/providers/integrations/buddy/buddy-usage-store.ts +0 -181
- package/src/providers/integrations/buddy/codebuddy/cn-catalog.ts +0 -37
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +0 -188
- package/src/providers/integrations/buddy/codebuddy/oauth.ts +0 -47
- package/src/providers/integrations/buddy/codebuddy/provider.ts +0 -179
- package/src/providers/integrations/buddy/codebuddy/quota.ts +0 -154
- package/src/providers/integrations/qoder/chat-proto.ts +0 -328
- package/src/providers/integrations/qoder/qoder-grpc.ts +0 -870
- package/src/providers/integrations/qoder/qoder-ide.ts +0 -481
- /package/dist/dashboard/assets/{app-DIXLjojg.js → app-C0gTXEoN.js} +0 -0
- /package/dist/dashboard/assets/{app-DIXLjojg.js.gz → app-C0gTXEoN.js.gz} +0 -0
- /package/src/network/{http2-bidi.ts → bidi.ts} +0 -0
- /package/src/network/pool/{byte-accounting.ts → accounting.ts} +0 -0
- /package/src/network/pool/{pool-scripts.ts → scripts.ts} +0 -0
- /package/src/network/pool/{pool-state.ts → state.ts} +0 -0
- /package/src/network/{backoff.ts → util/backoff.ts} +0 -0
- /package/src/network/{deduplication.ts → util/dedupe.ts} +0 -0
- /package/src/network/{ip-utils.ts → util/ip.ts} +0 -0
- /package/src/network/{rate-limit-verdict.ts → verdict.ts} +0 -0
|
@@ -1,496 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The buddy growth/credit endpoints, as one call each.
|
|
3
|
-
*
|
|
4
|
-
* Six upstream capabilities the reference gateways exercise and we did not:
|
|
5
|
-
* streak makeup, milestone redeem, streak lottery, cat travel, first-buddy
|
|
6
|
-
* adoption, and the trial gift pack. Each is a "claim once per day" (or once
|
|
7
|
-
* per account) surface, and every one of them is a *credit gain* — which is why
|
|
8
|
-
* they exist here at all: an account that never claims them pays for credits it
|
|
9
|
-
* was entitled to.
|
|
10
|
-
*
|
|
11
|
-
* Two rules shape every function in this module:
|
|
12
|
-
*
|
|
13
|
-
* 1. **A duplicate is a success.** `409 duplicate`, business code `14051`, and
|
|
14
|
-
* the "not enough days / no chances / not eligible" family are all normal
|
|
15
|
-
* upstream states, not failures. Reporting them as errors would flood the
|
|
16
|
-
* log with one line per account per day and, worse, would let a gamification
|
|
17
|
-
* failure mark an account unhealthy.
|
|
18
|
-
* 2. **Nothing here throws.** A growth endpoint that 404s (the international
|
|
19
|
-
* sites do not serve all of them) yields `skipped`, never a cooldown.
|
|
20
|
-
*
|
|
21
|
-
* The reference gateways gate part of this chain on CN: the international
|
|
22
|
-
* `/activity/growth/*` paths exist but a fresh international account answers
|
|
23
|
-
* 500 on the first read. Rather than hard-coding that, each task reports
|
|
24
|
-
* `skipped` when the endpoint does not answer, so an operator can switch a task
|
|
25
|
-
* off with `CARTETHYIA_DISABLED_TASKS` if a variant is noisy.
|
|
26
|
-
*/
|
|
27
|
-
import { randomUUID } from "node:crypto";
|
|
28
|
-
import type { FetchLike } from "../../quota/quota-contracts";
|
|
29
|
-
import { record as asRecord, text as asText } from "../../quota/quota-contracts";
|
|
30
|
-
|
|
31
|
-
/** Abort budget for one growth request. */
|
|
32
|
-
const TIMEOUT_MS = 15_000;
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* The base the growth endpoints hang off.
|
|
36
|
-
*
|
|
37
|
-
* Growth is a chat-host surface, not a billing-host one: the reference sends
|
|
38
|
-
* these to `chatBase` (the provider's console origin) with billing identity
|
|
39
|
-
* headers. `providerBaseUrl` is that origin for every buddy provider, and the
|
|
40
|
-
* `/v2` segment — which the billing meter needs and growth does not — is the
|
|
41
|
-
* one thing that differs, so it is stripped rather than assumed.
|
|
42
|
-
*/
|
|
43
|
-
function growthBase(baseUrl: string): string {
|
|
44
|
-
return baseUrl.replace(/\/+$/, "").replace(/\/v2$/, "");
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/** One growth/credit call's outcome. */
|
|
48
|
-
export type BuddyGrowthState = "claimed" | "already" | "not_eligible" | "skipped" | "error";
|
|
49
|
-
|
|
50
|
-
export interface BuddyGrowthResult {
|
|
51
|
-
readonly state: BuddyGrowthState;
|
|
52
|
-
/** Provider-facing detail for the log; `null` on success. */
|
|
53
|
-
readonly error: string | null;
|
|
54
|
-
/** Credits granted, when the upstream reported them. */
|
|
55
|
-
readonly credit: number | null;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export interface BuddyGrowthCall {
|
|
59
|
-
readonly baseUrl: string;
|
|
60
|
-
readonly headers: Record<string, string>;
|
|
61
|
-
readonly credential: string;
|
|
62
|
-
readonly fetcher: FetchLike;
|
|
63
|
-
/** Account UID for the `X-User-Id` identity header. */
|
|
64
|
-
readonly uid?: string | undefined;
|
|
65
|
-
/** Which account and provider the call belongs to — needed by the activity report. */
|
|
66
|
-
readonly accountId: string;
|
|
67
|
-
readonly providerId: string;
|
|
68
|
-
/** Injectable idempotency token generator (tests). */
|
|
69
|
-
readonly token?: (prefix: string) => string;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/** `client_token` is `<prefix>-<uuid>` per call: reusing one is an idempotent no-op upstream. */
|
|
73
|
-
function clientToken(call: BuddyGrowthCall, prefix: string): string {
|
|
74
|
-
return (call.token ?? ((name: string) => `${name}-${randomUUID()}`))(prefix);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
interface Reply {
|
|
78
|
-
readonly status: number;
|
|
79
|
-
readonly payload: Record<string, unknown> | null;
|
|
80
|
-
readonly data: Record<string, unknown> | null;
|
|
81
|
-
readonly code: number | null;
|
|
82
|
-
readonly message: string;
|
|
83
|
-
readonly ok: boolean;
|
|
84
|
-
readonly transport: string | null;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* One growth request, unwrapped.
|
|
89
|
-
*
|
|
90
|
-
* `ok === false` with `transport === null` is a *business* refusal (the
|
|
91
|
-
* endpoint answered and said no), which is the state most of this module treats
|
|
92
|
-
* as normal. `transport !== null` is a real failure — the endpoint did not
|
|
93
|
-
* answer at all.
|
|
94
|
-
*/
|
|
95
|
-
async function call(
|
|
96
|
-
options: BuddyGrowthCall,
|
|
97
|
-
method: "GET" | "POST",
|
|
98
|
-
path: string,
|
|
99
|
-
body?: Record<string, unknown>,
|
|
100
|
-
): Promise<Reply> {
|
|
101
|
-
const url = `${growthBase(options.baseUrl)}${path}`;
|
|
102
|
-
let response: Response;
|
|
103
|
-
try {
|
|
104
|
-
response = await options.fetcher(url, {
|
|
105
|
-
method,
|
|
106
|
-
headers: {
|
|
107
|
-
...options.headers,
|
|
108
|
-
...(options.uid === undefined || options.uid.length === 0
|
|
109
|
-
? {}
|
|
110
|
-
: { "X-User-Id": options.uid }),
|
|
111
|
-
Authorization: `Bearer ${options.credential}`,
|
|
112
|
-
"Content-Type": "application/json",
|
|
113
|
-
Accept: "application/json",
|
|
114
|
-
},
|
|
115
|
-
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
116
|
-
signal: AbortSignal.timeout(TIMEOUT_MS),
|
|
117
|
-
});
|
|
118
|
-
} catch (error) {
|
|
119
|
-
return {
|
|
120
|
-
status: 0,
|
|
121
|
-
payload: null,
|
|
122
|
-
data: null,
|
|
123
|
-
code: null,
|
|
124
|
-
message: error instanceof Error ? error.message : "request failed",
|
|
125
|
-
ok: false,
|
|
126
|
-
transport: error instanceof Error ? error.message : "request failed",
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
let payload: Record<string, unknown> | null = null;
|
|
130
|
-
let text = "";
|
|
131
|
-
try {
|
|
132
|
-
text = await response.text();
|
|
133
|
-
payload = text.length > 0 ? asRecord(JSON.parse(text)) : null;
|
|
134
|
-
} catch {
|
|
135
|
-
payload = null;
|
|
136
|
-
}
|
|
137
|
-
const code = payload === null || !("code" in payload) ? null : Number(payload["code"]);
|
|
138
|
-
const message = asText(payload?.["msg"]) ?? asText(payload?.["message"]) ?? text.slice(0, 200);
|
|
139
|
-
const data = asRecord(payload?.["data"]) ?? (payload !== null && !("code" in payload) ? payload : null);
|
|
140
|
-
if (!response.ok) {
|
|
141
|
-
return {
|
|
142
|
-
status: response.status,
|
|
143
|
-
payload,
|
|
144
|
-
data,
|
|
145
|
-
code: Number.isFinite(code) ? code : null,
|
|
146
|
-
message,
|
|
147
|
-
ok: false,
|
|
148
|
-
// A 409/403/400 here is the upstream's own answer, not a transport fault.
|
|
149
|
-
transport: null,
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
if (Number.isFinite(code) && code !== 0) {
|
|
153
|
-
return { status: response.status, payload, data, code, message, ok: false, transport: null };
|
|
154
|
-
}
|
|
155
|
-
return { status: response.status, payload, data, code, message, ok: true, transport: null };
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/** A refusal whose message carries any of `markers` — the "already / not eligible" family. */
|
|
159
|
-
function refused(reply: Reply, status: number, markers: readonly string[]): boolean {
|
|
160
|
-
if (reply.ok || reply.status !== status) return false;
|
|
161
|
-
const lower = reply.message.toLowerCase();
|
|
162
|
-
return markers.some((marker) => lower.includes(marker));
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/** A business code that means "already claimed" rather than "failed". */
|
|
166
|
-
function coded(reply: Reply, code: number): boolean {
|
|
167
|
-
return !reply.ok && reply.code === code;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function done(reply: Reply, credit?: number | null): BuddyGrowthResult {
|
|
171
|
-
if (reply.ok) {
|
|
172
|
-
return { state: "claimed", error: null, credit: credit ?? creditOf(reply) };
|
|
173
|
-
}
|
|
174
|
-
return { state: "error", error: reply.message || `HTTP ${reply.status}`, credit: null };
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
function creditOf(reply: Reply): number | null {
|
|
178
|
-
if (reply.data === null) return null;
|
|
179
|
-
const raw = reply.data["credit"] ?? reply.data["credit_granted"] ?? reply.data["reward_credit"];
|
|
180
|
-
const value = Number(raw);
|
|
181
|
-
return Number.isFinite(value) ? value : null;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// ---------------------------------------------------------------------------
|
|
185
|
-
// 1. Streak: read, find a missed day, spend a makeup card on it
|
|
186
|
-
// ---------------------------------------------------------------------------
|
|
187
|
-
|
|
188
|
-
const STREAK_PATH = "/activity/growth/streak";
|
|
189
|
-
const HEATMAP_PATH = "/activity/growth/heatmap";
|
|
190
|
-
const MAKEUP_PATH = "/activity/growth/makeup-cards/use";
|
|
191
|
-
|
|
192
|
-
export interface BuddyStreakState {
|
|
193
|
-
readonly days: number;
|
|
194
|
-
readonly makeupCards: number;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/** Reads the streak length and the makeup-card balance (one GET). */
|
|
198
|
-
export async function readBuddyStreak(
|
|
199
|
-
options: BuddyGrowthCall,
|
|
200
|
-
): Promise<BuddyStreakState | null> {
|
|
201
|
-
const reply = await call(options, "GET", STREAK_PATH);
|
|
202
|
-
if (!reply.ok) return null;
|
|
203
|
-
const streak = asRecord(reply.data?.["streak"]);
|
|
204
|
-
const cards = asRecord(reply.data?.["makeup_cards"]);
|
|
205
|
-
return {
|
|
206
|
-
days: Number(streak?.["days"] ?? 0) || 0,
|
|
207
|
-
makeupCards: Number(cards?.["balance"] ?? 0) || 0,
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/** One heatmap cell: a day and its activity score (`0` means missed). */
|
|
212
|
-
export interface BuddyHeatmapCell {
|
|
213
|
-
readonly date: string;
|
|
214
|
-
readonly score: number;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
export async function readBuddyHeatmap(
|
|
218
|
-
options: BuddyGrowthCall,
|
|
219
|
-
): Promise<readonly BuddyHeatmapCell[] | null> {
|
|
220
|
-
const reply = await call(options, "GET", HEATMAP_PATH);
|
|
221
|
-
if (!reply.ok || reply.data === null) return null;
|
|
222
|
-
const list = reply.data["cells"];
|
|
223
|
-
if (!Array.isArray(list)) return null;
|
|
224
|
-
return list.flatMap((entry) => {
|
|
225
|
-
const row = asRecord(entry);
|
|
226
|
-
const date = asText(row?.["date"])?.slice(0, 10);
|
|
227
|
-
if (date === undefined || date.length === 0) return [];
|
|
228
|
-
return [{ date, score: Number(row?.["score"] ?? 0) || 0 }];
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/** `YYYY-MM-DD` for yesterday in CST (+08:00), the upstream's own day boundary. */
|
|
233
|
-
export function cstYesterday(now: Date = new Date()): string {
|
|
234
|
-
const shifted = new Date(now.getTime() + 8 * 3600_000);
|
|
235
|
-
return new Date(shifted.getTime() - 86_400_000).toISOString().slice(0, 10);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Spends a makeup card on a missed day so the streak does not break.
|
|
240
|
-
*
|
|
241
|
-
* A broken streak costs seven days to rebuild, which is far more than a card —
|
|
242
|
-
* so the conditions are checked first (yesterday scored zero, and a card is
|
|
243
|
-
* available) and every other outcome is `skipped` rather than `error`.
|
|
244
|
-
*/
|
|
245
|
-
export async function useBuddyMakeupCard(
|
|
246
|
-
options: BuddyGrowthCall,
|
|
247
|
-
targetDate: string = cstYesterday(),
|
|
248
|
-
): Promise<BuddyGrowthResult> {
|
|
249
|
-
const [heatmap, streak] = await Promise.all([
|
|
250
|
-
readBuddyHeatmap(options),
|
|
251
|
-
readBuddyStreak(options),
|
|
252
|
-
]);
|
|
253
|
-
if (heatmap === null) return { state: "skipped", error: "heatmap unavailable", credit: null };
|
|
254
|
-
const cell = heatmap.find((entry) => entry.date === targetDate);
|
|
255
|
-
// No cell for the day means the heatmap does not cover it — no evidence of a
|
|
256
|
-
// miss, so nothing is spent.
|
|
257
|
-
if (cell === undefined || cell.score !== 0) {
|
|
258
|
-
return { state: "skipped", error: null, credit: null };
|
|
259
|
-
}
|
|
260
|
-
if (streak === null || streak.makeupCards <= 0) {
|
|
261
|
-
return { state: "skipped", error: null, credit: null };
|
|
262
|
-
}
|
|
263
|
-
const reply = await call(options, "POST", MAKEUP_PATH, { target_date: targetDate });
|
|
264
|
-
// No card, no miss, or already made up — all normal.
|
|
265
|
-
if (refused(reply, 400, ["no card", "insufficient", "already", "无卡", "已补"])) {
|
|
266
|
-
return { state: "already", error: null, credit: null };
|
|
267
|
-
}
|
|
268
|
-
return done(reply);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// ---------------------------------------------------------------------------
|
|
272
|
-
// 2. Milestone redeem
|
|
273
|
-
// ---------------------------------------------------------------------------
|
|
274
|
-
|
|
275
|
-
const REDEEM_PATH = "/activity/growth/redeem";
|
|
276
|
-
|
|
277
|
-
/** The three milestone tiers the streak unlocks. */
|
|
278
|
-
export const BUDDY_REDEEM_TIERS = ["7d", "14d", "28d"] as const;
|
|
279
|
-
export type BuddyRedeemTier = (typeof BUDDY_REDEEM_TIERS)[number];
|
|
280
|
-
|
|
281
|
-
/** A tier's own state, as the streak endpoint reports it. */
|
|
282
|
-
export type BuddyTierStatus = "available" | "claimed" | "locked" | "unknown";
|
|
283
|
-
|
|
284
|
-
export interface BuddyRedemptionStatus {
|
|
285
|
-
readonly tiers: Readonly<Record<BuddyRedeemTier, BuddyTierStatus>>;
|
|
286
|
-
readonly remainingDays: number | null;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
const TIER_FIELDS: Readonly<Record<BuddyRedeemTier, string>> = {
|
|
290
|
-
"7d": "tier_7d_status",
|
|
291
|
-
"14d": "tier_14d_status",
|
|
292
|
-
"28d": "tier_28d_status",
|
|
293
|
-
};
|
|
294
|
-
|
|
295
|
-
/** Reads each tier's claim state from the same streak response. */
|
|
296
|
-
export async function readBuddyRedemption(
|
|
297
|
-
options: BuddyGrowthCall,
|
|
298
|
-
): Promise<BuddyRedemptionStatus | null> {
|
|
299
|
-
const reply = await call(options, "GET", STREAK_PATH);
|
|
300
|
-
if (!reply.ok || reply.data === null) return null;
|
|
301
|
-
const status = asRecord(reply.data["redemption_status"]) ?? {};
|
|
302
|
-
const tiers = {} as Record<BuddyRedeemTier, BuddyTierStatus>;
|
|
303
|
-
for (const tier of BUDDY_REDEEM_TIERS) {
|
|
304
|
-
const value = asText(status[TIER_FIELDS[tier]])?.toLowerCase();
|
|
305
|
-
tiers[tier] =
|
|
306
|
-
value === "available" || value === "claimed" || value === "locked" ? value : "unknown";
|
|
307
|
-
}
|
|
308
|
-
const remaining = Number(status["remaining_days"]);
|
|
309
|
-
return { tiers, remainingDays: Number.isFinite(remaining) ? remaining : null };
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
/**
|
|
313
|
-
* Claims one milestone tier.
|
|
314
|
-
*
|
|
315
|
-
* `409 duplicate` and a 403 "days not enough" are both success-equivalent: the
|
|
316
|
-
* first means this month's claim already happened, the second means the tier is
|
|
317
|
-
* not reachable yet. Neither is worth a retry or a log line.
|
|
318
|
-
*/
|
|
319
|
-
export async function redeemBuddyStreakTier(
|
|
320
|
-
options: BuddyGrowthCall,
|
|
321
|
-
tier: BuddyRedeemTier,
|
|
322
|
-
): Promise<BuddyGrowthResult> {
|
|
323
|
-
const reply = await call(options, "POST", REDEEM_PATH, {
|
|
324
|
-
tier,
|
|
325
|
-
client_token: clientToken(options, `redeem-${tier}`),
|
|
326
|
-
});
|
|
327
|
-
if (coded(reply, 409) || refused(reply, 409, ["duplicate", "已领取"])) {
|
|
328
|
-
return { state: "already", error: null, credit: null };
|
|
329
|
-
}
|
|
330
|
-
if (refused(reply, 403, ["not enough", "insufficient", "未达标", "天数不足"])) {
|
|
331
|
-
return { state: "not_eligible", error: null, credit: null };
|
|
332
|
-
}
|
|
333
|
-
return done(reply);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// ---------------------------------------------------------------------------
|
|
337
|
-
// 3. Streak lottery
|
|
338
|
-
// ---------------------------------------------------------------------------
|
|
339
|
-
|
|
340
|
-
// ---------------------------------------------------------------------------
|
|
341
|
-
// 4. Cat travel
|
|
342
|
-
// ---------------------------------------------------------------------------
|
|
343
|
-
|
|
344
|
-
const TRAVEL_STATUS_PATH = "/activity/growth/buddy/travel/status";
|
|
345
|
-
const TRAVEL_CONFIG_PATH = "/activity/growth/buddy/travel/config";
|
|
346
|
-
const TRAVEL_DEPART_PATH = "/activity/growth/buddy/travel/depart";
|
|
347
|
-
const TRAVEL_CLAIM_PATH = "/activity/growth/buddy/travel/claim";
|
|
348
|
-
/**
|
|
349
|
-
* Departure location. The reference fixes 4 ("古镇客栈"): all four locations
|
|
350
|
-
* pay the same range over the same duration, so there is no optimum to chase.
|
|
351
|
-
*/
|
|
352
|
-
const TRAVEL_LOCATION_ID = 4;
|
|
353
|
-
|
|
354
|
-
export type BuddyTravelState = "idle" | "traveling" | "arrived" | "unknown";
|
|
355
|
-
|
|
356
|
-
export interface BuddyTravelStatus {
|
|
357
|
-
readonly state: BuddyTravelState;
|
|
358
|
-
readonly dailyLimitReached: boolean;
|
|
359
|
-
readonly recordId: number | null;
|
|
360
|
-
readonly rewardCredit: number | null;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
export async function readBuddyTravelStatus(
|
|
364
|
-
options: BuddyGrowthCall,
|
|
365
|
-
): Promise<BuddyTravelStatus | null> {
|
|
366
|
-
const reply = await call(options, "GET", TRAVEL_STATUS_PATH);
|
|
367
|
-
if (!reply.ok || reply.data === null) return null;
|
|
368
|
-
const raw = asText(reply.data["state"])?.toLowerCase();
|
|
369
|
-
const state: BuddyTravelState =
|
|
370
|
-
raw === "idle" || raw === "traveling" || raw === "arrived" ? raw : "unknown";
|
|
371
|
-
const record = Number(reply.data["record_id"]);
|
|
372
|
-
const reward = Number(reply.data["reward_credit"]);
|
|
373
|
-
return {
|
|
374
|
-
state,
|
|
375
|
-
dailyLimitReached: reply.data["daily_limit_reached"] === true,
|
|
376
|
-
recordId: Number.isFinite(record) ? record : null,
|
|
377
|
-
rewardCredit: Number.isFinite(reward) ? reward : null,
|
|
378
|
-
};
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
/** The travel configuration; informational, and the depart pre-check. */
|
|
382
|
-
export async function readBuddyTravelConfig(
|
|
383
|
-
options: BuddyGrowthCall,
|
|
384
|
-
): Promise<Record<string, unknown> | null> {
|
|
385
|
-
const reply = await call(options, "GET", TRAVEL_CONFIG_PATH);
|
|
386
|
-
return reply.ok ? reply.data : null;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
export async function departBuddyTravel(
|
|
390
|
-
options: BuddyGrowthCall,
|
|
391
|
-
locationId: number = TRAVEL_LOCATION_ID,
|
|
392
|
-
): Promise<BuddyGrowthResult> {
|
|
393
|
-
const reply = await call(options, "POST", TRAVEL_DEPART_PATH, { location_id: locationId });
|
|
394
|
-
if (refused(reply, 400, ["already traveling", "location not available", "limit"])) {
|
|
395
|
-
return { state: "already", error: null, credit: null };
|
|
396
|
-
}
|
|
397
|
-
return done(reply);
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
/** Claims an arrived cat's reward. `record_id` is mandatory on this call. */
|
|
401
|
-
export async function claimBuddyTravel(
|
|
402
|
-
options: BuddyGrowthCall,
|
|
403
|
-
recordId: number,
|
|
404
|
-
): Promise<BuddyGrowthResult> {
|
|
405
|
-
const reply = await call(options, "POST", TRAVEL_CLAIM_PATH, { record_id: recordId });
|
|
406
|
-
return done(reply);
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
// ---------------------------------------------------------------------------
|
|
410
|
-
// 5. First-buddy adoption
|
|
411
|
-
// ---------------------------------------------------------------------------
|
|
412
|
-
|
|
413
|
-
const BUDDY_INFO_PATH = "/activity/growth/buddy/info";
|
|
414
|
-
const BUDDY_TASKS_PATH = "/v2/activity/growth/tasks";
|
|
415
|
-
const BUDDY_AGREEMENT_PATH = "/activity/growth/buddy/agreement";
|
|
416
|
-
const BUDDY_FIRST_PATH = "/activity/growth/buddy/first";
|
|
417
|
-
|
|
418
|
-
/** The task code that gates adoption. */
|
|
419
|
-
const FIRST_BUDDY_TASK = "first_buddy";
|
|
420
|
-
/** `accept_status` values that count as "the task is live or already accepted". */
|
|
421
|
-
const ACCEPTED_STATUSES: readonly string[] = [
|
|
422
|
-
"not_accepted",
|
|
423
|
-
"accepted",
|
|
424
|
-
"in_progress",
|
|
425
|
-
"completed",
|
|
426
|
-
];
|
|
427
|
-
|
|
428
|
-
/** True when this account already has a cat. */
|
|
429
|
-
export async function readBuddyInfo(
|
|
430
|
-
options: BuddyGrowthCall,
|
|
431
|
-
): Promise<{ readonly hasBuddy: boolean } | null> {
|
|
432
|
-
const reply = await call(options, "GET", BUDDY_INFO_PATH);
|
|
433
|
-
if (!reply.ok || reply.data === null || !("buddy" in reply.data)) return null;
|
|
434
|
-
const buddy = reply.data["buddy"];
|
|
435
|
-
return { hasBuddy: buddy !== null && typeof buddy === "object" };
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
/**
|
|
439
|
-
* Finds the single `first_buddy` task.
|
|
440
|
-
*
|
|
441
|
-
* The reference requires exactly one match and an unlocked, buddy-rewarding
|
|
442
|
-
* task in an accepted state: anything else is not evidence that adoption is
|
|
443
|
-
* available, and adopting blind is a write to an account's growth state.
|
|
444
|
-
*/
|
|
445
|
-
export async function readBuddyFirstTask(
|
|
446
|
-
options: BuddyGrowthCall,
|
|
447
|
-
): Promise<Record<string, unknown> | null> {
|
|
448
|
-
const reply = await call(options, "GET", BUDDY_TASKS_PATH);
|
|
449
|
-
if (!reply.ok || reply.data === null) return null;
|
|
450
|
-
const rows = reply.data["tasks"];
|
|
451
|
-
if (!Array.isArray(rows)) return null;
|
|
452
|
-
const matches = rows.filter(
|
|
453
|
-
(row) => asRecord(row)?.["task_code"] === FIRST_BUDDY_TASK,
|
|
454
|
-
);
|
|
455
|
-
if (matches.length !== 1) return null;
|
|
456
|
-
const task = asRecord(matches[0]);
|
|
457
|
-
if (task === null) return null;
|
|
458
|
-
if (task["locked"] !== false || task["reward_buddy"] !== true) return null;
|
|
459
|
-
const status = asText(task["accept_status"])?.toLowerCase();
|
|
460
|
-
if (status === undefined || !ACCEPTED_STATUSES.includes(status)) return null;
|
|
461
|
-
return task;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
/** Reads the agreement state before accepting it. */
|
|
465
|
-
export async function readBuddyAgreement(
|
|
466
|
-
options: BuddyGrowthCall,
|
|
467
|
-
): Promise<Record<string, unknown> | null> {
|
|
468
|
-
const reply = await call(options, "GET", BUDDY_AGREEMENT_PATH);
|
|
469
|
-
return reply.ok ? reply.data : null;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
/** Accepts the adoption agreement. Idempotent upstream. */
|
|
473
|
-
export async function agreeBuddyAgreement(
|
|
474
|
-
options: BuddyGrowthCall,
|
|
475
|
-
): Promise<BuddyGrowthResult> {
|
|
476
|
-
const reply = await call(options, "POST", BUDDY_AGREEMENT_PATH, { agree: true });
|
|
477
|
-
return done(reply);
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
/**
|
|
481
|
-
* Adopts the first cat.
|
|
482
|
-
*
|
|
483
|
-
* A 400 carrying `first_buddy task not completed yet` is the conversation-count
|
|
484
|
-
* threshold not being met — expected, and a reason to stop trying today rather
|
|
485
|
-
* than a failure.
|
|
486
|
-
*/
|
|
487
|
-
export async function adoptFirstBuddy(
|
|
488
|
-
options: BuddyGrowthCall,
|
|
489
|
-
): Promise<BuddyGrowthResult> {
|
|
490
|
-
const reply = await call(options, "POST", BUDDY_FIRST_PATH, {});
|
|
491
|
-
if (refused(reply, 400, ["first_buddy task not completed", "task not completed"])) {
|
|
492
|
-
return { state: "not_eligible", error: null, credit: null };
|
|
493
|
-
}
|
|
494
|
-
return done(reply);
|
|
495
|
-
}
|
|
496
|
-
|