cartethyia 2.5.9 → 2.6.0
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 +15 -42
- package/README.md +11 -10
- package/dist/dashboard/assets/{BannedPage-Brvuj_Eb.js → BannedPage-l6XMnfr9.js} +1 -1
- package/dist/dashboard/assets/BannedPage-l6XMnfr9.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-CqfXtI3y.js → CliToolDetailPage-DwOzAW45.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-DwOzAW45.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-GQeS91bg.js → CliToolsPage-BT3Up3QA.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-BT3Up3QA.js.gz +0 -0
- package/dist/dashboard/assets/{CombosPage-BDRsTXBB.js → CombosPage-6kYIUpzR.js} +1 -1
- package/dist/dashboard/assets/CombosPage-6kYIUpzR.js.gz +0 -0
- package/dist/dashboard/assets/{ConsoleLogPage-zP8AFHX0.js → ConsoleLogPage-zRnj1mlR.js} +1 -1
- package/dist/dashboard/assets/ConsoleLogPage-zRnj1mlR.js.gz +0 -0
- package/dist/dashboard/assets/{CustomizationPage-BmfrNZ14.js → CustomizationPage-C7m-H1Rz.js} +1 -1
- package/dist/dashboard/assets/CustomizationPage-C7m-H1Rz.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-C8MR_BdB.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-C8MR_BdB.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-DoreIgm8.js → LoginPage-CbphLvjg.js} +1 -1
- package/dist/dashboard/assets/LoginPage-CbphLvjg.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-C7pDb41R.js → ModelPicker-DVny4S5_.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-DVny4S5_.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-DABu2Wgt.js +1 -0
- package/dist/dashboard/assets/OverviewPage-DABu2Wgt.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-C15x9q13.js +3 -0
- package/dist/dashboard/assets/ProviderDetailPage-C15x9q13.js.gz +0 -0
- package/dist/dashboard/assets/{ProvidersPage-BnoPgA7w.js → ProvidersPage-Dfb2896L.js} +1 -1
- package/dist/dashboard/assets/ProvidersPage-Dfb2896L.js.gz +0 -0
- package/dist/dashboard/assets/{ProxyPage-Bq_TGgdb.js → ProxyPage-BaCIdAUt.js} +1 -1
- package/dist/dashboard/assets/ProxyPage-BaCIdAUt.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-BsdoCIw1.js +1 -0
- package/dist/dashboard/assets/QuotaPage-BsdoCIw1.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-KVJFh3Ip.js +1 -0
- package/dist/dashboard/assets/SettingsPage-KVJFh3Ip.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-Day3mCOn.js → SetupPage-D4g6vOGc.js} +1 -1
- package/dist/dashboard/assets/SetupPage-D4g6vOGc.js.gz +0 -0
- package/dist/dashboard/assets/{SortableList-DQxllnB5.js → SortableList-C93i14BD.js} +1 -1
- package/dist/dashboard/assets/SortableList-C93i14BD.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-uMExoWu1.js → StudioPage-SgQpymjm.js} +1 -1
- package/dist/dashboard/assets/{StudioPage-uMExoWu1.js.gz → StudioPage-SgQpymjm.js.gz} +0 -0
- package/dist/dashboard/assets/{UsagePage-DXa1oR-4.js → UsagePage-BFME-eba.js} +2 -2
- package/dist/dashboard/assets/UsagePage-BFME-eba.js.gz +0 -0
- package/dist/dashboard/assets/{api-keys-DQYLQxpW.js → api-keys-C23S9sgp.js} +1 -1
- package/dist/dashboard/assets/api-keys-C23S9sgp.js.gz +0 -0
- package/dist/dashboard/assets/{app-BIt446yE.js → app-2KhtHCv9.js} +1 -1
- package/dist/dashboard/assets/{app-BIt446yE.js.gz → app-2KhtHCv9.js.gz} +0 -0
- package/dist/dashboard/assets/app-CEeaMHOy.css +2 -0
- package/dist/dashboard/assets/app-CEeaMHOy.css.gz +0 -0
- package/dist/dashboard/assets/{assert-C9FD_JwJ.js → assert-DU6qrMA_.js} +1 -1
- package/dist/dashboard/assets/assert-DU6qrMA_.js.gz +0 -0
- package/dist/dashboard/assets/{badge-CR72dasT.js → badge-CKsF123d.js} +1 -1
- package/dist/dashboard/assets/badge-CKsF123d.js.gz +0 -0
- package/dist/dashboard/assets/{banner-CI9_eSfO.js → banner-8j7uChA9.js} +1 -1
- package/dist/dashboard/assets/banner-8j7uChA9.js.gz +0 -0
- package/dist/dashboard/assets/{card-DS7CQEsO.js → card-CdsSlWA3.js} +1 -1
- package/dist/dashboard/assets/card-CdsSlWA3.js.gz +0 -0
- package/dist/dashboard/assets/{cli-tools-CNqzrfdb.js → cli-tools-BaFUuFFF.js} +1 -1
- package/dist/dashboard/assets/cli-tools-BaFUuFFF.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-611pSsaX.js → clipboard-button-uL5aYLfW.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-uL5aYLfW.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-k37exjNt.js → dialog-C07hr5_G.js} +1 -1
- package/dist/dashboard/assets/dialog-C07hr5_G.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-CX7N8ucR.js → drawer-BPO1TcKH.js} +1 -1
- package/dist/dashboard/assets/drawer-BPO1TcKH.js.gz +0 -0
- package/dist/dashboard/assets/{entry-CTP8AyiS.js → entry-Bz_CtDfC.js} +1 -1
- package/dist/dashboard/assets/entry-Bz_CtDfC.js.gz +0 -0
- package/dist/dashboard/assets/entry-CwPRKk0d.js +4 -0
- package/dist/dashboard/assets/entry-CwPRKk0d.js.gz +0 -0
- package/dist/dashboard/assets/entry-GJRbmrpA.js +1 -0
- package/dist/dashboard/assets/entry-GJRbmrpA.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-sltk1qUi.js → github-badge-DmQreSDd.js} +1 -1
- package/dist/dashboard/assets/github-badge-DmQreSDd.js.gz +0 -0
- package/dist/dashboard/assets/{index-z3zDOSMM.js → index-Bblu6erR.js} +2 -2
- package/dist/dashboard/assets/index-Bblu6erR.js.gz +0 -0
- package/dist/dashboard/assets/{inline-BhY2sSnO.js → inline-C9dCYaBZ.js} +1 -1
- package/dist/dashboard/assets/inline-C9dCYaBZ.js.gz +0 -0
- package/dist/dashboard/assets/{input-V1umFiBk.js → input-CjCnXweJ.js} +1 -1
- package/dist/dashboard/assets/input-CjCnXweJ.js.gz +0 -0
- package/dist/dashboard/assets/{layout-BBOYycyS.js → layout-V6y8DAkl.js} +1 -1
- package/dist/dashboard/assets/layout-V6y8DAkl.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-1zzZYM0B.js → list-row-CM1AaG_y.js} +1 -1
- package/dist/dashboard/assets/list-row-CM1AaG_y.js.gz +0 -0
- package/dist/dashboard/assets/{live-BivGfuUs.js → live-ta6GMb-8.js} +1 -1
- package/dist/dashboard/assets/live-ta6GMb-8.js.gz +0 -0
- package/dist/dashboard/assets/{model-bans-CmffjLuG.js → model-bans-BmYTmbAk.js} +1 -1
- package/dist/dashboard/assets/model-bans-BmYTmbAk.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-46P0oWMe.js → mutation-g_fduiNw.js} +1 -1
- package/dist/dashboard/assets/mutation-g_fduiNw.js.gz +0 -0
- package/dist/dashboard/assets/{numbers-DkfMR5Qs.js → numbers-BOevJzdz.js} +1 -1
- package/dist/dashboard/assets/numbers-BOevJzdz.js.gz +0 -0
- package/dist/dashboard/assets/{query-keys-BFhGpwR1.js → query-keys-UETqfytp.js} +1 -1
- package/dist/dashboard/assets/query-keys-UETqfytp.js.gz +0 -0
- package/dist/dashboard/assets/{routing-CAb4F37R.js → routing-BPUo-W8d.js} +1 -1
- package/dist/dashboard/assets/routing-BPUo-W8d.js.gz +0 -0
- package/dist/dashboard/assets/{select-C6MMiaio.js → select-C980c4cz.js} +1 -1
- package/dist/dashboard/assets/select-C980c4cz.js.gz +0 -0
- package/dist/dashboard/assets/{settings-DNX9sydi.js → settings--3hxSyry.js} +1 -1
- package/dist/dashboard/assets/settings--3hxSyry.js.gz +0 -0
- package/dist/dashboard/assets/{stack-BHTfXdz_.js → stack-_gfTdLVB.js} +1 -1
- package/dist/dashboard/assets/stack-_gfTdLVB.js.gz +0 -0
- package/dist/dashboard/assets/{state-mPDRlQkl.js → state-2c5V4zo7.js} +1 -1
- package/dist/dashboard/assets/state-2c5V4zo7.js.gz +0 -0
- package/dist/dashboard/assets/{switch-DOmJxg7H.js → switch-Dkke7Naf.js} +1 -1
- package/dist/dashboard/assets/switch-Dkke7Naf.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-Dem0lXQj.js → useMutation-EDFRkn1i.js} +1 -1
- package/dist/dashboard/assets/useMutation-EDFRkn1i.js.gz +0 -0
- package/dist/dashboard/index.html +1 -1
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +51 -51
- package/migrations/README.md +52 -52
- package/migrations/full-postgres/0020_add_telemetry_event_analytics.sql +77 -0
- package/migrations/full-postgres/0021_add_api_key_concurrency_tolerance.sql +13 -0
- package/migrations/full-postgres/0022_add_inferhub_config_key.sql +17 -0
- package/migrations/full-postgres/0023_drop_api_key_concurrency_tolerance.sql +13 -0
- package/migrations/lite-sqlite3/0020_add_telemetry_event_analytics.sql +69 -0
- package/migrations/lite-sqlite3/0021_add_api_key_concurrency_tolerance.sql +13 -0
- package/migrations/lite-sqlite3/0022_add_inferhub_config_key.sql +17 -0
- package/migrations/lite-sqlite3/0023_drop_api_key_concurrency_tolerance.sql +13 -0
- package/package.json +96 -97
- package/scripts/cli/admin.ts +126 -125
- package/scripts/cli/autostart.ts +237 -237
- package/scripts/cli/background.ts +120 -110
- package/scripts/cli/bin/carte.cjs +8 -8
- package/scripts/cli/bin/cartethyia.cjs +167 -167
- package/scripts/cli/console-menu.ts +308 -275
- package/scripts/cli/controller.ts +145 -0
- package/scripts/cli/doctor.ts +1 -1
- package/scripts/cli/env.ts +99 -99
- package/scripts/cli/gateway.ts +264 -261
- package/scripts/{health.ts → cli/health.ts} +4 -4
- package/scripts/cli/instance.ts +234 -224
- package/scripts/cli/main.ts +188 -164
- package/scripts/cli/platform.ts +64 -64
- package/scripts/cli/prompt.ts +275 -107
- package/scripts/cli/services.ts +88 -88
- package/scripts/cli/setup.ts +445 -445
- package/scripts/cli/start.ts +85 -82
- package/scripts/cli/startup-signal.ts +23 -23
- package/scripts/cli/telemetry.ts +70 -0
- package/scripts/cli/tray/index.ts +263 -263
- package/scripts/cli/tray/windows.ps1 +107 -107
- package/scripts/cli/tray.ts +192 -208
- package/scripts/cli/update.ts +39 -39
- package/scripts/cli/version.ts +67 -67
- package/scripts/cli/wordmark.ts +58 -58
- package/src/boot/app.ts +16 -1
- package/src/boot/dependencies.ts +128 -25
- package/src/config/package-paths.ts +24 -0
- package/src/config/resources.ts +137 -0
- package/src/config/runtime.ts +133 -76
- package/src/config/spec.ts +30 -29
- package/src/config/telemetry.ts +0 -25
- package/src/console/api-keys/bucket-spend.ts +14 -17
- package/src/console/api-keys/contracts.ts +410 -410
- package/src/console/api-keys/routes.ts +12 -0
- package/src/console/audit/store.ts +2 -3
- package/src/console/auth/setup-service.ts +8 -1
- package/src/console/backup/routes.ts +7 -0
- package/src/console/backup/service.ts +463 -463
- package/src/console/backup/store.ts +16 -11
- package/src/console/backup/validate.ts +1 -4
- package/src/console/cli-tools/fs-ops.ts +18 -2
- package/src/console/data-management/contracts.ts +1 -8
- package/src/console/data-management/operations.ts +61 -48
- package/src/console/data-management/routes.ts +4 -2
- package/src/console/domain-registration.ts +25 -3
- package/src/console/inferhub/client.ts +139 -0
- package/src/console/inferhub/key-store.ts +129 -0
- package/src/console/inferhub/normalize.ts +347 -0
- package/src/console/inferhub/routes.ts +411 -0
- package/src/console/inferhub/store.ts +109 -0
- package/src/console/inferhub/types.ts +183 -0
- package/src/console/observability/contracts.ts +2 -41
- package/src/console/observability/store.ts +176 -371
- package/src/console/providers/catalog/public-model-store.ts +0 -0
- package/src/console/providers/catalog/store.ts +23 -36
- package/src/console/quota/account-quota-shared.ts +52 -52
- package/src/console/quota/account-quota-tenant.ts +16 -24
- package/src/console/quota/refresh.ts +441 -441
- package/src/console/remote-support/service.ts +270 -114
- package/src/console/routing/pools/import-parse.ts +206 -206
- package/src/console/routing/pools/operations.ts +543 -543
- package/src/console/routing/pools/probe-verdict.ts +132 -132
- package/src/console/routing/pools/routes.ts +181 -181
- package/src/console/routing/pools/store.ts +1 -1
- package/src/console/settings/contracts.ts +42 -40
- package/src/console/settings/store.ts +25 -25
- package/src/console/share/share-stats.ts +22 -4
- package/src/console/share/share-usage.ts +30 -4
- package/src/console/shared/cursor.ts +15 -0
- package/src/console/updates/repository-updates.ts +40 -39
- package/src/console/updates/routes.ts +12 -0
- package/src/main.ts +13 -5
- package/src/network/pool/resolver.ts +1104 -1104
- package/src/observability/client-name.ts +22 -0
- package/src/observability/event-log.ts +5 -29
- package/src/observability/event-query.ts +690 -0
- package/src/observability/log-ring.ts +26 -2
- package/src/observability/log-sanitize.ts +118 -0
- package/src/observability/logger.ts +17 -13
- package/src/observability/metrics.ts +35 -0
- package/src/observability/payload-capture.ts +114 -41
- package/src/observability/payload-store.ts +249 -24
- package/src/observability/raw-event-writer.ts +118 -0
- package/src/observability/runtime-metrics.ts +71 -1
- package/src/observability/telemetry-buffer.ts +100 -17
- package/src/observability/telemetry-reconciliation.ts +417 -0
- package/src/persistence/boot.ts +20 -9
- package/src/persistence/db-pg.ts +13 -0
- package/src/persistence/db-sqlite.ts +569 -497
- package/src/persistence/dialect.ts +3 -2
- package/src/persistence/migrate.ts +181 -181
- package/src/persistence/schema.ts +1496 -1406
- package/src/persistence/shared-cache.ts +5 -1
- package/src/persistence/stores/api-key-store.ts +581 -581
- package/src/persistence/stores/telemetry-store.ts +124 -63
- package/src/protocol/reasoning-text.ts +30 -0
- package/src/protocol/registry.ts +2 -0
- package/src/protocol/response/chat.ts +33 -7
- package/src/protocol/response/codex.ts +14 -2
- package/src/protocol/response/messages.ts +5 -2
- package/src/providers/authentication/oauth-flow-store.ts +3 -10
- package/src/providers/bundled-discovery-providers.ts +122 -122
- package/src/providers/default-registry.ts +14 -0
- package/src/providers/discovery/catalog/catalog-blob.ts +18 -18
- package/src/providers/discovery/models-dev-catalog.ts +354 -354
- package/src/providers/discovery/probe-phases.ts +43 -7
- package/src/providers/discovery/probing-service.ts +70 -32
- package/src/providers/integrations/antigravity/antigravity.ts +9 -2
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +85 -17
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +934 -934
- package/src/providers/integrations/buddy/buddy-checkin.ts +5 -9
- package/src/providers/integrations/buddy/buddy-discovery-shared.ts +3 -7
- package/src/providers/integrations/buddy/buddy-quota-shared.ts +3 -1
- package/src/providers/integrations/buddy/capabilities.ts +4 -0
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +57 -49
- package/src/providers/integrations/buddy/codebuddy/provider.ts +180 -180
- package/src/providers/integrations/buddy/workbuddy/provider.ts +206 -206
- package/src/providers/integrations/buddy/workbuddy/quota.ts +161 -161
- package/src/providers/integrations/claude/claude-quota.ts +2 -2
- package/src/providers/integrations/claude/claude-user-profiles.ts +3 -2
- package/src/providers/integrations/claude/claude.ts +3 -4
- package/src/providers/integrations/cline/cline.ts +94 -8
- package/src/providers/integrations/codex/codex.ts +61 -37
- package/src/providers/integrations/cursor/cursor-catalog.ts +368 -368
- package/src/providers/integrations/cursor/cursor-http2.ts +300 -300
- package/src/providers/integrations/cursor/cursor-identity.ts +2 -4
- package/src/providers/integrations/cursor/cursor-request.ts +11 -0
- package/src/providers/integrations/cursor/cursor.ts +29 -17
- package/src/providers/integrations/cursor/protocol/framing.ts +129 -129
- package/src/providers/integrations/cursor/protocol/messages.ts +2380 -2380
- package/src/providers/integrations/cursor/protocol/struct.ts +305 -305
- package/src/providers/integrations/cursor/protocol/tool-canonical.ts +275 -0
- package/src/providers/integrations/github/github.ts +3 -1
- package/src/providers/integrations/grok/grok-quota.ts +279 -279
- package/src/providers/integrations/grok/grok-ratelimit-store.ts +73 -73
- package/src/providers/integrations/grok/grok-ratelimit.ts +218 -218
- package/src/providers/integrations/hermes.ts +7 -1
- package/src/providers/integrations/inferhub.ts +201 -73
- package/src/providers/integrations/kilo/kilo.ts +152 -152
- package/src/providers/integrations/kiro/kiro-machine-id.ts +2 -4
- package/src/providers/integrations/opencode/provider.ts +2 -0
- package/src/providers/integrations/openrouter/provider.ts +129 -129
- package/src/providers/integrations/qoder/qoder-quota.ts +193 -192
- package/src/providers/integrations/qoder/qoder-realm.ts +106 -106
- package/src/providers/integrations/trae/trae-headers.ts +17 -48
- package/src/providers/integrations/trae/trae-oauth.ts +4 -26
- package/src/providers/integrations/trae/trae-quota.ts +389 -384
- package/src/providers/integrations/trae/trae-realm.ts +15 -2
- package/src/providers/integrations/trae/trae.ts +22 -24
- package/src/providers/integrations/xiaomi-mimo/think-stream.ts +100 -24
- package/src/providers/integrations/zcode/oauth.ts +1 -1
- package/src/providers/operations/account-health-service.ts +605 -279
- package/src/providers/provider-metadata.ts +12 -1
- package/src/providers/provider-model-definition.ts +373 -373
- package/src/providers/provider-probe.ts +3 -0
- package/src/providers/quota/quota-contracts.ts +3 -7
- package/src/providers/search/search-provider.ts +2 -6
- package/src/security/admission/contracts.ts +10 -0
- package/src/security/admission/in-memory-store.ts +89 -14
- package/src/security/admission/lua.ts +191 -182
- package/src/security/admission/redis-store.ts +30 -0
- package/src/security/admission/service.ts +18 -0
- package/src/security/api-key-auth-cache.ts +96 -0
- package/src/security/api-key-auth.ts +11 -100
- package/src/security/client-router-fingerprint.ts +3 -7
- package/src/security/ip-boundary.ts +7 -3
- package/src/shared/coerce.ts +67 -0
- package/src/shared/hash.ts +6 -0
- package/src/shared/time.ts +7 -0
- package/src/transport/dispatch/attempt-finalize.ts +26 -138
- package/src/transport/dispatch/attempt-loop.ts +103 -81
- package/src/transport/dispatch/capture-policy.ts +78 -0
- package/src/transport/dispatch/proxy-request.ts +4 -2
- package/src/transport/dispatch/streaming/encoder.ts +111 -5
- package/src/transport/dispatch/streaming/stream.ts +19 -2
- package/src/transport/dispatch/streaming/thinking-loop.ts +300 -0
- package/src/transport/failure-policy.ts +27 -0
- package/src/transport/guards/abuse-guard.ts +2 -2
- package/src/transport/guards/auth-guard.ts +17 -0
- package/src/transport/guards/body-policy.ts +3 -2
- package/src/transport/guards/concurrency-guard.ts +89 -13
- package/src/transport/guards/early-rejection-capture.ts +43 -3
- package/src/transport/guards/error-lifecycle.ts +10 -2
- package/src/transport/guards/guard-errors.ts +23 -0
- package/src/transport/guards/headroom-guard.ts +41 -7
- package/src/transport/guards/pipeline.ts +24 -0
- package/src/transport/guards/request-context.ts +2 -1
- package/src/transport/routing/admission.ts +51 -9
- package/src/transport/routing/bounded-lru.ts +49 -0
- package/src/transport/routing/candidate-index.ts +88 -0
- package/src/transport/routing/reservations.ts +121 -7
- package/src/transport/routing/round-robin.ts +10 -22
- package/src/transport/routing/route-catalog.ts +26 -13
- package/src/transport/routing/route-model.ts +22 -2
- package/src/transport/routing/router.ts +53 -31
- package/src/transport/surface/chat/encode.ts +2 -3
- package/src/transport/surface/chat/parse.ts +2 -1
- package/src/transport/surface/dialects.ts +11 -3
- package/src/transport/surface/messages/encode.ts +2 -1
- package/src/transport/surface/messages/parse.ts +3 -1
- package/src/transport/surface/messages/stream.ts +2 -6
- package/src/workers/maintenance-lease.ts +200 -0
- package/src/workers/scheduler.ts +101 -1
- package/dist/dashboard/assets/BannedPage-Brvuj_Eb.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-CqfXtI3y.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-GQeS91bg.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-BDRsTXBB.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-zP8AFHX0.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-BmfrNZ14.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-Dn3M6WZc.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-Dn3M6WZc.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-DoreIgm8.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-C7pDb41R.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-CvvD1Ugk.js +0 -1
- package/dist/dashboard/assets/OverviewPage-CvvD1Ugk.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-Clbn1gsW.js +0 -3
- package/dist/dashboard/assets/ProviderDetailPage-Clbn1gsW.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-BnoPgA7w.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-Bq_TGgdb.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-D9DHXvom.js +0 -1
- package/dist/dashboard/assets/QuotaPage-D9DHXvom.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-7xV8WkWL.js +0 -1
- package/dist/dashboard/assets/SettingsPage-7xV8WkWL.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-Day3mCOn.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-DQxllnB5.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-DXa1oR-4.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-DQYLQxpW.js.gz +0 -0
- package/dist/dashboard/assets/app-CX_Z8cMs.css +0 -2
- package/dist/dashboard/assets/app-CX_Z8cMs.css.gz +0 -0
- package/dist/dashboard/assets/assert-C9FD_JwJ.js.gz +0 -0
- package/dist/dashboard/assets/badge-CR72dasT.js.gz +0 -0
- package/dist/dashboard/assets/banner-CI9_eSfO.js.gz +0 -0
- package/dist/dashboard/assets/card-DS7CQEsO.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-CNqzrfdb.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-611pSsaX.js.gz +0 -0
- package/dist/dashboard/assets/dialog-k37exjNt.js.gz +0 -0
- package/dist/dashboard/assets/drawer-CX7N8ucR.js.gz +0 -0
- package/dist/dashboard/assets/entry-B9Xvtnfj.js +0 -1
- package/dist/dashboard/assets/entry-B9Xvtnfj.js.gz +0 -0
- package/dist/dashboard/assets/entry-ByMGkR3C.js +0 -4
- package/dist/dashboard/assets/entry-ByMGkR3C.js.gz +0 -0
- package/dist/dashboard/assets/entry-CTP8AyiS.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-sltk1qUi.js.gz +0 -0
- package/dist/dashboard/assets/index-z3zDOSMM.js.gz +0 -0
- package/dist/dashboard/assets/inline-BhY2sSnO.js.gz +0 -0
- package/dist/dashboard/assets/input-V1umFiBk.js.gz +0 -0
- package/dist/dashboard/assets/layout-BBOYycyS.js.gz +0 -0
- package/dist/dashboard/assets/list-row-1zzZYM0B.js.gz +0 -0
- package/dist/dashboard/assets/live-BivGfuUs.js.gz +0 -0
- package/dist/dashboard/assets/model-bans-CmffjLuG.js.gz +0 -0
- package/dist/dashboard/assets/mutation-46P0oWMe.js.gz +0 -0
- package/dist/dashboard/assets/numbers-DkfMR5Qs.js.gz +0 -0
- package/dist/dashboard/assets/query-keys-BFhGpwR1.js.gz +0 -0
- package/dist/dashboard/assets/routing-CAb4F37R.js.gz +0 -0
- package/dist/dashboard/assets/select-C6MMiaio.js.gz +0 -0
- package/dist/dashboard/assets/settings-DNX9sydi.js.gz +0 -0
- package/dist/dashboard/assets/stack-BHTfXdz_.js.gz +0 -0
- package/dist/dashboard/assets/state-mPDRlQkl.js.gz +0 -0
- package/dist/dashboard/assets/switch-DOmJxg7H.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-Dem0lXQj.js.gz +0 -0
- package/scripts/build/aot.ts +0 -143
- package/scripts/build/build.ts +0 -24
- package/scripts/build/compile.ts +0 -88
- package/scripts/build/restart.ts +0 -120
- package/scripts/build/start.ts +0 -25
- package/src/observability/event-compaction.ts +0 -317
- package/src/observability/event-day-index.ts +0 -440
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
// `rejected` so the model can continue without the gateway ever touching a
|
|
18
18
|
// filesystem or a shell on the caller's behalf.
|
|
19
19
|
import { GatewayError } from "../../../protocol/gateway-error";
|
|
20
|
+
import { asRecord as recordOf } from "../../../shared/coerce";
|
|
20
21
|
import type {
|
|
21
22
|
CanonicalEvent,
|
|
22
23
|
CanonicalRequest,
|
|
@@ -60,6 +61,7 @@ import { fetchCursorRunOrigin } from "./cursor-catalog";
|
|
|
60
61
|
import { defaultParameterValues, fetchCursorModels } from "./cursor-catalog";
|
|
61
62
|
import type { CursorModelInfo } from "./cursor-catalog";
|
|
62
63
|
import { decodeValueToJson, decodeStructEntriesToJson } from "./protocol/struct";
|
|
64
|
+
import { cursorVariantForExecArgs } from "./protocol/tool-canonical";
|
|
63
65
|
|
|
64
66
|
/**
|
|
65
67
|
* Client-heartbeat cadence for every Cursor Run.
|
|
@@ -259,12 +261,6 @@ function usageFromTurnEnded(turnEnded: Record<string, unknown>): UsageRecord {
|
|
|
259
261
|
};
|
|
260
262
|
}
|
|
261
263
|
|
|
262
|
-
function recordOf(value: unknown): Record<string, unknown> | undefined {
|
|
263
|
-
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
264
|
-
? (value as Record<string, unknown>)
|
|
265
|
-
: undefined;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
264
|
function nonEmptyStringOf(value: unknown): string | undefined {
|
|
269
265
|
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
270
266
|
}
|
|
@@ -1093,6 +1089,32 @@ class CursorAdapter implements ProviderAdapter {
|
|
|
1093
1089
|
await handle.write(buildAllowlistPrecheckResult(execId, precheckKind, execIdString));
|
|
1094
1090
|
return { kind: "handled" };
|
|
1095
1091
|
}
|
|
1092
|
+
// Native first. A Cursor host-exec frame (`read_args`, `shell_args`,
|
|
1093
|
+
// `pi_read_args`, …) is relayed to the client as an ordinary tool call
|
|
1094
|
+
// instead of being refused. This is the half the earlier direct-forward
|
|
1095
|
+
// attempt got wrong: it built typed result frames by hand per variant and
|
|
1096
|
+
// they were not matched by the server. The canonical layer owns that
|
|
1097
|
+
// mapping now, keyed by the originating variant, so a result can only ever
|
|
1098
|
+
// come back in the arm it arrived on.
|
|
1099
|
+
const nativeCall = cursorVariantForExecArgs(execServer);
|
|
1100
|
+
// Only a tool the request actually declared may be relayed. A host frame
|
|
1101
|
+
// for something the client never advertised is an undeclared tool like any
|
|
1102
|
+
// other and keeps the refusal — forwarding it would hand the client a call
|
|
1103
|
+
// it has no handler for and nowhere to send the result.
|
|
1104
|
+
const declaredNative = nativeCall !== undefined &&
|
|
1105
|
+
session.descriptors.some((d) => d.toolName === nativeCall.variant.name);
|
|
1106
|
+
if (nativeCall !== undefined && declaredNative) {
|
|
1107
|
+
return {
|
|
1108
|
+
kind: "tool_call",
|
|
1109
|
+
execId,
|
|
1110
|
+
execIdString,
|
|
1111
|
+
toolName: nativeCall.variant.name,
|
|
1112
|
+
arguments: JSON.stringify(nativeCall.args),
|
|
1113
|
+
};
|
|
1114
|
+
}
|
|
1115
|
+
// MCP is the fallback, not the first stop: client-declared tools have no
|
|
1116
|
+
// native slot in `agent.proto`, so they are the ones that must travel this
|
|
1117
|
+
// channel. An arm neither path claims falls through to the refusal below.
|
|
1096
1118
|
const mcpArgs = recordOf(execServer["mcp_args"]);
|
|
1097
1119
|
if (mcpArgs !== undefined) {
|
|
1098
1120
|
const call = readExecutableMcpCall(mcpArgs, session.descriptors);
|
|
@@ -1110,17 +1132,7 @@ class CursorAdapter implements ProviderAdapter {
|
|
|
1110
1132
|
arguments: call.arguments,
|
|
1111
1133
|
};
|
|
1112
1134
|
}
|
|
1113
|
-
|
|
1114
|
-
// Cursor-native variant) are not executed here. Cursor's own executor on
|
|
1115
|
-
// the local CLI handles them; the MCP channel is the correct path for tool
|
|
1116
|
-
// calls routed through this gateway.
|
|
1117
|
-
//
|
|
1118
|
-
// A direct-forward path (`readHostExecCall`) was implemented experimentally
|
|
1119
|
-
// but produced protocol errors in practice: the typed result frames it sent
|
|
1120
|
-
// back (shell_result, write_result, read_result) were not matched correctly
|
|
1121
|
-
// by the server under all code paths, causing the Run to stall or the model
|
|
1122
|
-
// to receive malformed tool output. The path has been removed; use MCP.
|
|
1123
|
-
const refusal = "Cursor host tools are not executed by this gateway";
|
|
1135
|
+
const refusal = "this tool is not one the request declared";
|
|
1124
1136
|
await handle.write(buildExecThrow(execId, refusal, execIdString));
|
|
1125
1137
|
await handle.write(buildExecStreamClose(execId, execIdString));
|
|
1126
1138
|
return { kind: "handled" };
|
|
@@ -1,129 +1,129 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Vendored from `cursor-opencode-provider` (protocol layer only).
|
|
3
|
-
*
|
|
4
|
-
* MIT License
|
|
5
|
-
*
|
|
6
|
-
* Copyright (c) 2026 Oleksiy Akimov
|
|
7
|
-
*
|
|
8
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
9
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
10
|
-
* in the Software without restriction, including without limitation the rights
|
|
11
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
12
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
13
|
-
* furnished to do so, subject to the following conditions:
|
|
14
|
-
*
|
|
15
|
-
* The above copyright notice and this permission notice shall be included in all
|
|
16
|
-
* copies or substantial portions of the Software.
|
|
17
|
-
*
|
|
18
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
19
|
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
20
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
21
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
22
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
23
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
|
-
* SOFTWARE.
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
import { gunzipSync, gzipSync } from "node:zlib"
|
|
28
|
-
|
|
29
|
-
export const FLAG_GZIP = 0x01
|
|
30
|
-
export const FLAG_END_STREAM = 0x02
|
|
31
|
-
|
|
32
|
-
export type Frame = {
|
|
33
|
-
flags: number
|
|
34
|
-
payload: Uint8Array
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function encodeFrame(flags: number, payload: Uint8Array): Uint8Array {
|
|
38
|
-
let data = payload
|
|
39
|
-
if (flags & FLAG_GZIP) {
|
|
40
|
-
data = gzipSync(data)
|
|
41
|
-
}
|
|
42
|
-
const len = data.length
|
|
43
|
-
const header = new Uint8Array(5)
|
|
44
|
-
header[0] = flags
|
|
45
|
-
header[1] = (len >> 24) & 0xff
|
|
46
|
-
header[2] = (len >> 16) & 0xff
|
|
47
|
-
header[3] = (len >> 8) & 0xff
|
|
48
|
-
header[4] = len & 0xff
|
|
49
|
-
const out = new Uint8Array(5 + len)
|
|
50
|
-
out.set(header, 0)
|
|
51
|
-
out.set(data, 5)
|
|
52
|
-
return out
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export function decodeFramePayload(frame: Frame): Uint8Array {
|
|
56
|
-
return frame.flags & FLAG_GZIP ? gunzipSync(frame.payload) : frame.payload
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export function* streamFrames(buffer: Uint8Array): Generator<Frame, void, void> {
|
|
60
|
-
let offset = 0
|
|
61
|
-
while (offset + 5 <= buffer.length) {
|
|
62
|
-
const flags = buffer[offset] ?? 0
|
|
63
|
-
const len =
|
|
64
|
-
(((buffer[offset + 1] ?? 0) << 24) |
|
|
65
|
-
((buffer[offset + 2] ?? 0) << 16) |
|
|
66
|
-
((buffer[offset + 3] ?? 0) << 8) |
|
|
67
|
-
(buffer[offset + 4] ?? 0)) >>> 0
|
|
68
|
-
const start = offset + 5
|
|
69
|
-
if (start + len > buffer.length) break
|
|
70
|
-
yield { flags, payload: buffer.subarray(start, start + len) }
|
|
71
|
-
offset = start + len
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export async function* asyncStreamFrames(
|
|
76
|
-
stream: ReadableStream<Uint8Array>,
|
|
77
|
-
): AsyncGenerator<Frame, void, void> {
|
|
78
|
-
const reader = stream.getReader()
|
|
79
|
-
const chunks: Uint8Array[] = []
|
|
80
|
-
let pendingLength = 0
|
|
81
|
-
|
|
82
|
-
try {
|
|
83
|
-
while (true) {
|
|
84
|
-
const { done, value } = await reader.read()
|
|
85
|
-
if (done && chunks.length === 0) return
|
|
86
|
-
if (value) {
|
|
87
|
-
chunks.push(value)
|
|
88
|
-
pendingLength += value.length
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Try to parse frames from accumulated data
|
|
92
|
-
const merged = mergeChunks(chunks)
|
|
93
|
-
const frames = Array.from(streamFrames(merged))
|
|
94
|
-
const consumed = frames.reduce((sum, f) => sum + 5 + f.payload.length, 0)
|
|
95
|
-
|
|
96
|
-
if (frames.length > 0) {
|
|
97
|
-
for (const frame of frames) {
|
|
98
|
-
yield frame
|
|
99
|
-
}
|
|
100
|
-
// Keep any trailing bytes for next iteration
|
|
101
|
-
const remaining = merged.subarray(consumed)
|
|
102
|
-
chunks.length = 0
|
|
103
|
-
if (remaining.length > 0) {
|
|
104
|
-
chunks.push(remaining)
|
|
105
|
-
pendingLength = remaining.length
|
|
106
|
-
} else {
|
|
107
|
-
pendingLength = 0
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (done) return
|
|
112
|
-
}
|
|
113
|
-
} finally {
|
|
114
|
-
reader.releaseLock()
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function mergeChunks(chunks: Uint8Array[]): Uint8Array {
|
|
119
|
-
const first = chunks[0]
|
|
120
|
-
if (chunks.length === 1 && first !== undefined) return first
|
|
121
|
-
const total = chunks.reduce((s, c) => s + c.length, 0)
|
|
122
|
-
const merged = new Uint8Array(total)
|
|
123
|
-
let offset = 0
|
|
124
|
-
for (const c of chunks) {
|
|
125
|
-
merged.set(c, offset)
|
|
126
|
-
offset += c.length
|
|
127
|
-
}
|
|
128
|
-
return merged
|
|
129
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Vendored from `cursor-opencode-provider` (protocol layer only).
|
|
3
|
+
*
|
|
4
|
+
* MIT License
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) 2026 Oleksiy Akimov
|
|
7
|
+
*
|
|
8
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
9
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
10
|
+
* in the Software without restriction, including without limitation the rights
|
|
11
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
12
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
13
|
+
* furnished to do so, subject to the following conditions:
|
|
14
|
+
*
|
|
15
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
16
|
+
* copies or substantial portions of the Software.
|
|
17
|
+
*
|
|
18
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
19
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
20
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
21
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
22
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
23
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
|
+
* SOFTWARE.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { gunzipSync, gzipSync } from "node:zlib"
|
|
28
|
+
|
|
29
|
+
export const FLAG_GZIP = 0x01
|
|
30
|
+
export const FLAG_END_STREAM = 0x02
|
|
31
|
+
|
|
32
|
+
export type Frame = {
|
|
33
|
+
flags: number
|
|
34
|
+
payload: Uint8Array
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function encodeFrame(flags: number, payload: Uint8Array): Uint8Array {
|
|
38
|
+
let data = payload
|
|
39
|
+
if (flags & FLAG_GZIP) {
|
|
40
|
+
data = gzipSync(data)
|
|
41
|
+
}
|
|
42
|
+
const len = data.length
|
|
43
|
+
const header = new Uint8Array(5)
|
|
44
|
+
header[0] = flags
|
|
45
|
+
header[1] = (len >> 24) & 0xff
|
|
46
|
+
header[2] = (len >> 16) & 0xff
|
|
47
|
+
header[3] = (len >> 8) & 0xff
|
|
48
|
+
header[4] = len & 0xff
|
|
49
|
+
const out = new Uint8Array(5 + len)
|
|
50
|
+
out.set(header, 0)
|
|
51
|
+
out.set(data, 5)
|
|
52
|
+
return out
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function decodeFramePayload(frame: Frame): Uint8Array {
|
|
56
|
+
return frame.flags & FLAG_GZIP ? gunzipSync(frame.payload) : frame.payload
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function* streamFrames(buffer: Uint8Array): Generator<Frame, void, void> {
|
|
60
|
+
let offset = 0
|
|
61
|
+
while (offset + 5 <= buffer.length) {
|
|
62
|
+
const flags = buffer[offset] ?? 0
|
|
63
|
+
const len =
|
|
64
|
+
(((buffer[offset + 1] ?? 0) << 24) |
|
|
65
|
+
((buffer[offset + 2] ?? 0) << 16) |
|
|
66
|
+
((buffer[offset + 3] ?? 0) << 8) |
|
|
67
|
+
(buffer[offset + 4] ?? 0)) >>> 0
|
|
68
|
+
const start = offset + 5
|
|
69
|
+
if (start + len > buffer.length) break
|
|
70
|
+
yield { flags, payload: buffer.subarray(start, start + len) }
|
|
71
|
+
offset = start + len
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export async function* asyncStreamFrames(
|
|
76
|
+
stream: ReadableStream<Uint8Array>,
|
|
77
|
+
): AsyncGenerator<Frame, void, void> {
|
|
78
|
+
const reader = stream.getReader()
|
|
79
|
+
const chunks: Uint8Array[] = []
|
|
80
|
+
let pendingLength = 0
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
while (true) {
|
|
84
|
+
const { done, value } = await reader.read()
|
|
85
|
+
if (done && chunks.length === 0) return
|
|
86
|
+
if (value) {
|
|
87
|
+
chunks.push(value)
|
|
88
|
+
pendingLength += value.length
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Try to parse frames from accumulated data
|
|
92
|
+
const merged = mergeChunks(chunks)
|
|
93
|
+
const frames = Array.from(streamFrames(merged))
|
|
94
|
+
const consumed = frames.reduce((sum, f) => sum + 5 + f.payload.length, 0)
|
|
95
|
+
|
|
96
|
+
if (frames.length > 0) {
|
|
97
|
+
for (const frame of frames) {
|
|
98
|
+
yield frame
|
|
99
|
+
}
|
|
100
|
+
// Keep any trailing bytes for next iteration
|
|
101
|
+
const remaining = merged.subarray(consumed)
|
|
102
|
+
chunks.length = 0
|
|
103
|
+
if (remaining.length > 0) {
|
|
104
|
+
chunks.push(remaining)
|
|
105
|
+
pendingLength = remaining.length
|
|
106
|
+
} else {
|
|
107
|
+
pendingLength = 0
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (done) return
|
|
112
|
+
}
|
|
113
|
+
} finally {
|
|
114
|
+
reader.releaseLock()
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function mergeChunks(chunks: Uint8Array[]): Uint8Array {
|
|
119
|
+
const first = chunks[0]
|
|
120
|
+
if (chunks.length === 1 && first !== undefined) return first
|
|
121
|
+
const total = chunks.reduce((s, c) => s + c.length, 0)
|
|
122
|
+
const merged = new Uint8Array(total)
|
|
123
|
+
let offset = 0
|
|
124
|
+
for (const c of chunks) {
|
|
125
|
+
merged.set(c, offset)
|
|
126
|
+
offset += c.length
|
|
127
|
+
}
|
|
128
|
+
return merged
|
|
129
|
+
}
|