cartethyia 2.7.3 → 2.7.5
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 +2 -0
- package/README.md +17 -5
- package/dist/dashboard/assets/{BannedPage-2TJD37Hb.js → BannedPage-Cj15GsMp.js} +1 -1
- package/dist/dashboard/assets/BannedPage-Cj15GsMp.js.gz +0 -0
- package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js +1 -0
- package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-xefJNRmJ.js → CliToolDetailPage-DZKmVuJN.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-DZKmVuJN.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-Rj9iHQ3F.js → CliToolsPage-BIccZmeL.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-BIccZmeL.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DEEKkYD-.js +1 -0
- package/dist/dashboard/assets/CombosPage-DEEKkYD-.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js +1 -0
- package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js +656 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-C4w7vea-.js → LoginPage-DkXb6dx-.js} +1 -1
- package/dist/dashboard/assets/LoginPage-DkXb6dx-.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-BtPcAfot.js → ModelPicker-DnQPSIA8.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-DnQPSIA8.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js +1 -0
- package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js +5 -0
- package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js +1 -0
- package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-5SnUSy3j.js +9 -0
- package/dist/dashboard/assets/ProxyPage-5SnUSy3j.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js +1 -0
- package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js +1 -0
- package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-BQS0Rx_u.js → SetupPage-WTa0sktC.js} +1 -1
- package/dist/dashboard/assets/SetupPage-WTa0sktC.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-TP7voAQ0.js → StudioPage-BfAb5-o_.js} +3 -3
- package/dist/dashboard/assets/StudioPage-BfAb5-o_.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js +56 -0
- package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-l29JROPv.js +1 -0
- package/dist/dashboard/assets/api-keys-l29JROPv.js.gz +0 -0
- package/dist/dashboard/assets/app--PJWq8_C.css +2 -0
- package/dist/dashboard/assets/app--PJWq8_C.css.gz +0 -0
- package/dist/dashboard/assets/{assert-C0YYuZJ4.js → assert-C0K-c4eo.js} +1 -1
- package/dist/dashboard/assets/assert-C0K-c4eo.js.gz +0 -0
- package/dist/dashboard/assets/{badge-B_DMpMJN.js → badge-DfbhQU4k.js} +1 -1
- package/dist/dashboard/assets/badge-DfbhQU4k.js.gz +0 -0
- package/dist/dashboard/assets/{banner-DMrMPmth.js → banner-BllT4LcF.js} +1 -1
- package/dist/dashboard/assets/banner-BllT4LcF.js.gz +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/{card-Be5Uzamx.js → card-CtkX66Wd.js} +1 -1
- package/dist/dashboard/assets/card-CtkX66Wd.js.gz +0 -0
- package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js +1 -0
- package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js.gz +0 -0
- package/dist/dashboard/assets/{cli-tools-muFRphDu.js → cli-tools-CQXVMn4P.js} +1 -1
- package/dist/dashboard/assets/cli-tools-CQXVMn4P.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-DOfrVmsi.js → clipboard-button-BQqBQd2n.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-BQqBQd2n.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-C9omdjQt.js → dialog-BEK7KP0H.js} +1 -1
- package/dist/dashboard/assets/dialog-BEK7KP0H.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-Bm8WKaGY.js → drawer-BrTEZ8zu.js} +1 -1
- package/dist/dashboard/assets/drawer-BrTEZ8zu.js.gz +0 -0
- package/dist/dashboard/assets/entry-CHWxbS56.js +1 -0
- package/dist/dashboard/assets/entry-CHWxbS56.js.gz +0 -0
- package/dist/dashboard/assets/{entry-q5HwZ8nN.js → entry-EBHJ3DrE.js} +1 -1
- package/dist/dashboard/assets/entry-EBHJ3DrE.js.gz +0 -0
- package/dist/dashboard/assets/entry-LCS85hiq.js +4 -0
- package/dist/dashboard/assets/entry-LCS85hiq.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-BTScrQpe.js → formatters-BAACA727.js} +1 -1
- package/dist/dashboard/assets/formatters-BAACA727.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-Orjuzemr.js → github-badge-YSCE4UC2.js} +1 -1
- package/dist/dashboard/assets/github-badge-YSCE4UC2.js.gz +0 -0
- package/dist/dashboard/assets/{index-cPrX5vn6.js → index-FQlj0HUT.js} +2 -2
- package/dist/dashboard/assets/index-FQlj0HUT.js.gz +0 -0
- package/dist/dashboard/assets/{inline-Dvux8w-0.js → inline-C4gU5iOL.js} +1 -1
- package/dist/dashboard/assets/inline-C4gU5iOL.js.gz +0 -0
- package/dist/dashboard/assets/{input-CU1Rgf3p.js → input-BHHX_Tmm.js} +1 -1
- package/dist/dashboard/assets/input-BHHX_Tmm.js.gz +0 -0
- package/dist/dashboard/assets/{layout-C_7A6H55.js → layout-CajrYUnD.js} +1 -1
- package/dist/dashboard/assets/{layout-C_7A6H55.js.gz → layout-CajrYUnD.js.gz} +0 -0
- package/dist/dashboard/assets/{list-row-CzFvogbj.js → list-row-Bx2I7XUe.js} +1 -1
- package/dist/dashboard/assets/list-row-Bx2I7XUe.js.gz +0 -0
- package/dist/dashboard/assets/live-DNNqRbj2.js +1 -0
- package/dist/dashboard/assets/live-DNNqRbj2.js.gz +0 -0
- package/dist/dashboard/assets/minimize-2-BTmMTK4o.js +1 -0
- package/dist/dashboard/assets/minimize-2-BTmMTK4o.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-JJGQm8TD.js → mutation-Rr0TIvcj.js} +1 -1
- package/dist/dashboard/assets/mutation-Rr0TIvcj.js.gz +0 -0
- package/dist/dashboard/assets/network-C5UBACrJ.js +1 -0
- package/dist/dashboard/assets/network-C5UBACrJ.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CUWAZK4J.js +2 -0
- package/dist/dashboard/assets/numbers-CUWAZK4J.js.gz +0 -0
- package/dist/dashboard/assets/pagination-Xsi9Qx04.js +1 -0
- package/dist/dashboard/assets/pagination-Xsi9Qx04.js.gz +0 -0
- package/dist/dashboard/assets/{query-keys-CLaNz8Xz.js → query-keys-BloKtaXJ.js} +1 -1
- package/dist/dashboard/assets/query-keys-BloKtaXJ.js.gz +0 -0
- package/dist/dashboard/assets/quota-DvQbD_sr.js +1 -0
- package/dist/dashboard/assets/quota-DvQbD_sr.js.gz +0 -0
- package/dist/dashboard/assets/{routing-BEH32QOC.js → routing-in0f_IfK.js} +1 -1
- package/dist/dashboard/assets/routing-in0f_IfK.js.gz +0 -0
- package/dist/dashboard/assets/{select-B3iTetHN.js → select-BLULNqTp.js} +2 -2
- package/dist/dashboard/assets/select-BLULNqTp.js.gz +0 -0
- package/dist/dashboard/assets/settings-CcGczj6I.js +1 -0
- package/dist/dashboard/assets/settings-CcGczj6I.js.gz +0 -0
- package/dist/dashboard/assets/shield-BMQNxORh.js +1 -0
- package/dist/dashboard/assets/shield-BMQNxORh.js.gz +0 -0
- package/dist/dashboard/assets/{stack-KrOrm7JZ.js → stack-D2lIR0tg.js} +1 -1
- package/dist/dashboard/assets/stack-D2lIR0tg.js.gz +0 -0
- package/dist/dashboard/assets/{state-DLS7IDMy.js → state-DTARjfiW.js} +1 -1
- package/dist/dashboard/assets/state-DTARjfiW.js.gz +0 -0
- package/dist/dashboard/assets/{switch-42ItvbAs.js → switch-c0oPSnqW.js} +1 -1
- package/dist/dashboard/assets/switch-c0oPSnqW.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-Dmnfh1Ue.js → useMutation-m8WGNPrz.js} +1 -1
- package/dist/dashboard/assets/useMutation-m8WGNPrz.js.gz +0 -0
- package/dist/dashboard/console-backgrounds.jpg +0 -0
- package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +59 -57
- package/migrations/full-postgres/0027_qoder_identity_genuine_seed.sql +9 -10
- package/migrations/full-postgres/0032_add_provider_pool_accounts.sql +53 -0
- package/migrations/full-postgres/0033_add_api_key_ban.sql +9 -0
- package/migrations/full-postgres/0034_add_api_key_ban_until.sql +7 -0
- package/migrations/full-postgres/0035_align_pool_account_status_enum.sql +21 -0
- package/migrations/full-postgres/0036_system_prompt_presets_refs.sql +10 -0
- package/migrations/full-postgres/0037_share_concurrency_scope.sql +3 -0
- package/migrations/full-postgres/0038_add_accounts_quota_snapshot.sql +7 -0
- package/migrations/full-postgres/0039_add_telemetry_provider_model_indexes.sql +13 -0
- package/migrations/full-postgres/0040_add_models_dialect.sql +3 -0
- package/migrations/full-postgres/0041_add_daily_checkin_ledger_account_fk.sql +7 -0
- package/migrations/lite-sqlite3/0027_qoder_identity_genuine_seed.sql +9 -10
- package/migrations/lite-sqlite3/0032_add_provider_pool_accounts.sql +44 -0
- package/migrations/lite-sqlite3/0033_add_api_key_ban.sql +8 -0
- package/migrations/lite-sqlite3/0034_add_api_key_ban_until.sql +5 -0
- package/migrations/lite-sqlite3/0035_align_pool_account_status_enum.sql +6 -0
- package/migrations/lite-sqlite3/0036_system_prompt_presets_refs.sql +14 -0
- package/migrations/lite-sqlite3/0037_share_concurrency_scope.sql +3 -0
- package/migrations/lite-sqlite3/0038_add_accounts_quota_snapshot.sql +4 -0
- package/migrations/lite-sqlite3/0039_add_telemetry_provider_model_indexes.sql +6 -0
- package/migrations/lite-sqlite3/0040_add_models_dialect.sql +3 -0
- package/migrations/lite-sqlite3/0041_add_daily_checkin_ledger_account_fk.sql +26 -0
- package/package.json +4 -5
- package/scripts/cli/update.ts +15 -41
- package/src/boot/app.ts +67 -10
- package/src/boot/dependencies.ts +786 -732
- package/src/boot/lifecycle.ts +11 -12
- package/src/config/cooldowns.ts +34 -55
- package/src/config/network.ts +8 -4
- package/src/config/package-paths.ts +1 -1
- package/src/config/providers.ts +13 -14
- package/src/config/resources.ts +0 -5
- package/src/config/runtime.ts +2 -5
- package/src/config/spec.ts +26 -6
- package/src/console/api-keys/ban-constants.ts +38 -0
- package/src/console/api-keys/contracts.ts +92 -31
- package/src/console/api-keys/routes.ts +131 -6
- package/src/console/audit/routes.ts +3 -3
- package/src/console/auth/access.ts +4 -4
- package/src/console/auth/account-reset.ts +5 -5
- package/src/console/auth/audit-recorder.ts +1 -1
- package/src/console/auth/lockout.ts +7 -3
- package/src/console/auth/row-coercion.ts +57 -12
- package/src/console/auth/session-resolver.ts +1 -1
- package/src/console/auth/session-service.ts +8 -2
- package/src/console/auth/session.ts +40 -34
- package/src/console/auth/setup-service.ts +4 -3
- package/src/console/auth/user-store.ts +1 -1
- package/src/console/backup/9router-map.ts +0 -3
- package/src/console/backup/contracts.ts +11 -9
- package/src/console/backup/export-endpoint.ts +11 -12
- package/src/console/backup/nine-router.ts +1 -7
- package/src/console/backup/routes.ts +32 -36
- package/src/console/backup/service.ts +468 -468
- package/src/console/backup/store.ts +47 -29
- package/src/console/backup/validate.ts +11 -1
- package/src/console/cli-tools/contracts.ts +7 -7
- package/src/console/cli-tools/injectors/cline.ts +110 -110
- package/src/console/cli-tools/injectors/codex.ts +181 -181
- package/src/console/cli-tools/injectors/copilot.ts +108 -108
- package/src/console/cli-tools/injectors/driver.ts +247 -247
- package/src/console/cli-tools/injectors/jcode.ts +115 -115
- package/src/console/cli-tools/injectors/openclaw.ts +161 -161
- package/src/console/cli-tools/injectors/opencode.ts +153 -153
- package/src/console/cli-tools/routes.ts +4 -4
- package/src/console/cli-tools/service.ts +1 -1
- package/src/console/cli-tools/store.ts +2 -2
- package/src/console/console-router-guards.ts +2 -2
- package/src/console/console-router.ts +36 -4
- package/src/console/dashboard-assets.ts +9 -9
- package/src/console/data-management/routes.ts +3 -3
- package/src/console/domain-registration.ts +18 -4
- package/src/console/inferhub/client.ts +0 -7
- package/src/console/inferhub/key-store.ts +1 -1
- package/src/console/inferhub/normalize.ts +2 -2
- package/src/console/inferhub/routes.ts +1 -1
- package/src/console/inferhub/types.ts +1 -1
- package/src/console/observability/contracts.ts +5 -1
- package/src/console/observability/live.ts +34 -21
- package/src/console/observability/logs.ts +4 -4
- package/src/console/observability/performance.ts +3 -3
- package/src/console/observability/routes.ts +3 -3
- package/src/console/observability/sse.ts +62 -8
- package/src/console/observability/store.ts +36 -20
- package/src/console/providers/catalog/catalog-projections.ts +1 -1
- package/src/console/providers/catalog/contracts.ts +82 -22
- package/src/console/providers/catalog/model-operations.ts +1 -1
- package/src/console/providers/catalog/provider-operations.ts +110 -17
- package/src/console/providers/catalog/public-model-store.ts +0 -0
- package/src/console/providers/catalog/routes.ts +30 -6
- package/src/console/providers/catalog/store-helpers.ts +349 -326
- package/src/console/providers/catalog/store.ts +1662 -1292
- package/src/console/providers/detail/routes.ts +4 -4
- package/src/console/providers/oauth/callback-listener.ts +100 -18
- package/src/console/providers/oauth/routes.ts +95 -16
- package/src/console/quota/account-quota-global.ts +41 -3
- package/src/console/quota/account-quota-shared.ts +10 -3
- package/src/console/quota/account-quota-tenant.ts +285 -67
- package/src/console/quota/account-test-egress.ts +4 -4
- package/src/console/quota/daily-growth-job.ts +308 -0
- package/src/console/quota/native-identity.ts +2 -2
- package/src/console/quota/quota-view.ts +1 -1
- package/src/console/remote-support/routes.ts +1 -1
- package/src/console/remote-support/service.ts +2 -2
- package/src/console/routing/model/contracts.ts +1 -1
- package/src/console/routing/model/routes.ts +3 -3
- package/src/console/routing/pools/contracts.ts +13 -10
- package/src/console/routing/pools/import-parse.ts +3 -3
- package/src/console/routing/pools/operations.ts +14 -7
- package/src/console/routing/pools/probe-verdict.ts +8 -3
- package/src/console/routing/pools/relay-deploy.ts +12 -12
- package/src/console/routing/pools/routes.ts +3 -3
- package/src/console/routing/pools/store.ts +21 -1
- package/src/console/settings/contracts.ts +29 -2
- package/src/console/settings/routes.ts +3 -3
- package/src/console/settings/store.ts +20 -0
- package/src/console/share/share-router.ts +112 -44
- package/src/console/share/share-stats.ts +109 -39
- package/src/console/share/share-usage.ts +1 -1
- package/src/console/shared/errors.ts +2 -2
- package/src/console/shared/object.ts +18 -0
- package/src/console/shared/read-through-cache.ts +42 -6
- package/src/console/shared/transaction.ts +14 -0
- package/src/console/system-prompt/contracts.ts +89 -12
- package/src/console/system-prompt/routes.ts +98 -45
- package/src/console/updates/apply-update.ts +244 -0
- package/src/console/updates/routes.ts +256 -98
- package/src/main.ts +11 -5
- package/src/network/http2-bidi.ts +1 -1
- package/src/network/ip-utils.ts +298 -0
- package/src/network/outbound-fetch.ts +76 -5
- package/src/network/pool/agent.ts +7 -6
- package/src/network/pool/byte-accounting.ts +1 -1
- package/src/network/pool/resolver.ts +55 -51
- package/src/network/pool/selector.ts +150 -136
- package/src/network/pool-cooldown-store.ts +71 -16
- package/src/network/pool-health-machine.ts +22 -2
- package/src/network/rate-limit-verdict.ts +68 -0
- package/src/network/retry.ts +1 -1
- package/src/network/ssrf.ts +24 -75
- package/src/observability/event-log.ts +2 -2
- package/src/observability/event-query.ts +286 -214
- package/src/observability/log-sanitize.ts +1 -1
- package/src/observability/metrics.ts +4 -4
- package/src/observability/payload-capture.ts +6 -5
- package/src/observability/payload-store.ts +1 -1
- package/src/observability/runtime-metrics.ts +1 -1
- package/src/observability/telemetry-buffer.ts +12 -3
- package/src/observability/telemetry-reconciliation.ts +3 -3
- package/src/observability/telemetry-status.ts +1 -1
- package/src/observability/token-speed.ts +1 -1
- package/src/persistence/connection-url.ts +1 -1
- package/src/persistence/db-handle.ts +1 -1
- package/src/persistence/db-pg.ts +2 -2
- package/src/persistence/db-sqlite.ts +10 -5
- package/src/persistence/dialect.ts +5 -1
- package/src/persistence/readiness.ts +19 -7
- package/src/persistence/redis.ts +1 -1
- package/src/persistence/schema.ts +177 -45
- package/src/persistence/shared-cache.ts +2 -2
- package/src/persistence/sort-order.ts +1 -1
- package/src/persistence/stores/api-key-store.ts +109 -4
- package/src/persistence/stores/share-store.ts +20 -8
- package/src/persistence/stores/telemetry-store.ts +1 -1
- package/src/persistence/system-prompt-store.ts +63 -18
- package/src/persistence/tenant-preferences.ts +1 -1
- package/src/persistence/ttl-cache.ts +3 -3
- package/src/persistence/usage-flush.ts +7 -2
- package/src/protocol/canonical-model.ts +1 -1
- package/src/protocol/codecs.ts +12 -16
- package/src/protocol/dsml.ts +3 -49
- package/src/protocol/gateway-error.ts +6 -3
- package/src/protocol/image-clamp.ts +2 -2
- package/src/protocol/image-generation.ts +2 -2
- package/src/protocol/reasoning-text.ts +1 -1
- package/src/protocol/reasoning.ts +7 -52
- package/src/protocol/registry.ts +50 -15
- package/src/protocol/request/chat.ts +46 -19
- package/src/protocol/request/codex.ts +1 -1
- package/src/protocol/request/messages.ts +46 -14
- package/src/protocol/request/responses.ts +19 -10
- package/src/protocol/response/chat.ts +27 -12
- package/src/protocol/response/codex.ts +2 -2
- package/src/protocol/response/gemini.ts +1 -1
- package/src/protocol/response/messages.ts +6 -6
- package/src/protocol/response/omp-tool-call-markup.ts +1 -1
- package/src/protocol/response/responses.ts +3 -3
- package/src/protocol/sse-decoder.ts +64 -4
- package/src/protocol/stop-reason.ts +2 -2
- package/src/protocol/think-split.ts +347 -0
- package/src/protocol/translation/attachment-integrity.ts +3 -3
- package/src/protocol/translation/cache-controls.ts +8 -20
- package/src/protocol/translation/capabilities.ts +8 -8
- package/src/protocol/translation/dialect/deepseek.ts +10 -0
- package/src/protocol/translation/dialect/inband.ts +39 -0
- package/src/protocol/translation/dialect/model-thinking.ts +89 -0
- package/src/protocol/translation/dialect/qwen.ts +114 -0
- package/src/protocol/translation/dialect/resolver.ts +83 -0
- package/src/protocol/translation/dialect/rules.ts +93 -0
- package/src/protocol/translation/dialect/thinking-ladder.ts +245 -0
- package/src/protocol/translation/dialect/types.ts +58 -0
- package/src/protocol/translation/quirks.ts +18 -4
- package/src/protocol/translation/thinking.ts +12 -252
- package/src/protocol/translation/tool-id.ts +3 -3
- package/src/protocol/translation/tool-repair.ts +1 -1
- package/src/protocol/upstream/post-json.ts +6 -1
- package/src/protocol/upstream/send-claude-messages.ts +3 -3
- package/src/providers/adapters/base-adapter.ts +1 -1
- package/src/providers/adapters/compatible-adapter.ts +44 -5
- package/src/providers/authentication/jwt-validator.ts +0 -5
- package/src/providers/authentication/oauth-client.ts +254 -253
- package/src/providers/authentication/oauth-flow-store.ts +42 -7
- package/src/providers/authentication/oauth-refresh-service.ts +2 -2
- package/src/providers/bundled-discovery-providers.ts +6 -30
- package/src/providers/configured-openai-providers.ts +82 -97
- package/src/providers/configured-provider.ts +1 -1
- package/src/providers/default-registry.ts +23 -35
- package/src/providers/discovery/catalog/catalog-format.ts +13 -2
- package/src/providers/discovery/discovery-types.ts +0 -1
- package/src/providers/discovery/openai-model-discovery.ts +2 -2
- package/src/providers/discovery/probe-phases.ts +50 -15
- package/src/providers/discovery/probing-service.ts +18 -20
- package/src/providers/integrations/agentrouter/agentrouter.ts +14 -5
- package/src/providers/integrations/alibabacp/alibabacp.ts +2 -2
- package/src/providers/integrations/anthropic.ts +4 -11
- package/src/providers/integrations/antigravity/antigravity-oauth.ts +0 -1
- package/src/providers/integrations/antigravity/antigravity-protocol.ts +0 -14
- package/src/providers/integrations/antigravity/antigravity.ts +15 -26
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +2 -2
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +20 -4
- package/src/providers/integrations/buddy/buddy-cli-headers.ts +19 -52
- package/src/providers/integrations/buddy/buddy-discovery-shared.ts +37 -6
- package/src/providers/integrations/buddy/buddy-identity.ts +108 -1
- package/src/providers/integrations/buddy/buddy-media.ts +16 -16
- package/src/providers/integrations/buddy/buddy-oauth-shared.ts +1 -1
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +1 -2
- package/src/providers/integrations/buddy/codebuddy/provider.ts +5 -16
- package/src/providers/integrations/buddy/codebuddy/shared.ts +30 -9
- package/src/providers/integrations/buddy/workbuddy/provider.ts +6 -10
- package/src/providers/integrations/buddy/workbuddy/shared.ts +21 -3
- package/src/providers/integrations/claude/claude-betas.ts +1 -1
- package/src/providers/integrations/claude/claude-cch.ts +148 -148
- package/src/providers/integrations/claude/claude-credentials.ts +1 -1
- package/src/providers/integrations/claude/claude-slow-lane-retry.ts +1 -1
- package/src/providers/integrations/claude/claude.ts +3 -10
- package/src/providers/integrations/claude/fallback-credit.ts +3 -7
- package/src/providers/integrations/cline/cline.ts +2 -2
- package/src/providers/integrations/cloudflareai/cloudflareai.ts +4 -7
- package/src/providers/integrations/codex/codex-device-code.ts +101 -101
- package/src/providers/integrations/codex/codex-errors.ts +1 -1
- package/src/providers/integrations/codex/codex-oauth.ts +0 -1
- package/src/providers/integrations/codex/codex.ts +14 -15
- package/src/providers/integrations/commandcode/commandcode.ts +76 -116
- package/src/providers/integrations/commandcode/quota.ts +132 -48
- package/src/providers/integrations/cursor/cursor-http2.ts +11 -14
- package/src/providers/integrations/cursor/cursor-quota.ts +2 -6
- package/src/providers/integrations/cursor/cursor-request.ts +0 -87
- package/src/providers/integrations/cursor/cursor-shared.ts +0 -4
- package/src/providers/integrations/cursor/cursor.ts +53 -20
- package/src/providers/integrations/cursor/protocol/framing.ts +0 -56
- package/src/providers/integrations/cursor/protocol/messages.ts +0 -96
- package/src/providers/integrations/factory/factory.ts +13 -13
- package/src/providers/integrations/gemini.ts +11 -11
- package/src/providers/integrations/github/github-discovery.ts +55 -10
- package/src/providers/integrations/github/github-oauth.ts +6 -5
- package/src/providers/integrations/github/github.ts +47 -20
- package/src/providers/integrations/grok/grok-oauth.ts +261 -261
- package/src/providers/integrations/grok/grok-quota-frame.ts +1 -1
- package/src/providers/integrations/grok/grok-quota.ts +16 -7
- package/src/providers/integrations/grok/grok-ratelimit-store.ts +0 -5
- package/src/providers/integrations/grok/grok-ratelimit.ts +3 -0
- package/src/providers/integrations/grok/grok.ts +88 -42
- package/src/providers/integrations/inferhub.ts +4 -11
- package/src/providers/integrations/kilo/kilo-oauth.ts +1 -1
- package/src/providers/integrations/kimi/kimi.ts +4 -11
- package/src/providers/integrations/kiro/kiro-cache.ts +31 -0
- package/src/providers/integrations/kiro/kiro-catalog.ts +31 -5
- package/src/providers/integrations/kiro/kiro-discovery.ts +47 -43
- package/src/providers/integrations/kiro/kiro-machine-id.ts +6 -3
- package/src/providers/integrations/kiro/kiro-oauth.ts +523 -255
- package/src/providers/integrations/kiro/kiro-quota.ts +35 -47
- package/src/providers/integrations/kiro/kiro-request.ts +8 -11
- package/src/providers/integrations/kiro/kiro-stream.ts +13 -0
- package/src/providers/integrations/kiro/kiro.ts +91 -78
- package/src/providers/integrations/muse/muse-oauth.ts +0 -1
- package/src/providers/integrations/muse/muse-quota.ts +0 -4
- package/src/providers/integrations/muse/muse.ts +6 -9
- package/src/providers/integrations/opencode/fingerprint.ts +7 -5
- package/src/providers/integrations/opencode/provider.ts +17 -24
- package/src/providers/integrations/perplexity.ts +2 -2
- package/src/providers/integrations/qoder/qoder-catalog.ts +132 -17
- package/src/providers/integrations/qoder/qoder-checkin.ts +26 -8
- package/src/providers/integrations/qoder/qoder-native-identity-store.ts +9 -8
- package/src/providers/integrations/qoder/qoder-oauth.ts +165 -25
- package/src/providers/integrations/qoder/qoder-quota.ts +48 -6
- package/src/providers/integrations/qoder/qoder-realm.ts +22 -28
- package/src/providers/integrations/qoder/qoder-sse.ts +159 -0
- package/src/providers/integrations/qoder/qoder-umid.ts +33 -38
- package/src/providers/integrations/qoder/qoder-virtual.ts +14 -2
- package/src/providers/integrations/qoder/qoder.ts +521 -63
- package/src/providers/integrations/shared/connect.ts +0 -54
- package/src/providers/integrations/shared/hash.ts +15 -0
- package/src/providers/integrations/shared/session-cache.ts +136 -0
- package/src/providers/integrations/tokenharbor.ts +2 -2
- package/src/providers/integrations/trae/trae-cache.ts +7 -87
- package/src/providers/integrations/trae/trae-catalog.ts +1 -6
- package/src/providers/integrations/trae/trae-protocol.ts +17 -8
- package/src/providers/integrations/trae/trae-realm.ts +0 -7
- package/src/providers/integrations/trae/trae.ts +59 -19
- package/src/providers/integrations/xai/xai-oauth.ts +5 -5
- package/src/providers/integrations/zcode/oauth.ts +1 -1
- package/src/providers/integrations/zcode-start-plan/gifts.ts +265 -0
- package/src/providers/integrations/zcode-start-plan/oauth.ts +154 -0
- package/src/providers/integrations/zcode-start-plan/provider.ts +131 -0
- package/src/providers/integrations/zcode-start-plan/quota.ts +152 -0
- package/src/providers/integrations/zcode-start-plan/zcode_prompt.json +10 -0
- package/src/providers/operations/account-error-classification.ts +131 -116
- package/src/providers/operations/account-health-service.ts +31 -39
- package/src/providers/operations/account-reset-service.ts +58 -1
- package/src/providers/operations/cli-platform.ts +31 -5
- package/src/providers/operations/client-version-resolver.ts +4 -6
- package/src/providers/operations/client-versions.ts +10 -7
- package/src/providers/operations/provider-catalog-seeder.ts +2 -0
- package/src/providers/operations/provider-catalog-service.ts +22 -6
- package/src/providers/operations/provider-credential-service.ts +35 -6
- package/src/providers/operations/upstream-deadline.ts +3 -3
- package/src/providers/provider-contracts.ts +65 -10
- package/src/providers/provider-credential-resolver.ts +1 -1
- package/src/providers/provider-metadata.ts +17 -2
- package/src/providers/provider-model-definition.ts +22 -1
- package/src/providers/provider-probe.ts +4 -2
- package/src/providers/provider-registry.ts +4 -4
- package/src/providers/provider-usage.ts +81 -16
- package/src/providers/quota/cache.ts +29 -0
- package/src/providers/quota/quota-contracts.ts +28 -10
- package/src/providers/quota/refresh.ts +29 -6
- package/src/providers/quota/snapshot.ts +94 -0
- package/src/providers/search/search-provider.ts +6 -8
- package/src/providers/search/search-providers.ts +2 -1
- package/src/security/abuse.ts +8 -8
- package/src/security/admission/reasons.ts +17 -17
- package/src/security/admission/service.ts +2 -2
- package/src/security/admission/ttl.ts +61 -61
- package/src/security/api-key-auth.ts +20 -8
- package/src/security/client-router-fingerprint.ts +4 -4
- package/src/security/credential-codec.ts +3 -3
- package/src/security/csrf.ts +2 -2
- package/src/security/ip-boundary.ts +16 -189
- package/src/security/model-access-rule.ts +1 -1
- package/src/security/outbound-headers.ts +18 -0
- package/src/shared/coerce.ts +19 -0
- package/src/shared/invoke-guard.ts +47 -0
- package/src/transport/contracts.ts +13 -4
- package/src/transport/dispatch/attempt-finalize.ts +5 -4
- package/src/transport/dispatch/attempt-loop.ts +10 -7
- package/src/transport/dispatch/capture-policy.ts +34 -37
- package/src/transport/dispatch/empty-stream.ts +28 -25
- package/src/transport/dispatch/generation.ts +33 -53
- package/src/transport/dispatch/leases.ts +8 -8
- package/src/transport/dispatch/native-shared.ts +127 -0
- package/src/transport/dispatch/proxy-request.ts +10 -8
- package/src/transport/dispatch/responses-compact.ts +35 -56
- package/src/transport/dispatch/retry-policy.ts +4 -67
- package/src/transport/dispatch/stream-bridge.ts +1 -1
- package/src/transport/dispatch/streaming/encoder.ts +18 -72
- package/src/transport/dispatch/streaming/finalize.ts +11 -10
- package/src/transport/dispatch/streaming/stream.ts +7 -3
- package/src/transport/dispatch/streaming/thinking-loop.ts +1 -1
- package/src/transport/dispatch/systemone.ts +27 -55
- package/src/transport/dispatch/token-count.ts +49 -46
- package/src/transport/dispatch/websearch.ts +24 -50
- package/src/transport/drain-endpoint.ts +3 -3
- package/src/transport/failure-policy.ts +19 -6
- package/src/transport/guards/auth-guard.ts +6 -11
- package/src/transport/guards/body-policy.ts +7 -7
- package/src/transport/guards/error-lifecycle.ts +18 -3
- package/src/transport/guards/guard-errors.ts +7 -3
- package/src/transport/guards/headroom-guard.ts +2 -2
- package/src/transport/guards/pipeline.ts +7 -7
- package/src/transport/guards/readiness-guard.ts +16 -2
- package/src/transport/guards/request-context.ts +15 -20
- package/src/transport/request/preparer.ts +306 -27
- package/src/transport/request/rtk/compress-request.ts +3 -3
- package/src/transport/request/rtk/rtk-constants.ts +2 -2
- package/src/transport/request/rtk/rtk-filters.ts +8 -8
- package/src/transport/request/state.ts +5 -20
- package/src/transport/request/system-prompt-injection.ts +74 -11
- package/src/transport/request/tenant-preferences.ts +22 -0
- package/src/transport/routing/candidate-index.ts +2 -2
- package/src/transport/routing/round-robin.ts +1 -1
- package/src/transport/routing/route-catalog.ts +113 -19
- package/src/transport/routing/route-model.ts +131 -38
- package/src/transport/routing/router.ts +154 -39
- package/src/transport/shutdown-notice.ts +21 -9
- package/src/transport/surface/adapters.ts +3 -3
- package/src/transport/surface/chat/encode.ts +7 -7
- package/src/transport/surface/chat/parse.ts +7 -7
- package/src/transport/surface/completion.ts +3 -3
- package/src/transport/surface/content-parts.ts +1 -1
- package/src/transport/surface/dialects.ts +2 -2
- package/src/transport/surface/messages/encode.ts +1 -1
- package/src/transport/surface/messages/parse.ts +3 -3
- package/src/transport/surface/responses/adapter.ts +1 -1
- package/src/transport/surface/responses/errors.ts +0 -9
- package/src/transport/vision-handoff-cache.ts +58 -0
- package/src/transport/vision-handoff.ts +376 -0
- package/src/workers/account-identity-sweep.ts +2 -2
- package/src/workers/buddy-growth-tasks.ts +392 -346
- package/src/workers/daily-checkin.ts +102 -49
- package/src/workers/maintenance-lease.ts +2 -35
- package/src/workers/oauth-refresh-sweep.ts +8 -1
- package/src/workers/quota-refresh-sweep.ts +120 -82
- package/src/workers/scheduler.ts +67 -4
- package/src/workers/sweep-runner.ts +24 -5
- package/src/workers/zcode-gift-claim.ts +108 -0
- package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js +0 -1
- package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js.gz +0 -0
- package/dist/dashboard/assets/BannedPage-2TJD37Hb.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-xefJNRmJ.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-Rj9iHQ3F.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-JEbYJFxw.js +0 -1
- package/dist/dashboard/assets/CombosPage-JEbYJFxw.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-C3xx_inM.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-C3xx_inM.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-Dw306gJU.js +0 -1
- package/dist/dashboard/assets/CustomizationPage-Dw306gJU.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-jaBo1TWV.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-jaBo1TWV.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-C4w7vea-.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-BtPcAfot.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-CGC5gNs1.js +0 -1
- package/dist/dashboard/assets/OverviewPage-CGC5gNs1.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-DQcthNGt.js +0 -4
- package/dist/dashboard/assets/ProviderDetailPage-DQcthNGt.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js +0 -1
- package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-0OsXRGXe.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-0OsXRGXe.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-DdPc8slb.js +0 -9
- package/dist/dashboard/assets/ProxyPage-DdPc8slb.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-Ci5O4BnH.js +0 -1
- package/dist/dashboard/assets/QuotaPage-Ci5O4BnH.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CDZjTt87.js +0 -1
- package/dist/dashboard/assets/SettingsPage-CDZjTt87.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-BQS0Rx_u.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-BAL5npBC.js +0 -1
- package/dist/dashboard/assets/SortableList-BAL5npBC.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-TP7voAQ0.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-Cj_fEzhg.js +0 -56
- package/dist/dashboard/assets/UsagePage-Cj_fEzhg.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-MbI7e7rt.js +0 -1
- package/dist/dashboard/assets/api-keys-MbI7e7rt.js.gz +0 -0
- package/dist/dashboard/assets/app-B2vyPoII.css +0 -2
- package/dist/dashboard/assets/app-B2vyPoII.css.gz +0 -0
- package/dist/dashboard/assets/assert-C0YYuZJ4.js.gz +0 -0
- package/dist/dashboard/assets/badge-B_DMpMJN.js.gz +0 -0
- package/dist/dashboard/assets/banner-DMrMPmth.js.gz +0 -0
- package/dist/dashboard/assets/card-Be5Uzamx.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-muFRphDu.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-DOfrVmsi.js.gz +0 -0
- package/dist/dashboard/assets/dialog-C9omdjQt.js.gz +0 -0
- package/dist/dashboard/assets/drawer-Bm8WKaGY.js.gz +0 -0
- package/dist/dashboard/assets/entry-DS90gUks.js +0 -1
- package/dist/dashboard/assets/entry-DS90gUks.js.gz +0 -0
- package/dist/dashboard/assets/entry-j40LJotO.js +0 -4
- package/dist/dashboard/assets/entry-j40LJotO.js.gz +0 -0
- package/dist/dashboard/assets/entry-q5HwZ8nN.js.gz +0 -0
- package/dist/dashboard/assets/formatters-BTScrQpe.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-Orjuzemr.js.gz +0 -0
- package/dist/dashboard/assets/index-cPrX5vn6.js.gz +0 -0
- package/dist/dashboard/assets/inline-Dvux8w-0.js.gz +0 -0
- package/dist/dashboard/assets/input-CU1Rgf3p.js.gz +0 -0
- package/dist/dashboard/assets/list-row-CzFvogbj.js.gz +0 -0
- package/dist/dashboard/assets/live-KaG15HLQ.js +0 -1
- package/dist/dashboard/assets/live-KaG15HLQ.js.gz +0 -0
- package/dist/dashboard/assets/minimize-2-D-MN8W8a.js +0 -1
- package/dist/dashboard/assets/minimize-2-D-MN8W8a.js.gz +0 -0
- package/dist/dashboard/assets/mutation-JJGQm8TD.js.gz +0 -0
- package/dist/dashboard/assets/numbers-BNDZnsUE.js +0 -2
- package/dist/dashboard/assets/numbers-BNDZnsUE.js.gz +0 -0
- package/dist/dashboard/assets/query-keys-CLaNz8Xz.js.gz +0 -0
- package/dist/dashboard/assets/quota-DZGyp0N5.js +0 -1
- package/dist/dashboard/assets/quota-DZGyp0N5.js.gz +0 -0
- package/dist/dashboard/assets/routing-BEH32QOC.js.gz +0 -0
- package/dist/dashboard/assets/select-B3iTetHN.js.gz +0 -0
- package/dist/dashboard/assets/settings-Dt3Eqbnq.js +0 -1
- package/dist/dashboard/assets/settings-Dt3Eqbnq.js.gz +0 -0
- package/dist/dashboard/assets/stack-KrOrm7JZ.js.gz +0 -0
- package/dist/dashboard/assets/state-DLS7IDMy.js.gz +0 -0
- package/dist/dashboard/assets/stream-retry-5QnVuSSo.js +0 -1
- package/dist/dashboard/assets/stream-retry-5QnVuSSo.js.gz +0 -0
- package/dist/dashboard/assets/switch-42ItvbAs.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-Dmnfh1Ue.js.gz +0 -0
- package/src/network/response-headers.ts +0 -39
- package/src/protocol/response/dsml.ts +0 -38
- package/src/providers/integrations/xiaomi-mimo/think-stream.ts +0 -229
- /package/dist/dashboard/assets/{app-wvwCi4Eh.js → app-Bgd3zANQ.js} +0 -0
- /package/dist/dashboard/assets/{app-wvwCi4Eh.js.gz → app-Bgd3zANQ.js.gz} +0 -0
- /package/dist/dashboard/assets/brand/{og_bansos.webp → og_bansos_initial.webp} +0 -0
- /package/dist/dashboard/assets/brand/{og_image.webp → og_image_initial.webp} +0 -0
|
@@ -36,7 +36,7 @@ export const RELAY_TARGETS: readonly RelayTarget[] = ["cloudflare", "vercel", "d
|
|
|
36
36
|
* The same handler body runs on Workers and Vercel Edge (`export default {
|
|
37
37
|
* fetch }`); the Deno entry wraps it in `Deno.serve`.
|
|
38
38
|
*/
|
|
39
|
-
|
|
39
|
+
const RELAY_WORKER_SOURCE = `// Cartethyia hosted relay.
|
|
40
40
|
//
|
|
41
41
|
// Forwards a request to the origin named by the x-relay-target header, at the
|
|
42
42
|
// path named by x-relay-path. Deployed to Cloudflare Workers / Vercel Edge.
|
|
@@ -83,7 +83,7 @@ export default handler;
|
|
|
83
83
|
`;
|
|
84
84
|
|
|
85
85
|
/** A Deno Deploy entry wraps the shared handler in `Deno.serve`. */
|
|
86
|
-
|
|
86
|
+
const RELAY_DENO_SOURCE = `${RELAY_WORKER_SOURCE}
|
|
87
87
|
Deno.serve((request) => handler.fetch(request));
|
|
88
88
|
`;
|
|
89
89
|
|
|
@@ -111,7 +111,7 @@ export interface RelayDeployResult {
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
/** Outbound fetch seam (the validated fetch), so tests can stub deploys. */
|
|
114
|
-
|
|
114
|
+
type RelayFetch = (url: string, init: RequestInit) => Promise<Response>;
|
|
115
115
|
|
|
116
116
|
function generateName(prefix: string): string {
|
|
117
117
|
return `${prefix}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
|
|
@@ -151,7 +151,7 @@ async function deployCloudflare(
|
|
|
151
151
|
request: RelayDeployRequest,
|
|
152
152
|
): Promise<RelayDeployResult> {
|
|
153
153
|
const accountId = request.accountId?.trim();
|
|
154
|
-
if (!accountId) throw new GatewayError("invalid_request", 400, "Cloudflare account id is required");
|
|
154
|
+
if (!accountId) throw new GatewayError("invalid_request", 400, "Cloudflare account id is required (request {requestId}). Check the shape of what you sent — then resend 🪞");
|
|
155
155
|
const projectName = request.projectName?.trim() || generateName("relay");
|
|
156
156
|
const scriptUrl = `https://api.cloudflare.com/client/v4/accounts/${encodeURIComponent(accountId)}/workers/scripts/${encodeURIComponent(projectName)}`;
|
|
157
157
|
const form = new FormData();
|
|
@@ -174,7 +174,7 @@ async function deployCloudflare(
|
|
|
174
174
|
body: form,
|
|
175
175
|
});
|
|
176
176
|
if (!upload.ok)
|
|
177
|
-
throw new GatewayError("platform_unavailable", upload.status, `Cloudflare deploy failed: ${await readError(upload)}
|
|
177
|
+
throw new GatewayError("platform_unavailable", upload.status, `Cloudflare deploy failed (request {requestId}): ${await readError(upload)}. Flip the switch back on when ready 🔌`);
|
|
178
178
|
// Enable the workers.dev route; a failure here is non-fatal because the
|
|
179
179
|
// subdomain lookup below decides the final URL and reports its own error.
|
|
180
180
|
await fetchImpl(`${scriptUrl}/subdomain`, {
|
|
@@ -190,7 +190,7 @@ async function deployCloudflare(
|
|
|
190
190
|
throw new GatewayError(
|
|
191
191
|
"platform_unavailable",
|
|
192
192
|
subdomainRes.status,
|
|
193
|
-
`Worker uploaded but its workers.dev subdomain could not be read: ${await readError(subdomainRes)}
|
|
193
|
+
`Worker uploaded but its workers.dev subdomain could not be read (request {requestId}): ${await readError(subdomainRes)}. Flip the switch back on when ready 🔌`,
|
|
194
194
|
);
|
|
195
195
|
const subdomainData = (await subdomainRes.json().catch(() => ({}))) as {
|
|
196
196
|
result?: { subdomain?: string };
|
|
@@ -200,7 +200,7 @@ async function deployCloudflare(
|
|
|
200
200
|
throw new GatewayError(
|
|
201
201
|
"platform_unavailable",
|
|
202
202
|
502,
|
|
203
|
-
"Worker uploaded but no workers.dev subdomain is configured for this Cloudflare account.",
|
|
203
|
+
"Worker uploaded but no workers.dev subdomain is configured for this Cloudflare account (request {requestId}). Flip the switch back on when ready 🔌",
|
|
204
204
|
);
|
|
205
205
|
return { target: "cloudflare", projectName, relayUrl: `https://${projectName}.${subdomain}.workers.dev` };
|
|
206
206
|
}
|
|
@@ -251,7 +251,7 @@ async function deployVercel(
|
|
|
251
251
|
}),
|
|
252
252
|
});
|
|
253
253
|
if (!deployRes.ok)
|
|
254
|
-
throw new GatewayError("platform_unavailable", deployRes.status, `Vercel deploy failed: ${await readError(deployRes)}
|
|
254
|
+
throw new GatewayError("platform_unavailable", deployRes.status, `Vercel deploy failed (request {requestId}): ${await readError(deployRes)}. Flip the switch back on when ready 🔌`);
|
|
255
255
|
// Pin the region again after the deploy created the project: the PATCH above
|
|
256
256
|
// is a no-op on first deploy (the project does not exist yet), and a redeploy
|
|
257
257
|
// is what Vercel requires for a region change to take effect anyway. The SSO
|
|
@@ -280,7 +280,7 @@ async function deployVercel(
|
|
|
280
280
|
}).catch(() => undefined);
|
|
281
281
|
const deployment = (await deployRes.json().catch(() => ({}))) as { url?: string };
|
|
282
282
|
if (!deployment.url)
|
|
283
|
-
throw new GatewayError("platform_unavailable", 502, "Vercel deploy returned no URL");
|
|
283
|
+
throw new GatewayError("platform_unavailable", 502, "Vercel deploy returned no URL (request {requestId}). Flip the switch back on when ready 🔌");
|
|
284
284
|
return {
|
|
285
285
|
target: "vercel",
|
|
286
286
|
projectName,
|
|
@@ -300,11 +300,11 @@ async function deployDeno(
|
|
|
300
300
|
body: JSON.stringify({ project: projectName, entryPoint: "main.ts", files: { "main.ts": RELAY_DENO_SOURCE } }),
|
|
301
301
|
});
|
|
302
302
|
if (!deployRes.ok)
|
|
303
|
-
throw new GatewayError("platform_unavailable", deployRes.status, `Deno deploy failed: ${await readError(deployRes)}
|
|
303
|
+
throw new GatewayError("platform_unavailable", deployRes.status, `Deno deploy failed (request {requestId}): ${await readError(deployRes)}. Flip the switch back on when ready 🔌`);
|
|
304
304
|
const deployment = (await deployRes.json().catch(() => ({}))) as { url?: string; domains?: string[] };
|
|
305
305
|
const url = deployment.url ?? deployment.domains?.[0];
|
|
306
306
|
if (!url)
|
|
307
|
-
throw new GatewayError("platform_unavailable", 502, "Deno deploy returned no URL");
|
|
307
|
+
throw new GatewayError("platform_unavailable", 502, "Deno deploy returned no URL (request {requestId}). Flip the switch back on when ready 🔌");
|
|
308
308
|
return {
|
|
309
309
|
target: "deno",
|
|
310
310
|
projectName,
|
|
@@ -333,6 +333,6 @@ export async function deployRelay(
|
|
|
333
333
|
request: RelayDeployRequest,
|
|
334
334
|
): Promise<RelayDeployResult> {
|
|
335
335
|
if (!request.token.trim())
|
|
336
|
-
throw new GatewayError("invalid_request", 400, `${request.target} API token is required
|
|
336
|
+
throw new GatewayError("invalid_request", 400, `${request.target} API token is required (request {requestId}). Wrong key, right door — check the credential 🔑`);
|
|
337
337
|
return DEPLOYERS[request.target](fetchImpl, request);
|
|
338
338
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Network-pool control-plane HTTP routes. The only module that touches Elysia.
|
|
2
|
-
import { Elysia, t } from "elysia";
|
|
2
|
+
import { Elysia, t, type AnyElysia } from "elysia";
|
|
3
3
|
import { consoleErrorHandler } from "../../shared/errors";
|
|
4
4
|
import { literalUnion } from "../../shared/elysia-schema";
|
|
5
5
|
import { RELAY_TARGETS, type RelayDeployRequest } from "./relay-deploy";
|
|
@@ -56,7 +56,7 @@ const importPoolsBody = t.Object({
|
|
|
56
56
|
family: t.Optional(t.Union([t.String(), t.Null()])),
|
|
57
57
|
labelPrefix: t.Optional(t.String()),
|
|
58
58
|
});
|
|
59
|
-
export function createNetworkPoolRoutes(config: NetworkPoolConfig):
|
|
59
|
+
export function createNetworkPoolRoutes(config: NetworkPoolConfig): AnyElysia {
|
|
60
60
|
const factory = createNetworkPoolOperations(config);
|
|
61
61
|
return new Elysia({ prefix: "/network/pools" })
|
|
62
62
|
.error(consoleErrorHandler("Network pool operation failed"))
|
|
@@ -207,5 +207,5 @@ export function createNetworkPoolRoutes(config: NetworkPoolConfig): Elysia {
|
|
|
207
207
|
body as Partial<PoolStrategySetting>,
|
|
208
208
|
);
|
|
209
209
|
},
|
|
210
|
-
)
|
|
210
|
+
);
|
|
211
211
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// Drizzle-backed console persistence for network pools.
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
2
3
|
import { and, asc, eq, inArray, sql } from "drizzle-orm";
|
|
3
4
|
import type { CartethyiaDatabase } from "../../../persistence/db-handle";
|
|
4
|
-
import { networkPools, poolRoutingSettings } from "../../../persistence/schema";
|
|
5
|
+
import { networkPools, poolRoutingSettings, providerPoolAccounts } from "../../../persistence/schema";
|
|
6
|
+
import { providerRateLimitIsIpScoped } from "../../../providers/provider-metadata";
|
|
5
7
|
import {
|
|
6
8
|
DEFAULT_POOL_STRATEGY,
|
|
7
9
|
type CreateNetworkPoolRequest,
|
|
@@ -444,6 +446,24 @@ export class DrizzleNetworkPoolStore implements NetworkPoolStore {
|
|
|
444
446
|
...(record.family !== undefined ? { family: record.family } : {}),
|
|
445
447
|
}));
|
|
446
448
|
await this.db.insert(networkPools).values(values);
|
|
449
|
+
|
|
450
|
+
// Materialize pool-as-account rows for ip-scoped, credential-less
|
|
451
|
+
// providers (OpenCode Free today): the operator sees the egress in the
|
|
452
|
+
// provider's Accounts card, and routing excludes a spent egress like a
|
|
453
|
+
// cooling account. Every other provider gets no row.
|
|
454
|
+
if (providerRateLimitIsIpScoped("opencode")) {
|
|
455
|
+
const now = new Date();
|
|
456
|
+
const poolAccounts = values.map((value) => ({
|
|
457
|
+
id: randomUUID(),
|
|
458
|
+
tenantId: value.tenantId,
|
|
459
|
+
networkPoolId: value.id,
|
|
460
|
+
providerId: "opencode",
|
|
461
|
+
label: `Pool ${value.id.slice(0, 8)}`,
|
|
462
|
+
createdAt: now,
|
|
463
|
+
updatedAt: now,
|
|
464
|
+
}));
|
|
465
|
+
await this.db.insert(providerPoolAccounts).values(poolAccounts);
|
|
466
|
+
}
|
|
447
467
|
return values.length;
|
|
448
468
|
}
|
|
449
469
|
|
|
@@ -53,6 +53,12 @@ export interface RuntimeSettingsResponse {
|
|
|
53
53
|
readonly telemetryWindowMinutes: number | null;
|
|
54
54
|
/** Per-tenant inline attachment ceiling in bytes; null means deployment default. */
|
|
55
55
|
readonly imageClampMaxBytes: number | null;
|
|
56
|
+
/**
|
|
57
|
+
* Ordered vision-model ids used to describe images when the selected model
|
|
58
|
+
* is not image-capable. Empty means the legacy `[image]` placeholder is
|
|
59
|
+
* used instead.
|
|
60
|
+
*/
|
|
61
|
+
readonly visionHandoffModels: readonly string[];
|
|
56
62
|
readonly updatedAt: string;
|
|
57
63
|
/*
|
|
58
64
|
* What each `null` actually resolves to, in the same units the row stores.
|
|
@@ -67,7 +73,7 @@ export interface RuntimeSettingsResponse {
|
|
|
67
73
|
}
|
|
68
74
|
|
|
69
75
|
/** The deployment-level values a `null` runtime setting falls back to. */
|
|
70
|
-
|
|
76
|
+
interface RuntimeSettingsDefaults {
|
|
71
77
|
/** Seconds; from `CARTETHYIA_UPSTREAM_TIMEOUT_MS`. */
|
|
72
78
|
readonly upstreamTimeoutSeconds: number;
|
|
73
79
|
/** Minutes; what the payload store expires frames after. */
|
|
@@ -122,7 +128,7 @@ export type TelemetryPayloadMode = (typeof TELEMETRY_PAYLOAD_MODES)[number];
|
|
|
122
128
|
export const TELEMETRY_PAYLOAD_DEPTHS = ["minimum", "moderate", "maximum"] as const;
|
|
123
129
|
/** Capture depth for `full` mode accepted by the runtime-settings PATCH. */
|
|
124
130
|
export type TelemetryPayloadDepth = (typeof TELEMETRY_PAYLOAD_DEPTHS)[number];
|
|
125
|
-
|
|
131
|
+
const PRIVACY_MODES = ["masked", "full"] as const;
|
|
126
132
|
/** Provider/model label privacy accepted by the runtime-settings PATCH. */
|
|
127
133
|
export type PrivacyMode = (typeof PRIVACY_MODES)[number];
|
|
128
134
|
/** PonyTail directive intensity accepted by the runtime-settings PATCH. */
|
|
@@ -130,6 +136,9 @@ export type PonyTailLevel = (typeof PONYTAIL_LEVELS)[number];
|
|
|
130
136
|
/** RTK prune strength accepted by the runtime-settings PATCH. */
|
|
131
137
|
export type RtkLevel = (typeof RTK_LEVELS)[number];
|
|
132
138
|
|
|
139
|
+
/** Upper bound on the number of ordered vision-handoff models. */
|
|
140
|
+
export const VISION_HANDOFF_MAX_MODELS = 8;
|
|
141
|
+
|
|
133
142
|
export function createRuntimeSettingsOperations(deps: RuntimeSettingsConfig) {
|
|
134
143
|
const operations = {
|
|
135
144
|
async get(access: AccessDecision | undefined): Promise<RuntimeSettingsResponse> {
|
|
@@ -178,6 +187,23 @@ export function createRuntimeSettingsOperations(deps: RuntimeSettingsConfig) {
|
|
|
178
187
|
if (patch.rtkPruneLevel !== undefined && !RTK_LEVELS.includes(patch.rtkPruneLevel)) {
|
|
179
188
|
throw new ConsoleDomainError("invalid_request", 400, "Invalid rtkPruneLevel");
|
|
180
189
|
}
|
|
190
|
+
if (patch.visionHandoffModels !== undefined) {
|
|
191
|
+
if (!Array.isArray(patch.visionHandoffModels)) {
|
|
192
|
+
throw new ConsoleDomainError("invalid_request", 400, "visionHandoffModels must be an array of model ids");
|
|
193
|
+
}
|
|
194
|
+
if (patch.visionHandoffModels.length > VISION_HANDOFF_MAX_MODELS) {
|
|
195
|
+
throw new ConsoleDomainError(
|
|
196
|
+
"invalid_request",
|
|
197
|
+
400,
|
|
198
|
+
`visionHandoffModels may list at most ${VISION_HANDOFF_MAX_MODELS} models`,
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
for (const model of patch.visionHandoffModels) {
|
|
202
|
+
if (typeof model !== "string" || model.trim().length === 0) {
|
|
203
|
+
throw new ConsoleDomainError("invalid_request", 400, "visionHandoffModels entries must be non-empty model ids");
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
181
207
|
const updated = await deps.store.update(a.tenantId, patch);
|
|
182
208
|
await deps.auditSink?.record({
|
|
183
209
|
access: a,
|
|
@@ -245,4 +271,5 @@ export const runtimeUpdateBody = t.Object({
|
|
|
245
271
|
imageClampMaxBytes: t.Optional(
|
|
246
272
|
t.Union([t.Null(), t.Number({ minimum: 64 * 1024, maximum: 100 * 1024 * 1024 })]),
|
|
247
273
|
),
|
|
274
|
+
visionHandoffModels: t.Optional(t.Array(t.String())),
|
|
248
275
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Elysia } from "elysia";
|
|
1
|
+
import { Elysia, type AnyElysia } from "elysia";
|
|
2
2
|
import {
|
|
3
3
|
createRuntimeSettingsOperations,
|
|
4
4
|
runtimeUpdateBody,
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
type UpdateRuntimeSettingsRequest,
|
|
8
8
|
} from "./contracts";
|
|
9
9
|
|
|
10
|
-
export function createRuntimeSettingsRoutes(config: RuntimeSettingsConfig):
|
|
10
|
+
export function createRuntimeSettingsRoutes(config: RuntimeSettingsConfig): AnyElysia {
|
|
11
11
|
const factory = createRuntimeSettingsOperations(config);
|
|
12
12
|
return new Elysia({ prefix: "/settings/runtime" })
|
|
13
13
|
.get("/", async ({ request, set }) => {
|
|
@@ -26,5 +26,5 @@ export function createRuntimeSettingsRoutes(config: RuntimeSettingsConfig): Elys
|
|
|
26
26
|
} catch (e) {
|
|
27
27
|
return runtimeSettingsErrorResponse(e, set);
|
|
28
28
|
}
|
|
29
|
-
})
|
|
29
|
+
});
|
|
30
30
|
}
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
RTK_LEVELS,
|
|
16
16
|
TELEMETRY_PAYLOAD_DEPTHS,
|
|
17
17
|
TELEMETRY_PAYLOAD_MODES,
|
|
18
|
+
VISION_HANDOFF_MAX_MODELS,
|
|
18
19
|
type PonyTailLevel,
|
|
19
20
|
type ResponsesReasoningSummary,
|
|
20
21
|
type RtkLevel,
|
|
@@ -115,6 +116,22 @@ function normalizeConcurrencyTolerance(value: unknown): number | null {
|
|
|
115
116
|
return normalizeBoundedNumber(value, 0, 1024);
|
|
116
117
|
}
|
|
117
118
|
|
|
119
|
+
/** Ordered vision-handoff models: non-empty strings, trimmed, de-duplicated. */
|
|
120
|
+
function normalizeVisionHandoffModels(value: unknown): readonly string[] {
|
|
121
|
+
if (!Array.isArray(value)) return [];
|
|
122
|
+
const seen = new Set<string>();
|
|
123
|
+
const out: string[] = [];
|
|
124
|
+
for (const entry of value) {
|
|
125
|
+
if (typeof entry !== "string") continue;
|
|
126
|
+
const model = entry.trim();
|
|
127
|
+
if (model.length === 0 || seen.has(model)) continue;
|
|
128
|
+
seen.add(model);
|
|
129
|
+
out.push(model);
|
|
130
|
+
if (out.length >= VISION_HANDOFF_MAX_MODELS) break;
|
|
131
|
+
}
|
|
132
|
+
return out;
|
|
133
|
+
}
|
|
134
|
+
|
|
118
135
|
function mapRuntimeSettingsRow(
|
|
119
136
|
row: typeof consoleSettings.$inferSelect | undefined,
|
|
120
137
|
redisBackend: RedisBackend,
|
|
@@ -148,6 +165,7 @@ function mapRuntimeSettingsRow(
|
|
|
148
165
|
upstreamTimeoutSeconds: normalizeUpstreamTimeoutSeconds(prefs.upstreamTimeoutSeconds),
|
|
149
166
|
telemetryWindowMinutes: normalizeTelemetryWindowMinutes(prefs.telemetryWindowMinutes),
|
|
150
167
|
imageClampMaxBytes: normalizeImageClampMaxBytes(prefs.imageClampMaxBytes),
|
|
168
|
+
visionHandoffModels: normalizeVisionHandoffModels(prefs.visionHandoffModels),
|
|
151
169
|
updatedAt,
|
|
152
170
|
/*
|
|
153
171
|
* Resolved at request time, not at module load: all four come from env the
|
|
@@ -211,6 +229,8 @@ export class DrizzleRuntimeSettingsStore implements RuntimeSettingsStore {
|
|
|
211
229
|
);
|
|
212
230
|
if (patch.imageClampMaxBytes !== undefined)
|
|
213
231
|
patchPrefs.imageClampMaxBytes = normalizeImageClampMaxBytes(patch.imageClampMaxBytes);
|
|
232
|
+
if (patch.visionHandoffModels !== undefined)
|
|
233
|
+
patchPrefs.visionHandoffModels = normalizeVisionHandoffModels(patch.visionHandoffModels);
|
|
214
234
|
|
|
215
235
|
const rows = await this.db
|
|
216
236
|
.insert(consoleSettings)
|
|
@@ -4,7 +4,7 @@ import { globalOrOwnedBy } from "../../persistence/tenant-scope";
|
|
|
4
4
|
// Share URLs reveal policy and allow one child key to be created per canonical
|
|
5
5
|
// client IP. The child bearer is returned only by the successful issue call.
|
|
6
6
|
|
|
7
|
-
import { Elysia } from "elysia";
|
|
7
|
+
import { Elysia, type AnyElysia } from "elysia";
|
|
8
8
|
import { and, eq, sql } from "drizzle-orm";
|
|
9
9
|
import type { CartethyiaDatabase } from "../../persistence/db-handle";
|
|
10
10
|
import { apiKeys, models, providers } from "../../persistence/schema";
|
|
@@ -34,12 +34,83 @@ const MIN_TOKEN_LENGTH = 20;
|
|
|
34
34
|
/**
|
|
35
35
|
* Cadence of the share stats SSE snapshot.
|
|
36
36
|
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
37
|
+
* Roughly one tenth of the cache TTL (`SHARE_STATS_CACHE_TTL_SECONDS`, 300
|
|
38
|
+
* s), so nearly every tick is served from the cache and the cold-compute
|
|
39
|
+
* path runs at most once per five minutes per family. Short enough to still
|
|
40
|
+
* read as live.
|
|
41
41
|
*/
|
|
42
|
-
const SHARE_STATS_STREAM_INTERVAL_MS =
|
|
42
|
+
const SHARE_STATS_STREAM_INTERVAL_MS = 30_000;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* A subscriber of the process-wide share-stats sampler. A recipient is keyed
|
|
46
|
+
* by its family (`tenantId` + template key) so one sampler tick can push to
|
|
47
|
+
* many links without a per-connection timer.
|
|
48
|
+
*/
|
|
49
|
+
type ShareStatsListener = (family: ShareFamilyStats) => void;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* One sampler per gateway for share-stats SSE delivery. Previously each
|
|
53
|
+
* `/share/:token/stats/stream` connection ran its own 30s timer and re-read
|
|
54
|
+
* the family rollup; with N open tabs that is N timers and N rollups all
|
|
55
|
+
* reading the same data. Now a single process-level sampler ticks on the
|
|
56
|
+
* shared cadence and fans each computed rollup out to the subscribers whose
|
|
57
|
+
* family it matches. Timers run only while at least one stream is open.
|
|
58
|
+
*/
|
|
59
|
+
function createShareStatsSampler(config: {
|
|
60
|
+
readonly db: CartethyiaDatabase;
|
|
61
|
+
readonly shareStore: ShareLinkStore;
|
|
62
|
+
readonly stats: ShareStatsPort;
|
|
63
|
+
}): { subscribe(token: string, listener: ShareStatsListener): () => void } {
|
|
64
|
+
const subscribers = new Map<string, Set<ShareStatsListener>>();
|
|
65
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
66
|
+
|
|
67
|
+
const tick = async (): Promise<void> => {
|
|
68
|
+
// One resolve per family token: a recipient with several tabs shares one
|
|
69
|
+
// rollup, and a family with no open stream is never computed.
|
|
70
|
+
const tokens = new Set(subscribers.keys());
|
|
71
|
+
for (const token of tokens) {
|
|
72
|
+
const listeners = subscribers.get(token);
|
|
73
|
+
if (listeners === undefined || listeners.size === 0) continue;
|
|
74
|
+
try {
|
|
75
|
+
const family = await resolveFamilyStats(config.db, config.shareStore, config.stats, token);
|
|
76
|
+
if (family === null) continue;
|
|
77
|
+
for (const listener of listeners) listener(family);
|
|
78
|
+
} catch {
|
|
79
|
+
// A transient read must not interrupt the sampler.
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const start = (): void => {
|
|
85
|
+
if (timer !== undefined) return;
|
|
86
|
+
timer = setTimeout(async function run() {
|
|
87
|
+
timer = setTimeout(run, SHARE_STATS_STREAM_INTERVAL_MS);
|
|
88
|
+
await tick();
|
|
89
|
+
}, SHARE_STATS_STREAM_INTERVAL_MS);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const stop = (): void => {
|
|
93
|
+
if (timer !== undefined) clearTimeout(timer);
|
|
94
|
+
timer = undefined;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
subscribe(token, listener) {
|
|
99
|
+
let listeners = subscribers.get(token);
|
|
100
|
+
if (listeners === undefined) {
|
|
101
|
+
listeners = new Set();
|
|
102
|
+
subscribers.set(token, listeners);
|
|
103
|
+
}
|
|
104
|
+
listeners.add(listener);
|
|
105
|
+
if (subscribers.size > 0) start();
|
|
106
|
+
return () => {
|
|
107
|
+
listeners.delete(listener);
|
|
108
|
+
if (listeners.size === 0) subscribers.delete(token);
|
|
109
|
+
if (subscribers.size === 0) stop();
|
|
110
|
+
};
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
}
|
|
43
114
|
|
|
44
115
|
/**
|
|
45
116
|
* Context and capabilities for one allowed model, sourced from the public
|
|
@@ -56,7 +127,7 @@ export interface ShareModelInfo {
|
|
|
56
127
|
readonly webSearch: boolean;
|
|
57
128
|
}
|
|
58
129
|
|
|
59
|
-
|
|
130
|
+
interface ShareRouterOptions {
|
|
60
131
|
readonly db: CartethyiaDatabase;
|
|
61
132
|
readonly shareStore: ShareLinkStore;
|
|
62
133
|
/** Resolves the normalized client IP through the trusted-proxy boundary. */
|
|
@@ -89,12 +160,12 @@ function json(body: unknown, status = 200): Response {
|
|
|
89
160
|
function notFound(refusal: ShareLinkRefusal = "not_found"): Response {
|
|
90
161
|
const message =
|
|
91
162
|
refusal === "expired"
|
|
92
|
-
? "This share link has expired"
|
|
163
|
+
? "This share link has expired (request {requestId}). Time for a fresh one 🌷"
|
|
93
164
|
: refusal === "revoked"
|
|
94
|
-
? "This share link's key has been revoked"
|
|
165
|
+
? "This share link's key has been revoked (request {requestId}). Ask for a new one 📩"
|
|
95
166
|
: refusal === "disabled"
|
|
96
|
-
? "This share link's key is paused"
|
|
97
|
-
: "Share link is unavailable";
|
|
167
|
+
? "This share link's key is paused (request {requestId}). Re-enable it when ready 🌱"
|
|
168
|
+
: "Share link is unavailable (request {requestId}). This link leads nowhere — check it again 🔗";
|
|
98
169
|
const error = new GatewayError(`link_${refusal}`, 404, message);
|
|
99
170
|
return json(publicGatewayErrorBody(error), error.status);
|
|
100
171
|
}
|
|
@@ -261,8 +332,14 @@ async function resolveFamilyStats(
|
|
|
261
332
|
}
|
|
262
333
|
|
|
263
334
|
/** Creates the public enrollment page and one-time shared-key issuance route. */
|
|
264
|
-
export function createShareRouter(options: ShareRouterOptions):
|
|
335
|
+
export function createShareRouter(options: ShareRouterOptions): AnyElysia {
|
|
265
336
|
const { db, shareStore } = options;
|
|
337
|
+
// The sampler is shared by every stats stream this router serves; it is a
|
|
338
|
+
// no-op when the composition omits `stats` (tests that do not exercise the
|
|
339
|
+
// stream), because `tick` never runs with no subscribers.
|
|
340
|
+
const statsSampler = options.stats === undefined
|
|
341
|
+
? undefined
|
|
342
|
+
: createShareStatsSampler({ db, shareStore, stats: options.stats });
|
|
266
343
|
|
|
267
344
|
return new Elysia()
|
|
268
345
|
/**
|
|
@@ -296,6 +373,7 @@ export function createShareRouter(options: ShareRouterOptions): Elysia {
|
|
|
296
373
|
oneTimeLimit: row.lifetimeTokenBudget,
|
|
297
374
|
requestsPerMinute: row.requestsPerMinute,
|
|
298
375
|
maxConcurrentRequests: row.maxConcurrentRequests,
|
|
376
|
+
concurrencyScope: row.concurrencyScope ?? "family",
|
|
299
377
|
modelPrefix: row.modelPrefix,
|
|
300
378
|
models,
|
|
301
379
|
modelInfo,
|
|
@@ -442,49 +520,39 @@ export function createShareRouter(options: ShareRouterOptions): Elysia {
|
|
|
442
520
|
/**
|
|
443
521
|
* Live family activity for the share page's stats section, pushed over SSE.
|
|
444
522
|
*
|
|
445
|
-
* The stream sends one snapshot immediately on connect, then
|
|
446
|
-
*
|
|
447
|
-
* refreshing. Telemetry is buffered server-side and drained in
|
|
448
|
-
* second, so
|
|
449
|
-
* be. Same bearer token and same masked-IP rollup as the one-shot
|
|
450
|
-
* token that stops resolving closes the stream rather than
|
|
523
|
+
* The stream sends one snapshot immediately on connect, then pushes on a
|
|
524
|
+
* shared sampler cadence so the recipient watches the shared quota move
|
|
525
|
+
* instead of refreshing. Telemetry is buffered server-side and drained in
|
|
526
|
+
* under a second, so the sampler's DB re-read is as fresh as an event bus
|
|
527
|
+
* would be. Same bearer token and same masked-IP rollup as the one-shot
|
|
528
|
+
* route; a token that stops resolving closes the stream rather than
|
|
529
|
+
* reporting zeros.
|
|
451
530
|
*/
|
|
452
531
|
.get("/share/:token/stats/stream", async ({ params, request }) => {
|
|
453
532
|
if (typeof params.token !== "string") return notFound();
|
|
454
533
|
const token = params.token;
|
|
455
534
|
if (token.length < MIN_TOKEN_LENGTH) return notFound();
|
|
456
535
|
const stats = options.stats;
|
|
457
|
-
if (!stats
|
|
536
|
+
if (!stats || statsSampler === undefined) {
|
|
537
|
+
return json({ error: { code: "stats_unavailable", message: "Stats are unavailable" } }, 503);
|
|
538
|
+
}
|
|
539
|
+
const sampler = statsSampler;
|
|
458
540
|
return consoleSseResponse(
|
|
459
541
|
createConsoleSseStream(request.signal, (sender) => {
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
542
|
+
const unsubscribe = sampler.subscribe(token, (family) => sender.send("stats", family));
|
|
543
|
+
// Bootstrap: deliver the link's current rollup immediately, and close
|
|
544
|
+
// the stream if the token no longer resolves — the same terminal
|
|
545
|
+
// behavior the per-connection timer had on a dead link.
|
|
546
|
+
void resolveFamilyStats(db, shareStore, stats, token).then((family) => {
|
|
465
547
|
if (family === null) {
|
|
466
548
|
sender.send("error", { code: "link_not_found" });
|
|
467
|
-
|
|
468
|
-
|
|
549
|
+
unsubscribe();
|
|
550
|
+
} else {
|
|
551
|
+
sender.send("stats", family);
|
|
469
552
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
const schedule = () => {
|
|
473
|
-
timer = setTimeout(() => {
|
|
474
|
-
if (closed) return;
|
|
475
|
-
void push().finally(() => {
|
|
476
|
-
if (!closed) schedule();
|
|
477
|
-
});
|
|
478
|
-
}, SHARE_STATS_STREAM_INTERVAL_MS);
|
|
479
|
-
};
|
|
480
|
-
void push().finally(() => {
|
|
481
|
-
if (!closed) schedule();
|
|
482
|
-
});
|
|
483
|
-
return () => {
|
|
484
|
-
closed = true;
|
|
485
|
-
clearTimeout(timer);
|
|
486
|
-
};
|
|
553
|
+
}).catch(() => undefined);
|
|
554
|
+
return unsubscribe;
|
|
487
555
|
}),
|
|
488
556
|
);
|
|
489
|
-
})
|
|
557
|
+
})
|
|
490
558
|
}
|