@srouterhq/server 0.0.0-stage → 0.9.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/LICENSE +21 -0
- package/client/dist/assets/am-TQ7Jmdqe.js +1 -0
- package/client/dist/assets/ar-Bf5a7yfC.js +1 -0
- package/client/dist/assets/az-CrosE1xH.js +1 -0
- package/client/dist/assets/bg-BvWnmcUz.js +1 -0
- package/client/dist/assets/bn-ams5TsJp.js +1 -0
- package/client/dist/assets/cs-Dh67CaJ_.js +1 -0
- package/client/dist/assets/da-9WlT8Iq7.js +1 -0
- package/client/dist/assets/de-lWhJJzz7.js +1 -0
- package/client/dist/assets/el-Cu7_GhNq.js +1 -0
- package/client/dist/assets/es-BZuzsBcP.js +1 -0
- package/client/dist/assets/fa-Dwdn-jKS.js +1 -0
- package/client/dist/assets/fi-BFrFyOTy.js +1 -0
- package/client/dist/assets/fr-XhbtmPpj.js +1 -0
- package/client/dist/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/client/dist/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/client/dist/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/client/dist/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/client/dist/assets/geist-mono-cyrillic-ext-wght-normal-I4S5GZfc.woff2 +0 -0
- package/client/dist/assets/geist-mono-cyrillic-wght-normal-BmXc_FBt.woff2 +0 -0
- package/client/dist/assets/geist-mono-latin-ext-wght-normal-DrnZ1wKl.woff2 +0 -0
- package/client/dist/assets/geist-mono-latin-wght-normal-B_7UjwxQ.woff2 +0 -0
- package/client/dist/assets/geist-mono-symbols2-wght-normal-GZpp1pK2.woff2 +0 -0
- package/client/dist/assets/geist-mono-vietnamese-wght-normal-D8KDMBhC.woff2 +0 -0
- package/client/dist/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/client/dist/assets/gu-D0uhLUB8.js +1 -0
- package/client/dist/assets/ha-CVlm9pqp.js +1 -0
- package/client/dist/assets/he-CMIFeoWr.js +1 -0
- package/client/dist/assets/hi-ENTwhBpn.js +1 -0
- package/client/dist/assets/hr-fc03vMy_.js +1 -0
- package/client/dist/assets/hu-YBo2BIYt.js +1 -0
- package/client/dist/assets/id-BEWXYX69.js +1 -0
- package/client/dist/assets/ig-BKLZFKka.js +1 -0
- package/client/dist/assets/index-D9I4KtvR.js +145 -0
- package/client/dist/assets/index-txhEmCdb.css +2 -0
- package/client/dist/assets/it-Bzmei1Y2.js +1 -0
- package/client/dist/assets/ja-BfcpuqEl.js +1 -0
- package/client/dist/assets/ka-CnWjRgLo.js +1 -0
- package/client/dist/assets/km-BAjPR3Qj.js +1 -0
- package/client/dist/assets/kn-B15WKU5m.js +1 -0
- package/client/dist/assets/ko-C61fAB-w.js +1 -0
- package/client/dist/assets/lt-CYTr4_YW.js +1 -0
- package/client/dist/assets/ml-CtqxgyH3.js +1 -0
- package/client/dist/assets/mr-X5TMY7aR.js +1 -0
- package/client/dist/assets/ms-DSg8RqPS.js +1 -0
- package/client/dist/assets/my-D7RqR8uE.js +1 -0
- package/client/dist/assets/ne-DaflxkDj.js +1 -0
- package/client/dist/assets/nl-BOqqtsC8.js +1 -0
- package/client/dist/assets/no-BSHp_StB.js +1 -0
- package/client/dist/assets/or-BkLR9Vub.js +1 -0
- package/client/dist/assets/pa-CarJ2XgI.js +1 -0
- package/client/dist/assets/pl-C2n0V-0k.js +1 -0
- package/client/dist/assets/pt-BR-CqJdNhg2.js +1 -0
- package/client/dist/assets/pt-PT-C_HpMyKz.js +1 -0
- package/client/dist/assets/ro-DVRZqSAS.js +1 -0
- package/client/dist/assets/ru-DwAJG7VC.js +1 -0
- package/client/dist/assets/si-BxUezXm0.js +1 -0
- package/client/dist/assets/sk-eI1VcIfY.js +1 -0
- package/client/dist/assets/sr-CJF3auCr.js +1 -0
- package/client/dist/assets/srouter-logo-C6ZfjGIi.svg +14 -0
- package/client/dist/assets/sv-nqvwHAq_.js +1 -0
- package/client/dist/assets/sw-DWynE2pW.js +1 -0
- package/client/dist/assets/ta-BVM12knC.js +1 -0
- package/client/dist/assets/te-oyzEkF7_.js +1 -0
- package/client/dist/assets/th-COVeE0ZQ.js +1 -0
- package/client/dist/assets/tl-DsJANnQz.js +1 -0
- package/client/dist/assets/tr-B-Fl-urO.js +1 -0
- package/client/dist/assets/uk-C5qketAO.js +1 -0
- package/client/dist/assets/ur-QU9KugfV.js +1 -0
- package/client/dist/assets/uz-DL9cG_mY.js +1 -0
- package/client/dist/assets/vi-CQiVy3Pu.js +1 -0
- package/client/dist/assets/yo-DqazMdrZ.js +1 -0
- package/client/dist/assets/zh-CN-DnYL_Kio.js +1 -0
- package/client/dist/assets/zh-TW-5bdbosKj.js +1 -0
- package/client/dist/favicon.svg +14 -0
- package/client/dist/icons.svg +24 -0
- package/client/dist/index.html +37 -0
- package/dist/app.d.ts +4 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +372 -0
- package/dist/app.js.map +1 -0
- package/dist/db/index.d.ts +49 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +203 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/migrate/TEMPLATE.d.ts +4 -0
- package/dist/db/migrate/TEMPLATE.d.ts.map +1 -0
- package/dist/db/migrate/TEMPLATE.js +18 -0
- package/dist/db/migrate/TEMPLATE.js.map +1 -0
- package/dist/db/migrate/cli.d.ts +2 -0
- package/dist/db/migrate/cli.d.ts.map +1 -0
- package/dist/db/migrate/cli.js +177 -0
- package/dist/db/migrate/cli.js.map +1 -0
- package/dist/db/migrate/defaults.d.ts +52 -0
- package/dist/db/migrate/defaults.d.ts.map +1 -0
- package/dist/db/migrate/defaults.js +126 -0
- package/dist/db/migrate/defaults.js.map +1 -0
- package/dist/db/migrate/runner.d.ts +16 -0
- package/dist/db/migrate/runner.d.ts.map +1 -0
- package/dist/db/migrate/runner.js +178 -0
- package/dist/db/migrate/runner.js.map +1 -0
- package/dist/db/migrations/20260101_000000_legacy_baseline.d.ts +4 -0
- package/dist/db/migrations/20260101_000000_legacy_baseline.d.ts.map +1 -0
- package/dist/db/migrations/20260101_000000_legacy_baseline.js +2240 -0
- package/dist/db/migrations/20260101_000000_legacy_baseline.js.map +1 -0
- package/dist/db/migrations/20260627_000001_custom_provider_modalities.d.ts +4 -0
- package/dist/db/migrations/20260627_000001_custom_provider_modalities.d.ts.map +1 -0
- package/dist/db/migrations/20260627_000001_custom_provider_modalities.js +27 -0
- package/dist/db/migrations/20260627_000001_custom_provider_modalities.js.map +1 -0
- package/dist/db/migrations/20260627_000002_catalog_model_state.d.ts +4 -0
- package/dist/db/migrations/20260627_000002_catalog_model_state.d.ts.map +1 -0
- package/dist/db/migrations/20260627_000002_catalog_model_state.js +30 -0
- package/dist/db/migrations/20260627_000002_catalog_model_state.js.map +1 -0
- package/dist/db/migrations/20260628_120000_request_aggregates.d.ts +6 -0
- package/dist/db/migrations/20260628_120000_request_aggregates.d.ts.map +1 -0
- package/dist/db/migrations/20260628_120000_request_aggregates.js +104 -0
- package/dist/db/migrations/20260628_120000_request_aggregates.js.map +1 -0
- package/dist/db/migrations/20260630_000001_github_gpt41_context.d.ts +9 -0
- package/dist/db/migrations/20260630_000001_github_gpt41_context.d.ts.map +1 -0
- package/dist/db/migrations/20260630_000001_github_gpt41_context.js +22 -0
- package/dist/db/migrations/20260630_000001_github_gpt41_context.js.map +1 -0
- package/dist/db/migrations/20260706_000001_request_client_info.d.ts +4 -0
- package/dist/db/migrations/20260706_000001_request_client_info.d.ts.map +1 -0
- package/dist/db/migrations/20260706_000001_request_client_info.js +30 -0
- package/dist/db/migrations/20260706_000001_request_client_info.js.map +1 -0
- package/dist/db/migrations/20260706_000002_custom_model_tool_support.d.ts +17 -0
- package/dist/db/migrations/20260706_000002_custom_model_tool_support.d.ts.map +1 -0
- package/dist/db/migrations/20260706_000002_custom_model_tool_support.js +20 -0
- package/dist/db/migrations/20260706_000002_custom_model_tool_support.js.map +1 -0
- package/dist/db/migrations/20260714_000001_profile_chain_backfill.d.ts +12 -0
- package/dist/db/migrations/20260714_000001_profile_chain_backfill.d.ts.map +1 -0
- package/dist/db/migrations/20260714_000001_profile_chain_backfill.js +45 -0
- package/dist/db/migrations/20260714_000001_profile_chain_backfill.js.map +1 -0
- package/dist/db/migrations/20260720_000001_key_health_error.d.ts +5 -0
- package/dist/db/migrations/20260720_000001_key_health_error.d.ts.map +1 -0
- package/dist/db/migrations/20260720_000001_key_health_error.js +16 -0
- package/dist/db/migrations/20260720_000001_key_health_error.js.map +1 -0
- package/dist/db/migrations/20260726_000001_cooldown_probe_provenance.d.ts +4 -0
- package/dist/db/migrations/20260726_000001_cooldown_probe_provenance.d.ts.map +1 -0
- package/dist/db/migrations/20260726_000001_cooldown_probe_provenance.js +38 -0
- package/dist/db/migrations/20260726_000001_cooldown_probe_provenance.js.map +1 -0
- package/dist/db/migrations/20260726_000002_request_attempts.d.ts +4 -0
- package/dist/db/migrations/20260726_000002_request_attempts.d.ts.map +1 -0
- package/dist/db/migrations/20260726_000002_request_attempts.js +43 -0
- package/dist/db/migrations/20260726_000002_request_attempts.js.map +1 -0
- package/dist/db/migrations/20260726_000003_model_source_provenance.d.ts +4 -0
- package/dist/db/migrations/20260726_000003_model_source_provenance.d.ts.map +1 -0
- package/dist/db/migrations/20260726_000003_model_source_provenance.js +80 -0
- package/dist/db/migrations/20260726_000003_model_source_provenance.js.map +1 -0
- package/dist/db/migrations/20260726_000004_media_model_meta.d.ts +4 -0
- package/dist/db/migrations/20260726_000004_media_model_meta.d.ts.map +1 -0
- package/dist/db/migrations/20260726_000004_media_model_meta.js +34 -0
- package/dist/db/migrations/20260726_000004_media_model_meta.js.map +1 -0
- package/dist/db/migrations/20260726_000005_request_served_model.d.ts +4 -0
- package/dist/db/migrations/20260726_000005_request_served_model.d.ts.map +1 -0
- package/dist/db/migrations/20260726_000005_request_served_model.js +31 -0
- package/dist/db/migrations/20260726_000005_request_served_model.js.map +1 -0
- package/dist/db/migrations/20260726_000006_attempt_error_summary.d.ts +4 -0
- package/dist/db/migrations/20260726_000006_attempt_error_summary.d.ts.map +1 -0
- package/dist/db/migrations/20260726_000006_attempt_error_summary.js +29 -0
- package/dist/db/migrations/20260726_000006_attempt_error_summary.js.map +1 -0
- package/dist/db/migrations/20260727_000001_agent_compatibility.d.ts +4 -0
- package/dist/db/migrations/20260727_000001_agent_compatibility.d.ts.map +1 -0
- package/dist/db/migrations/20260727_000001_agent_compatibility.js +41 -0
- package/dist/db/migrations/20260727_000001_agent_compatibility.js.map +1 -0
- package/dist/db/migrations/20260728_000001_tombstone_provenance.d.ts +12 -0
- package/dist/db/migrations/20260728_000001_tombstone_provenance.d.ts.map +1 -0
- package/dist/db/migrations/20260728_000001_tombstone_provenance.js +37 -0
- package/dist/db/migrations/20260728_000001_tombstone_provenance.js.map +1 -0
- package/dist/db/migrations/20260729_000001_custom_model_endpoint_identity.d.ts +4 -0
- package/dist/db/migrations/20260729_000001_custom_model_endpoint_identity.d.ts.map +1 -0
- package/dist/db/migrations/20260729_000001_custom_model_endpoint_identity.js +145 -0
- package/dist/db/migrations/20260729_000001_custom_model_endpoint_identity.js.map +1 -0
- package/dist/db/migrations/20260802_000001_custom_endpoint_host_labels.d.ts +6 -0
- package/dist/db/migrations/20260802_000001_custom_endpoint_host_labels.d.ts.map +1 -0
- package/dist/db/migrations/20260802_000001_custom_endpoint_host_labels.js +52 -0
- package/dist/db/migrations/20260802_000001_custom_endpoint_host_labels.js.map +1 -0
- package/dist/db/migrations/20260805_000001_key_model_scope.d.ts +6 -0
- package/dist/db/migrations/20260805_000001_key_model_scope.d.ts.map +1 -0
- package/dist/db/migrations/20260805_000001_key_model_scope.js +17 -0
- package/dist/db/migrations/20260805_000001_key_model_scope.js.map +1 -0
- package/dist/db/migrations/20260805_000002_client_profiles.d.ts +4 -0
- package/dist/db/migrations/20260805_000002_client_profiles.d.ts.map +1 -0
- package/dist/db/migrations/20260805_000002_client_profiles.js +33 -0
- package/dist/db/migrations/20260805_000002_client_profiles.js.map +1 -0
- package/dist/db/migrations/20260810_000001_api_key_proxy.d.ts +4 -0
- package/dist/db/migrations/20260810_000001_api_key_proxy.d.ts.map +1 -0
- package/dist/db/migrations/20260810_000001_api_key_proxy.js +33 -0
- package/dist/db/migrations/20260810_000001_api_key_proxy.js.map +1 -0
- package/dist/db/migrations/20260819_000001_custom_model_tombstones.d.ts +4 -0
- package/dist/db/migrations/20260819_000001_custom_model_tombstones.d.ts.map +1 -0
- package/dist/db/migrations/20260819_000001_custom_model_tombstones.js +19 -0
- package/dist/db/migrations/20260819_000001_custom_model_tombstones.js.map +1 -0
- package/dist/db/migrations/20260820_000001_playground_conversations.d.ts +4 -0
- package/dist/db/migrations/20260820_000001_playground_conversations.d.ts.map +1 -0
- package/dist/db/migrations/20260820_000001_playground_conversations.js +47 -0
- package/dist/db/migrations/20260820_000001_playground_conversations.js.map +1 -0
- package/dist/db/migrations/20260823_000001_server_logs.d.ts +4 -0
- package/dist/db/migrations/20260823_000001_server_logs.d.ts.map +1 -0
- package/dist/db/migrations/20260823_000001_server_logs.js +53 -0
- package/dist/db/migrations/20260823_000001_server_logs.js.map +1 -0
- package/dist/db/migrations/20260823_000002_backups_table.d.ts +8 -0
- package/dist/db/migrations/20260823_000002_backups_table.d.ts.map +1 -0
- package/dist/db/migrations/20260823_000002_backups_table.js +22 -0
- package/dist/db/migrations/20260823_000002_backups_table.js.map +1 -0
- package/dist/db/migrations/20260823_000003_attempt_key_label.d.ts +19 -0
- package/dist/db/migrations/20260823_000003_attempt_key_label.d.ts.map +1 -0
- package/dist/db/migrations/20260823_000003_attempt_key_label.js +28 -0
- package/dist/db/migrations/20260823_000003_attempt_key_label.js.map +1 -0
- package/dist/db/migrations/20260823_000004_profile_auto_include.d.ts +14 -0
- package/dist/db/migrations/20260823_000004_profile_auto_include.d.ts.map +1 -0
- package/dist/db/migrations/20260823_000004_profile_auto_include.js +25 -0
- package/dist/db/migrations/20260823_000004_profile_auto_include.js.map +1 -0
- package/dist/db/migrations/20260901_000001_idempotency_claims.d.ts +4 -0
- package/dist/db/migrations/20260901_000001_idempotency_claims.d.ts.map +1 -0
- package/dist/db/migrations/20260901_000001_idempotency_claims.js +46 -0
- package/dist/db/migrations/20260901_000001_idempotency_claims.js.map +1 -0
- package/dist/db/migrations/20260901_000002_quota_observation_lookup.d.ts +4 -0
- package/dist/db/migrations/20260901_000002_quota_observation_lookup.d.ts.map +1 -0
- package/dist/db/migrations/20260901_000002_quota_observation_lookup.js +37 -0
- package/dist/db/migrations/20260901_000002_quota_observation_lookup.js.map +1 -0
- package/dist/db/migrations/20260901_000003_request_caller.d.ts +4 -0
- package/dist/db/migrations/20260901_000003_request_caller.d.ts.map +1 -0
- package/dist/db/migrations/20260901_000003_request_caller.js +27 -0
- package/dist/db/migrations/20260901_000003_request_caller.js.map +1 -0
- package/dist/db/migrations/20260902_000001_analytics_latency_percentile_index.d.ts +4 -0
- package/dist/db/migrations/20260902_000001_analytics_latency_percentile_index.d.ts.map +1 -0
- package/dist/db/migrations/20260902_000001_analytics_latency_percentile_index.js +35 -0
- package/dist/db/migrations/20260902_000001_analytics_latency_percentile_index.js.map +1 -0
- package/dist/db/migrations/20260903_000001_mcp_enabled_default.d.ts +4 -0
- package/dist/db/migrations/20260903_000001_mcp_enabled_default.d.ts.map +1 -0
- package/dist/db/migrations/20260903_000001_mcp_enabled_default.js +37 -0
- package/dist/db/migrations/20260903_000001_mcp_enabled_default.js.map +1 -0
- package/dist/db/migrations/20260903_000002_response_cache.d.ts +4 -0
- package/dist/db/migrations/20260903_000002_response_cache.d.ts.map +1 -0
- package/dist/db/migrations/20260903_000002_response_cache.js +53 -0
- package/dist/db/migrations/20260903_000002_response_cache.js.map +1 -0
- package/dist/db/migrations/20260904_000001_key_monthly_budget.d.ts +13 -0
- package/dist/db/migrations/20260904_000001_key_monthly_budget.d.ts.map +1 -0
- package/dist/db/migrations/20260904_000001_key_monthly_budget.js +35 -0
- package/dist/db/migrations/20260904_000001_key_monthly_budget.js.map +1 -0
- package/dist/db/migrations/20260913_000001_request_model_attribution.d.ts +4 -0
- package/dist/db/migrations/20260913_000001_request_model_attribution.d.ts.map +1 -0
- package/dist/db/migrations/20260913_000001_request_model_attribution.js +68 -0
- package/dist/db/migrations/20260913_000001_request_model_attribution.js.map +1 -0
- package/dist/db/migrations/20260914_000001_key_monthly_usage.d.ts +5 -0
- package/dist/db/migrations/20260914_000001_key_monthly_usage.d.ts.map +1 -0
- package/dist/db/migrations/20260914_000001_key_monthly_usage.js +37 -0
- package/dist/db/migrations/20260914_000001_key_monthly_usage.js.map +1 -0
- package/dist/db/migrations/20260915_000001_quota_snapshot_freshness.d.ts +4 -0
- package/dist/db/migrations/20260915_000001_quota_snapshot_freshness.d.ts.map +1 -0
- package/dist/db/migrations/20260915_000001_quota_snapshot_freshness.js +28 -0
- package/dist/db/migrations/20260915_000001_quota_snapshot_freshness.js.map +1 -0
- package/dist/db/migrations/20261004_000001_unified_key_sr_prefix.d.ts +4 -0
- package/dist/db/migrations/20261004_000001_unified_key_sr_prefix.d.ts.map +1 -0
- package/dist/db/migrations/20261004_000001_unified_key_sr_prefix.js +25 -0
- package/dist/db/migrations/20261004_000001_unified_key_sr_prefix.js.map +1 -0
- package/dist/db/migrations/20261005_000001_key_budget_period.d.ts +10 -0
- package/dist/db/migrations/20261005_000001_key_budget_period.d.ts.map +1 -0
- package/dist/db/migrations/20261005_000001_key_budget_period.js +78 -0
- package/dist/db/migrations/20261005_000001_key_budget_period.js.map +1 -0
- package/dist/db/migrations/20261005_000002_key_budget_period_auto.d.ts +21 -0
- package/dist/db/migrations/20261005_000002_key_budget_period_auto.d.ts.map +1 -0
- package/dist/db/migrations/20261005_000002_key_budget_period_auto.js +41 -0
- package/dist/db/migrations/20261005_000002_key_budget_period_auto.js.map +1 -0
- package/dist/db/model-pricing.d.ts +39 -0
- package/dist/db/model-pricing.d.ts.map +1 -0
- package/dist/db/model-pricing.js +274 -0
- package/dist/db/model-pricing.js.map +1 -0
- package/dist/db/node-sqlite.d.ts +9 -0
- package/dist/db/node-sqlite.d.ts.map +1 -0
- package/dist/db/node-sqlite.js +87 -0
- package/dist/db/node-sqlite.js.map +1 -0
- package/dist/db/types.d.ts +22 -0
- package/dist/db/types.d.ts.map +1 -0
- package/dist/db/types.js +2 -0
- package/dist/db/types.js.map +1 -0
- package/dist/docs/docs-page.d.ts +2 -0
- package/dist/docs/docs-page.d.ts.map +1 -0
- package/dist/docs/docs-page.js +319 -0
- package/dist/docs/docs-page.js.map +1 -0
- package/dist/docs/openapi.d.ts +1896 -0
- package/dist/docs/openapi.d.ts.map +1 -0
- package/dist/docs/openapi.js +1096 -0
- package/dist/docs/openapi.js.map +1 -0
- package/dist/env.d.ts +2 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +9 -0
- package/dist/env.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +144 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/anthropic-documents.d.ts +41 -0
- package/dist/lib/anthropic-documents.d.ts.map +1 -0
- package/dist/lib/anthropic-documents.js +132 -0
- package/dist/lib/anthropic-documents.js.map +1 -0
- package/dist/lib/app-version.d.ts +5 -0
- package/dist/lib/app-version.d.ts.map +1 -0
- package/dist/lib/app-version.js +61 -0
- package/dist/lib/app-version.js.map +1 -0
- package/dist/lib/attempt-trace.d.ts +23 -0
- package/dist/lib/attempt-trace.d.ts.map +1 -0
- package/dist/lib/attempt-trace.js +32 -0
- package/dist/lib/attempt-trace.js.map +1 -0
- package/dist/lib/budget.d.ts +6 -0
- package/dist/lib/budget.d.ts.map +1 -0
- package/dist/lib/budget.js +40 -0
- package/dist/lib/budget.js.map +1 -0
- package/dist/lib/client-classifier.d.ts +10 -0
- package/dist/lib/client-classifier.d.ts.map +1 -0
- package/dist/lib/client-classifier.js +126 -0
- package/dist/lib/client-classifier.js.map +1 -0
- package/dist/lib/client-context.d.ts +10 -0
- package/dist/lib/client-context.d.ts.map +1 -0
- package/dist/lib/client-context.js +39 -0
- package/dist/lib/client-context.js.map +1 -0
- package/dist/lib/config.d.ts +33 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +94 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/content.d.ts +33 -0
- package/dist/lib/content.d.ts.map +1 -0
- package/dist/lib/content.js +201 -0
- package/dist/lib/content.js.map +1 -0
- package/dist/lib/credential.d.ts +11 -0
- package/dist/lib/credential.d.ts.map +1 -0
- package/dist/lib/credential.js +18 -0
- package/dist/lib/credential.js.map +1 -0
- package/dist/lib/crypto.d.ts +31 -0
- package/dist/lib/crypto.d.ts.map +1 -0
- package/dist/lib/crypto.js +206 -0
- package/dist/lib/crypto.js.map +1 -0
- package/dist/lib/custom-provider-cleanup.d.ts +13 -0
- package/dist/lib/custom-provider-cleanup.d.ts.map +1 -0
- package/dist/lib/custom-provider-cleanup.js +43 -0
- package/dist/lib/custom-provider-cleanup.js.map +1 -0
- package/dist/lib/db-backup.d.ts +31 -0
- package/dist/lib/db-backup.d.ts.map +1 -0
- package/dist/lib/db-backup.js +255 -0
- package/dist/lib/db-backup.js.map +1 -0
- package/dist/lib/endpoint-scope.d.ts +42 -0
- package/dist/lib/endpoint-scope.d.ts.map +1 -0
- package/dist/lib/endpoint-scope.js +95 -0
- package/dist/lib/endpoint-scope.js.map +1 -0
- package/dist/lib/env-drift.d.ts +17 -0
- package/dist/lib/env-drift.d.ts.map +1 -0
- package/dist/lib/env-drift.js +108 -0
- package/dist/lib/env-drift.js.map +1 -0
- package/dist/lib/error-classify.d.ts +40 -0
- package/dist/lib/error-classify.d.ts.map +1 -0
- package/dist/lib/error-classify.js +687 -0
- package/dist/lib/error-classify.js.map +1 -0
- package/dist/lib/error-redaction.d.ts +8 -0
- package/dist/lib/error-redaction.d.ts.map +1 -0
- package/dist/lib/error-redaction.js +45 -0
- package/dist/lib/error-redaction.js.map +1 -0
- package/dist/lib/fallback-loop.d.ts +306 -0
- package/dist/lib/fallback-loop.d.ts.map +1 -0
- package/dist/lib/fallback-loop.js +1336 -0
- package/dist/lib/fallback-loop.js.map +1 -0
- package/dist/lib/file-permissions.d.ts +98 -0
- package/dist/lib/file-permissions.d.ts.map +1 -0
- package/dist/lib/file-permissions.js +159 -0
- package/dist/lib/file-permissions.js.map +1 -0
- package/dist/lib/gemini-wire.d.ts +76 -0
- package/dist/lib/gemini-wire.d.ts.map +1 -0
- package/dist/lib/gemini-wire.js +392 -0
- package/dist/lib/gemini-wire.js.map +1 -0
- package/dist/lib/guardrails.d.ts +37 -0
- package/dist/lib/guardrails.d.ts.map +1 -0
- package/dist/lib/guardrails.js +106 -0
- package/dist/lib/guardrails.js.map +1 -0
- package/dist/lib/header-value.d.ts +8 -0
- package/dist/lib/header-value.d.ts.map +1 -0
- package/dist/lib/header-value.js +55 -0
- package/dist/lib/header-value.js.map +1 -0
- package/dist/lib/image-normalize.d.ts +21 -0
- package/dist/lib/image-normalize.d.ts.map +1 -0
- package/dist/lib/image-normalize.js +218 -0
- package/dist/lib/image-normalize.js.map +1 -0
- package/dist/lib/inbound-chat.d.ts +48 -0
- package/dist/lib/inbound-chat.d.ts.map +1 -0
- package/dist/lib/inbound-chat.js +406 -0
- package/dist/lib/inbound-chat.js.map +1 -0
- package/dist/lib/key-parser.d.ts +79 -0
- package/dist/lib/key-parser.d.ts.map +1 -0
- package/dist/lib/key-parser.js +701 -0
- package/dist/lib/key-parser.js.map +1 -0
- package/dist/lib/key-proxy.d.ts +49 -0
- package/dist/lib/key-proxy.d.ts.map +1 -0
- package/dist/lib/key-proxy.js +92 -0
- package/dist/lib/key-proxy.js.map +1 -0
- package/dist/lib/log-redaction.d.ts +28 -0
- package/dist/lib/log-redaction.d.ts.map +1 -0
- package/dist/lib/log-redaction.js +166 -0
- package/dist/lib/log-redaction.js.map +1 -0
- package/dist/lib/model-scope.d.ts +9 -0
- package/dist/lib/model-scope.d.ts.map +1 -0
- package/dist/lib/model-scope.js +29 -0
- package/dist/lib/model-scope.js.map +1 -0
- package/dist/lib/one-time-code.d.ts +15 -0
- package/dist/lib/one-time-code.d.ts.map +1 -0
- package/dist/lib/one-time-code.js +41 -0
- package/dist/lib/one-time-code.js.map +1 -0
- package/dist/lib/output-cap.d.ts +24 -0
- package/dist/lib/output-cap.d.ts.map +1 -0
- package/dist/lib/output-cap.js +80 -0
- package/dist/lib/output-cap.js.map +1 -0
- package/dist/lib/password.d.ts +3 -0
- package/dist/lib/password.d.ts.map +1 -0
- package/dist/lib/password.js +27 -0
- package/dist/lib/password.js.map +1 -0
- package/dist/lib/process-safety-net.d.ts +32 -0
- package/dist/lib/process-safety-net.d.ts.map +1 -0
- package/dist/lib/process-safety-net.js +123 -0
- package/dist/lib/process-safety-net.js.map +1 -0
- package/dist/lib/provider-identity.d.ts +37 -0
- package/dist/lib/provider-identity.d.ts.map +1 -0
- package/dist/lib/provider-identity.js +110 -0
- package/dist/lib/provider-identity.js.map +1 -0
- package/dist/lib/provider-size-parser.d.ts +6 -0
- package/dist/lib/provider-size-parser.d.ts.map +1 -0
- package/dist/lib/provider-size-parser.js +72 -0
- package/dist/lib/provider-size-parser.js.map +1 -0
- package/dist/lib/provider-timeout.d.ts +17 -0
- package/dist/lib/provider-timeout.d.ts.map +1 -0
- package/dist/lib/provider-timeout.js +70 -0
- package/dist/lib/provider-timeout.js.map +1 -0
- package/dist/lib/proxy.d.ts +180 -0
- package/dist/lib/proxy.d.ts.map +1 -0
- package/dist/lib/proxy.js +1002 -0
- package/dist/lib/proxy.js.map +1 -0
- package/dist/lib/request-log.d.ts +4 -0
- package/dist/lib/request-log.d.ts.map +1 -0
- package/dist/lib/request-log.js +161 -0
- package/dist/lib/request-log.js.map +1 -0
- package/dist/lib/reset-code.d.ts +5 -0
- package/dist/lib/reset-code.d.ts.map +1 -0
- package/dist/lib/reset-code.js +39 -0
- package/dist/lib/reset-code.js.map +1 -0
- package/dist/lib/retry-hint.d.ts +14 -0
- package/dist/lib/retry-hint.d.ts.map +1 -0
- package/dist/lib/retry-hint.js +36 -0
- package/dist/lib/retry-hint.js.map +1 -0
- package/dist/lib/route-live.d.ts +29 -0
- package/dist/lib/route-live.d.ts.map +1 -0
- package/dist/lib/route-live.js +97 -0
- package/dist/lib/route-live.js.map +1 -0
- package/dist/lib/sampling-params.d.ts +183 -0
- package/dist/lib/sampling-params.d.ts.map +1 -0
- package/dist/lib/sampling-params.js +386 -0
- package/dist/lib/sampling-params.js.map +1 -0
- package/dist/lib/scheduler.d.ts +13 -0
- package/dist/lib/scheduler.d.ts.map +1 -0
- package/dist/lib/scheduler.js +11 -0
- package/dist/lib/scheduler.js.map +1 -0
- package/dist/lib/served-model.d.ts +16 -0
- package/dist/lib/served-model.d.ts.map +1 -0
- package/dist/lib/served-model.js +0 -0
- package/dist/lib/served-model.js.map +1 -0
- package/dist/lib/server-logs.d.ts +132 -0
- package/dist/lib/server-logs.d.ts.map +1 -0
- package/dist/lib/server-logs.js +473 -0
- package/dist/lib/server-logs.js.map +1 -0
- package/dist/lib/setup-code.d.ts +5 -0
- package/dist/lib/setup-code.d.ts.map +1 -0
- package/dist/lib/setup-code.js +35 -0
- package/dist/lib/setup-code.js.map +1 -0
- package/dist/lib/structured-output.d.ts +9 -0
- package/dist/lib/structured-output.d.ts.map +1 -0
- package/dist/lib/structured-output.js +72 -0
- package/dist/lib/structured-output.js.map +1 -0
- package/dist/lib/system-prompt.d.ts +30 -0
- package/dist/lib/system-prompt.d.ts.map +1 -0
- package/dist/lib/system-prompt.js +66 -0
- package/dist/lib/system-prompt.js.map +1 -0
- package/dist/lib/task-type.d.ts +18 -0
- package/dist/lib/task-type.d.ts.map +1 -0
- package/dist/lib/task-type.js +92 -0
- package/dist/lib/task-type.js.map +1 -0
- package/dist/lib/think-tags.d.ts +37 -0
- package/dist/lib/think-tags.d.ts.map +1 -0
- package/dist/lib/think-tags.js +203 -0
- package/dist/lib/think-tags.js.map +1 -0
- package/dist/lib/tool-args.d.ts +36 -0
- package/dist/lib/tool-args.d.ts.map +1 -0
- package/dist/lib/tool-args.js +190 -0
- package/dist/lib/tool-args.js.map +1 -0
- package/dist/lib/tool-call-rescue.d.ts +62 -0
- package/dist/lib/tool-call-rescue.d.ts.map +1 -0
- package/dist/lib/tool-call-rescue.js +258 -0
- package/dist/lib/tool-call-rescue.js.map +1 -0
- package/dist/lib/tool-capability.d.ts +13 -0
- package/dist/lib/tool-capability.d.ts.map +1 -0
- package/dist/lib/tool-capability.js +70 -0
- package/dist/lib/tool-capability.js.map +1 -0
- package/dist/lib/tool-validate.d.ts +44 -0
- package/dist/lib/tool-validate.d.ts.map +1 -0
- package/dist/lib/tool-validate.js +165 -0
- package/dist/lib/tool-validate.js.map +1 -0
- package/dist/lib/ttfb-budget.d.ts +13 -0
- package/dist/lib/ttfb-budget.d.ts.map +1 -0
- package/dist/lib/ttfb-budget.js +153 -0
- package/dist/lib/ttfb-budget.js.map +1 -0
- package/dist/lib/url-guard.d.ts +47 -0
- package/dist/lib/url-guard.d.ts.map +1 -0
- package/dist/lib/url-guard.js +229 -0
- package/dist/lib/url-guard.js.map +1 -0
- package/dist/lib/wake-detect.d.ts +12 -0
- package/dist/lib/wake-detect.d.ts.map +1 -0
- package/dist/lib/wake-detect.js +99 -0
- package/dist/lib/wake-detect.js.map +1 -0
- package/dist/middleware/errorHandler.d.ts +3 -0
- package/dist/middleware/errorHandler.d.ts.map +1 -0
- package/dist/middleware/errorHandler.js +45 -0
- package/dist/middleware/errorHandler.js.map +1 -0
- package/dist/middleware/rateLimit.d.ts +4 -0
- package/dist/middleware/rateLimit.d.ts.map +1 -0
- package/dist/middleware/rateLimit.js +125 -0
- package/dist/middleware/rateLimit.js.map +1 -0
- package/dist/middleware/requireAuth.d.ts +3 -0
- package/dist/middleware/requireAuth.d.ts.map +1 -0
- package/dist/middleware/requireAuth.js +17 -0
- package/dist/middleware/requireAuth.js.map +1 -0
- package/dist/providers/aclide.d.ts +18 -0
- package/dist/providers/aclide.d.ts.map +1 -0
- package/dist/providers/aclide.js +174 -0
- package/dist/providers/aclide.js.map +1 -0
- package/dist/providers/aihorde.d.ts +42 -0
- package/dist/providers/aihorde.d.ts.map +1 -0
- package/dist/providers/aihorde.js +192 -0
- package/dist/providers/aihorde.js.map +1 -0
- package/dist/providers/airforce.d.ts +11 -0
- package/dist/providers/airforce.d.ts.map +1 -0
- package/dist/providers/airforce.js +32 -0
- package/dist/providers/airforce.js.map +1 -0
- package/dist/providers/base.d.ts +177 -0
- package/dist/providers/base.d.ts.map +1 -0
- package/dist/providers/base.js +354 -0
- package/dist/providers/base.js.map +1 -0
- package/dist/providers/blaze.d.ts +11 -0
- package/dist/providers/blaze.d.ts.map +1 -0
- package/dist/providers/blaze.js +33 -0
- package/dist/providers/blaze.js.map +1 -0
- package/dist/providers/blockrun.d.ts +14 -0
- package/dist/providers/blockrun.d.ts.map +1 -0
- package/dist/providers/blockrun.js +36 -0
- package/dist/providers/blockrun.js.map +1 -0
- package/dist/providers/clod.d.ts +11 -0
- package/dist/providers/clod.d.ts.map +1 -0
- package/dist/providers/clod.js +39 -0
- package/dist/providers/clod.js.map +1 -0
- package/dist/providers/cloudflare.d.ts +15 -0
- package/dist/providers/cloudflare.d.ts.map +1 -0
- package/dist/providers/cloudflare.js +175 -0
- package/dist/providers/cloudflare.js.map +1 -0
- package/dist/providers/cohere.d.ts +11 -0
- package/dist/providers/cohere.d.ts.map +1 -0
- package/dist/providers/cohere.js +117 -0
- package/dist/providers/cohere.js.map +1 -0
- package/dist/providers/dreamprompting.d.ts +11 -0
- package/dist/providers/dreamprompting.d.ts.map +1 -0
- package/dist/providers/dreamprompting.js +41 -0
- package/dist/providers/dreamprompting.js.map +1 -0
- package/dist/providers/electronhub.d.ts +10 -0
- package/dist/providers/electronhub.d.ts.map +1 -0
- package/dist/providers/electronhub.js +56 -0
- package/dist/providers/electronhub.js.map +1 -0
- package/dist/providers/experiential.d.ts +10 -0
- package/dist/providers/experiential.d.ts.map +1 -0
- package/dist/providers/experiential.js +33 -0
- package/dist/providers/experiential.js.map +1 -0
- package/dist/providers/gizmo.d.ts +15 -0
- package/dist/providers/gizmo.d.ts.map +1 -0
- package/dist/providers/gizmo.js +54 -0
- package/dist/providers/gizmo.js.map +1 -0
- package/dist/providers/google.d.ts +30 -0
- package/dist/providers/google.d.ts.map +1 -0
- package/dist/providers/google.js +766 -0
- package/dist/providers/google.js.map +1 -0
- package/dist/providers/index.d.ts +13 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +561 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/llmtr.d.ts +15 -0
- package/dist/providers/llmtr.d.ts.map +1 -0
- package/dist/providers/llmtr.js +51 -0
- package/dist/providers/llmtr.js.map +1 -0
- package/dist/providers/logfare.d.ts +11 -0
- package/dist/providers/logfare.d.ts.map +1 -0
- package/dist/providers/logfare.js +33 -0
- package/dist/providers/logfare.js.map +1 -0
- package/dist/providers/lucidity.d.ts +11 -0
- package/dist/providers/lucidity.d.ts.map +1 -0
- package/dist/providers/lucidity.js +37 -0
- package/dist/providers/lucidity.js.map +1 -0
- package/dist/providers/modelscope.d.ts +50 -0
- package/dist/providers/modelscope.d.ts.map +1 -0
- package/dist/providers/modelscope.js +143 -0
- package/dist/providers/modelscope.js.map +1 -0
- package/dist/providers/moondream.d.ts +17 -0
- package/dist/providers/moondream.d.ts.map +1 -0
- package/dist/providers/moondream.js +140 -0
- package/dist/providers/moondream.js.map +1 -0
- package/dist/providers/openai-compat.d.ts +140 -0
- package/dist/providers/openai-compat.d.ts.map +1 -0
- package/dist/providers/openai-compat.js +537 -0
- package/dist/providers/openai-compat.js.map +1 -0
- package/dist/providers/opencode-free.d.ts +24 -0
- package/dist/providers/opencode-free.d.ts.map +1 -0
- package/dist/providers/opencode-free.js +262 -0
- package/dist/providers/opencode-free.js.map +1 -0
- package/dist/providers/pollinations.d.ts +32 -0
- package/dist/providers/pollinations.d.ts.map +1 -0
- package/dist/providers/pollinations.js +65 -0
- package/dist/providers/pollinations.js.map +1 -0
- package/dist/providers/router9.d.ts +16 -0
- package/dist/providers/router9.d.ts.map +1 -0
- package/dist/providers/router9.js +42 -0
- package/dist/providers/router9.js.map +1 -0
- package/dist/providers/sail.d.ts +44 -0
- package/dist/providers/sail.d.ts.map +1 -0
- package/dist/providers/sail.js +302 -0
- package/dist/providers/sail.js.map +1 -0
- package/dist/providers/septor.d.ts +10 -0
- package/dist/providers/septor.d.ts.map +1 -0
- package/dist/providers/septor.js +36 -0
- package/dist/providers/septor.js.map +1 -0
- package/dist/providers/speechify.d.ts +14 -0
- package/dist/providers/speechify.d.ts.map +1 -0
- package/dist/providers/speechify.js +28 -0
- package/dist/providers/speechify.js.map +1 -0
- package/dist/providers/speka.d.ts +12 -0
- package/dist/providers/speka.d.ts.map +1 -0
- package/dist/providers/speka.js +37 -0
- package/dist/providers/speka.js.map +1 -0
- package/dist/providers/waterfall.d.ts +11 -0
- package/dist/providers/waterfall.d.ts.map +1 -0
- package/dist/providers/waterfall.js +33 -0
- package/dist/providers/waterfall.js.map +1 -0
- package/dist/providers/xkiro.d.ts +71 -0
- package/dist/providers/xkiro.d.ts.map +1 -0
- package/dist/providers/xkiro.js +119 -0
- package/dist/providers/xkiro.js.map +1 -0
- package/dist/providers/zhipu.d.ts +43 -0
- package/dist/providers/zhipu.d.ts.map +1 -0
- package/dist/providers/zhipu.js +95 -0
- package/dist/providers/zhipu.js.map +1 -0
- package/dist/routes/analytics.d.ts +2 -0
- package/dist/routes/analytics.d.ts.map +1 -0
- package/dist/routes/analytics.js +738 -0
- package/dist/routes/analytics.js.map +1 -0
- package/dist/routes/anthropic.d.ts +7 -0
- package/dist/routes/anthropic.d.ts.map +1 -0
- package/dist/routes/anthropic.js +1076 -0
- package/dist/routes/anthropic.js.map +1 -0
- package/dist/routes/auth.d.ts +2 -0
- package/dist/routes/auth.d.ts.map +1 -0
- package/dist/routes/auth.js +310 -0
- package/dist/routes/auth.js.map +1 -0
- package/dist/routes/backups.d.ts +2 -0
- package/dist/routes/backups.d.ts.map +1 -0
- package/dist/routes/backups.js +127 -0
- package/dist/routes/backups.js.map +1 -0
- package/dist/routes/cache.d.ts +2 -0
- package/dist/routes/cache.d.ts.map +1 -0
- package/dist/routes/cache.js +40 -0
- package/dist/routes/cache.js.map +1 -0
- package/dist/routes/client-profiles.d.ts +2 -0
- package/dist/routes/client-profiles.d.ts.map +1 -0
- package/dist/routes/client-profiles.js +128 -0
- package/dist/routes/client-profiles.js.map +1 -0
- package/dist/routes/compression.d.ts +2 -0
- package/dist/routes/compression.d.ts.map +1 -0
- package/dist/routes/compression.js +82 -0
- package/dist/routes/compression.js.map +1 -0
- package/dist/routes/conversations.d.ts +3 -0
- package/dist/routes/conversations.d.ts.map +1 -0
- package/dist/routes/conversations.js +215 -0
- package/dist/routes/conversations.js.map +1 -0
- package/dist/routes/docs.d.ts +2 -0
- package/dist/routes/docs.d.ts.map +1 -0
- package/dist/routes/docs.js +21 -0
- package/dist/routes/docs.js.map +1 -0
- package/dist/routes/embeddings.d.ts +2 -0
- package/dist/routes/embeddings.d.ts.map +1 -0
- package/dist/routes/embeddings.js +255 -0
- package/dist/routes/embeddings.js.map +1 -0
- package/dist/routes/fallback.d.ts +2 -0
- package/dist/routes/fallback.d.ts.map +1 -0
- package/dist/routes/fallback.js +631 -0
- package/dist/routes/fallback.js.map +1 -0
- package/dist/routes/free-tier.d.ts +22 -0
- package/dist/routes/free-tier.d.ts.map +1 -0
- package/dist/routes/free-tier.js +175 -0
- package/dist/routes/free-tier.js.map +1 -0
- package/dist/routes/gemini.d.ts +2 -0
- package/dist/routes/gemini.d.ts.map +1 -0
- package/dist/routes/gemini.js +218 -0
- package/dist/routes/gemini.js.map +1 -0
- package/dist/routes/health.d.ts +2 -0
- package/dist/routes/health.d.ts.map +1 -0
- package/dist/routes/health.js +72 -0
- package/dist/routes/health.js.map +1 -0
- package/dist/routes/keys.d.ts +17 -0
- package/dist/routes/keys.d.ts.map +1 -0
- package/dist/routes/keys.js +1787 -0
- package/dist/routes/keys.js.map +1 -0
- package/dist/routes/logs.d.ts +2 -0
- package/dist/routes/logs.d.ts.map +1 -0
- package/dist/routes/logs.js +88 -0
- package/dist/routes/logs.js.map +1 -0
- package/dist/routes/mcp.d.ts +4 -0
- package/dist/routes/mcp.d.ts.map +1 -0
- package/dist/routes/mcp.js +563 -0
- package/dist/routes/mcp.js.map +1 -0
- package/dist/routes/media.d.ts +2 -0
- package/dist/routes/media.d.ts.map +1 -0
- package/dist/routes/media.js +180 -0
- package/dist/routes/media.js.map +1 -0
- package/dist/routes/models.d.ts +2 -0
- package/dist/routes/models.d.ts.map +1 -0
- package/dist/routes/models.js +439 -0
- package/dist/routes/models.js.map +1 -0
- package/dist/routes/ollama.d.ts +7 -0
- package/dist/routes/ollama.d.ts.map +1 -0
- package/dist/routes/ollama.js +595 -0
- package/dist/routes/ollama.js.map +1 -0
- package/dist/routes/profiles.d.ts +3 -0
- package/dist/routes/profiles.d.ts.map +1 -0
- package/dist/routes/profiles.js +431 -0
- package/dist/routes/profiles.js.map +1 -0
- package/dist/routes/proxy.d.ts +32 -0
- package/dist/routes/proxy.d.ts.map +1 -0
- package/dist/routes/proxy.js +2793 -0
- package/dist/routes/proxy.js.map +1 -0
- package/dist/routes/responses.d.ts +1161 -0
- package/dist/routes/responses.d.ts.map +1 -0
- package/dist/routes/responses.js +1521 -0
- package/dist/routes/responses.js.map +1 -0
- package/dist/routes/settings.d.ts +2 -0
- package/dist/routes/settings.d.ts.map +1 -0
- package/dist/routes/settings.js +549 -0
- package/dist/routes/settings.js.map +1 -0
- package/dist/routes/status.d.ts +3 -0
- package/dist/routes/status.d.ts.map +1 -0
- package/dist/routes/status.js +214 -0
- package/dist/routes/status.js.map +1 -0
- package/dist/routes/update.d.ts +31 -0
- package/dist/routes/update.d.ts.map +1 -0
- package/dist/routes/update.js +536 -0
- package/dist/routes/update.js.map +1 -0
- package/dist/routes/url-tokens.d.ts +2 -0
- package/dist/routes/url-tokens.d.ts.map +1 -0
- package/dist/routes/url-tokens.js +31 -0
- package/dist/routes/url-tokens.js.map +1 -0
- package/dist/scripts/export-catalog.d.ts +2 -0
- package/dist/scripts/export-catalog.d.ts.map +1 -0
- package/dist/scripts/export-catalog.js +114 -0
- package/dist/scripts/export-catalog.js.map +1 -0
- package/dist/scripts/rotate-encryption-key.d.ts +81 -0
- package/dist/scripts/rotate-encryption-key.d.ts.map +1 -0
- package/dist/scripts/rotate-encryption-key.js +232 -0
- package/dist/scripts/rotate-encryption-key.js.map +1 -0
- package/dist/scripts/routing-sim.d.ts +2 -0
- package/dist/scripts/routing-sim.d.ts.map +1 -0
- package/dist/scripts/routing-sim.js +130 -0
- package/dist/scripts/routing-sim.js.map +1 -0
- package/dist/scripts/test-all-models.d.ts +2 -0
- package/dist/scripts/test-all-models.d.ts.map +1 -0
- package/dist/scripts/test-all-models.js +56 -0
- package/dist/scripts/test-all-models.js.map +1 -0
- package/dist/services/anthropic-map.d.ts +39 -0
- package/dist/services/anthropic-map.d.ts.map +1 -0
- package/dist/services/anthropic-map.js +138 -0
- package/dist/services/anthropic-map.js.map +1 -0
- package/dist/services/auth.d.ts +30 -0
- package/dist/services/auth.d.ts.map +1 -0
- package/dist/services/auth.js +124 -0
- package/dist/services/auth.js.map +1 -0
- package/dist/services/auto-discover.d.ts +77 -0
- package/dist/services/auto-discover.d.ts.map +1 -0
- package/dist/services/auto-discover.js +1047 -0
- package/dist/services/auto-discover.js.map +1 -0
- package/dist/services/backups.d.ts +67 -0
- package/dist/services/backups.d.ts.map +1 -0
- package/dist/services/backups.js +444 -0
- package/dist/services/backups.js.map +1 -0
- package/dist/services/builtin-model-discovery.d.ts +107 -0
- package/dist/services/builtin-model-discovery.d.ts.map +1 -0
- package/dist/services/builtin-model-discovery.js +296 -0
- package/dist/services/builtin-model-discovery.js.map +1 -0
- package/dist/services/cache.d.ts +190 -0
- package/dist/services/cache.d.ts.map +1 -0
- package/dist/services/cache.js +617 -0
- package/dist/services/cache.js.map +1 -0
- package/dist/services/catalog-sync.d.ts +257 -0
- package/dist/services/catalog-sync.d.ts.map +1 -0
- package/dist/services/catalog-sync.js +1041 -0
- package/dist/services/catalog-sync.js.map +1 -0
- package/dist/services/compression/config.d.ts +33 -0
- package/dist/services/compression/config.d.ts.map +1 -0
- package/dist/services/compression/config.js +158 -0
- package/dist/services/compression/config.js.map +1 -0
- package/dist/services/compression/engines/aging.d.ts +2 -0
- package/dist/services/compression/engines/aging.d.ts.map +1 -0
- package/dist/services/compression/engines/aging.js +53 -0
- package/dist/services/compression/engines/aging.js.map +1 -0
- package/dist/services/compression/engines/custom-filters.d.ts +4 -0
- package/dist/services/compression/engines/custom-filters.d.ts.map +1 -0
- package/dist/services/compression/engines/custom-filters.js +49 -0
- package/dist/services/compression/engines/custom-filters.js.map +1 -0
- package/dist/services/compression/engines/dedup.d.ts +2 -0
- package/dist/services/compression/engines/dedup.d.ts.map +1 -0
- package/dist/services/compression/engines/dedup.js +61 -0
- package/dist/services/compression/engines/dedup.js.map +1 -0
- package/dist/services/compression/engines/filter-definitions.d.ts +75 -0
- package/dist/services/compression/engines/filter-definitions.d.ts.map +1 -0
- package/dist/services/compression/engines/filter-definitions.js +45 -0
- package/dist/services/compression/engines/filter-definitions.js.map +1 -0
- package/dist/services/compression/engines/hard-budget.d.ts +2 -0
- package/dist/services/compression/engines/hard-budget.d.ts.map +1 -0
- package/dist/services/compression/engines/hard-budget.js +52 -0
- package/dist/services/compression/engines/hard-budget.js.map +1 -0
- package/dist/services/compression/engines/index.d.ts +9 -0
- package/dist/services/compression/engines/index.d.ts.map +1 -0
- package/dist/services/compression/engines/index.js +9 -0
- package/dist/services/compression/engines/index.js.map +1 -0
- package/dist/services/compression/engines/jsoncompact.d.ts +3 -0
- package/dist/services/compression/engines/jsoncompact.d.ts.map +1 -0
- package/dist/services/compression/engines/jsoncompact.js +145 -0
- package/dist/services/compression/engines/jsoncompact.js.map +1 -0
- package/dist/services/compression/engines/lite.d.ts +2 -0
- package/dist/services/compression/engines/lite.d.ts.map +1 -0
- package/dist/services/compression/engines/lite.js +34 -0
- package/dist/services/compression/engines/lite.js.map +1 -0
- package/dist/services/compression/engines/read-lifecycle.d.ts +2 -0
- package/dist/services/compression/engines/read-lifecycle.d.ts.map +1 -0
- package/dist/services/compression/engines/read-lifecycle.js +70 -0
- package/dist/services/compression/engines/read-lifecycle.js.map +1 -0
- package/dist/services/compression/engines/relevance.d.ts +2 -0
- package/dist/services/compression/engines/relevance.d.ts.map +1 -0
- package/dist/services/compression/engines/relevance.js +74 -0
- package/dist/services/compression/engines/relevance.js.map +1 -0
- package/dist/services/compression/engines/toolfilter.d.ts +2 -0
- package/dist/services/compression/engines/toolfilter.d.ts.map +1 -0
- package/dist/services/compression/engines/toolfilter.js +195 -0
- package/dist/services/compression/engines/toolfilter.js.map +1 -0
- package/dist/services/compression/fidelity-gate.d.ts +12 -0
- package/dist/services/compression/fidelity-gate.d.ts.map +1 -0
- package/dist/services/compression/fidelity-gate.js +90 -0
- package/dist/services/compression/fidelity-gate.js.map +1 -0
- package/dist/services/compression/helpers.d.ts +9 -0
- package/dist/services/compression/helpers.d.ts.map +1 -0
- package/dist/services/compression/helpers.js +52 -0
- package/dist/services/compression/helpers.js.map +1 -0
- package/dist/services/compression/pipeline.d.ts +7 -0
- package/dist/services/compression/pipeline.d.ts.map +1 -0
- package/dist/services/compression/pipeline.js +245 -0
- package/dist/services/compression/pipeline.js.map +1 -0
- package/dist/services/compression/preservation.d.ts +27 -0
- package/dist/services/compression/preservation.d.ts.map +1 -0
- package/dist/services/compression/preservation.js +109 -0
- package/dist/services/compression/preservation.js.map +1 -0
- package/dist/services/compression/registry.d.ts +6 -0
- package/dist/services/compression/registry.d.ts.map +1 -0
- package/dist/services/compression/registry.js +16 -0
- package/dist/services/compression/registry.js.map +1 -0
- package/dist/services/compression/stats.d.ts +29 -0
- package/dist/services/compression/stats.d.ts.map +1 -0
- package/dist/services/compression/stats.js +55 -0
- package/dist/services/compression/stats.js.map +1 -0
- package/dist/services/compression/types.d.ts +90 -0
- package/dist/services/compression/types.d.ts.map +1 -0
- package/dist/services/compression/types.js +2 -0
- package/dist/services/compression/types.js.map +1 -0
- package/dist/services/context-handoff.d.ts +22 -0
- package/dist/services/context-handoff.d.ts.map +1 -0
- package/dist/services/context-handoff.js +164 -0
- package/dist/services/context-handoff.js.map +1 -0
- package/dist/services/cooldown-probe.d.ts +24 -0
- package/dist/services/cooldown-probe.d.ts.map +1 -0
- package/dist/services/cooldown-probe.js +181 -0
- package/dist/services/cooldown-probe.js.map +1 -0
- package/dist/services/custom-endpoint.d.ts +58 -0
- package/dist/services/custom-endpoint.d.ts.map +1 -0
- package/dist/services/custom-endpoint.js +167 -0
- package/dist/services/custom-endpoint.js.map +1 -0
- package/dist/services/custom-media-register.d.ts +22 -0
- package/dist/services/custom-media-register.d.ts.map +1 -0
- package/dist/services/custom-media-register.js +45 -0
- package/dist/services/custom-media-register.js.map +1 -0
- package/dist/services/custom-model-register.d.ts +50 -0
- package/dist/services/custom-model-register.d.ts.map +1 -0
- package/dist/services/custom-model-register.js +103 -0
- package/dist/services/custom-model-register.js.map +1 -0
- package/dist/services/custom-model-seed.d.ts +18 -0
- package/dist/services/custom-model-seed.d.ts.map +1 -0
- package/dist/services/custom-model-seed.js +42 -0
- package/dist/services/custom-model-seed.js.map +1 -0
- package/dist/services/custom-model-sync.d.ts +37 -0
- package/dist/services/custom-model-sync.d.ts.map +1 -0
- package/dist/services/custom-model-sync.js +118 -0
- package/dist/services/custom-model-sync.js.map +1 -0
- package/dist/services/custom-model-tombstone.d.ts +5 -0
- package/dist/services/custom-model-tombstone.d.ts.map +1 -0
- package/dist/services/custom-model-tombstone.js +30 -0
- package/dist/services/custom-model-tombstone.js.map +1 -0
- package/dist/services/declarative-config.d.ts +356 -0
- package/dist/services/declarative-config.d.ts.map +1 -0
- package/dist/services/declarative-config.js +427 -0
- package/dist/services/declarative-config.js.map +1 -0
- package/dist/services/degradation.d.ts +40 -0
- package/dist/services/degradation.d.ts.map +1 -0
- package/dist/services/degradation.js +119 -0
- package/dist/services/degradation.js.map +1 -0
- package/dist/services/embeddings.d.ts +76 -0
- package/dist/services/embeddings.d.ts.map +1 -0
- package/dist/services/embeddings.js +319 -0
- package/dist/services/embeddings.js.map +1 -0
- package/dist/services/fusion.d.ts +162 -0
- package/dist/services/fusion.d.ts.map +1 -0
- package/dist/services/fusion.js +789 -0
- package/dist/services/fusion.js.map +1 -0
- package/dist/services/gemini-map.d.ts +14 -0
- package/dist/services/gemini-map.d.ts.map +1 -0
- package/dist/services/gemini-map.js +78 -0
- package/dist/services/gemini-map.js.map +1 -0
- package/dist/services/health.d.ts +52 -0
- package/dist/services/health.d.ts.map +1 -0
- package/dist/services/health.js +352 -0
- package/dist/services/health.js.map +1 -0
- package/dist/services/idempotency.d.ts +49 -0
- package/dist/services/idempotency.d.ts.map +1 -0
- package/dist/services/idempotency.js +140 -0
- package/dist/services/idempotency.js.map +1 -0
- package/dist/services/key-budget.d.ts +74 -0
- package/dist/services/key-budget.d.ts.map +1 -0
- package/dist/services/key-budget.js +200 -0
- package/dist/services/key-budget.js.map +1 -0
- package/dist/services/media.d.ts +123 -0
- package/dist/services/media.d.ts.map +1 -0
- package/dist/services/media.js +954 -0
- package/dist/services/media.js.map +1 -0
- package/dist/services/model-discovery.d.ts +126 -0
- package/dist/services/model-discovery.d.ts.map +1 -0
- package/dist/services/model-discovery.js +662 -0
- package/dist/services/model-discovery.js.map +1 -0
- package/dist/services/model-groups.d.ts +149 -0
- package/dist/services/model-groups.d.ts.map +1 -0
- package/dist/services/model-groups.js +314 -0
- package/dist/services/model-groups.js.map +1 -0
- package/dist/services/model-listing.d.ts +18 -0
- package/dist/services/model-listing.d.ts.map +1 -0
- package/dist/services/model-listing.js +94 -0
- package/dist/services/model-listing.js.map +1 -0
- package/dist/services/model-retirement.d.ts +29 -0
- package/dist/services/model-retirement.d.ts.map +1 -0
- package/dist/services/model-retirement.js +86 -0
- package/dist/services/model-retirement.js.map +1 -0
- package/dist/services/model-state.d.ts +90 -0
- package/dist/services/model-state.d.ts.map +1 -0
- package/dist/services/model-state.js +342 -0
- package/dist/services/model-state.js.map +1 -0
- package/dist/services/model-weight-overrides.d.ts +48 -0
- package/dist/services/model-weight-overrides.d.ts.map +1 -0
- package/dist/services/model-weight-overrides.js +127 -0
- package/dist/services/model-weight-overrides.js.map +1 -0
- package/dist/services/notification-emitter.d.ts +50 -0
- package/dist/services/notification-emitter.d.ts.map +1 -0
- package/dist/services/notification-emitter.js +177 -0
- package/dist/services/notification-emitter.js.map +1 -0
- package/dist/services/opencode-free-sync.d.ts +29 -0
- package/dist/services/opencode-free-sync.d.ts.map +1 -0
- package/dist/services/opencode-free-sync.js +206 -0
- package/dist/services/opencode-free-sync.js.map +1 -0
- package/dist/services/penalty-inspector.d.ts +56 -0
- package/dist/services/penalty-inspector.d.ts.map +1 -0
- package/dist/services/penalty-inspector.js +167 -0
- package/dist/services/penalty-inspector.js.map +1 -0
- package/dist/services/profile-models.d.ts +5 -0
- package/dist/services/profile-models.d.ts.map +1 -0
- package/dist/services/profile-models.js +62 -0
- package/dist/services/profile-models.js.map +1 -0
- package/dist/services/provider-credential.d.ts +19 -0
- package/dist/services/provider-credential.d.ts.map +1 -0
- package/dist/services/provider-credential.js +51 -0
- package/dist/services/provider-credential.js.map +1 -0
- package/dist/services/provider-quota.d.ts +64 -0
- package/dist/services/provider-quota.d.ts.map +1 -0
- package/dist/services/provider-quota.js +563 -0
- package/dist/services/provider-quota.js.map +1 -0
- package/dist/services/quirks.d.ts +0 -0
- package/dist/services/quirks.d.ts.map +1 -0
- package/dist/services/quirks.js +0 -0
- package/dist/services/quirks.js.map +1 -0
- package/dist/services/quota-forecast.d.ts +50 -0
- package/dist/services/quota-forecast.d.ts.map +1 -0
- package/dist/services/quota-forecast.js +150 -0
- package/dist/services/quota-forecast.js.map +1 -0
- package/dist/services/quota-outlook.d.ts +4 -0
- package/dist/services/quota-outlook.d.ts.map +1 -0
- package/dist/services/quota-outlook.js +92 -0
- package/dist/services/quota-outlook.js.map +1 -0
- package/dist/services/ratelimit.d.ts +249 -0
- package/dist/services/ratelimit.d.ts.map +1 -0
- package/dist/services/ratelimit.js +1182 -0
- package/dist/services/ratelimit.js.map +1 -0
- package/dist/services/request-retention.d.ts +60 -0
- package/dist/services/request-retention.d.ts.map +1 -0
- package/dist/services/request-retention.js +229 -0
- package/dist/services/request-retention.js.map +1 -0
- package/dist/services/router.d.ts +384 -0
- package/dist/services/router.d.ts.map +1 -0
- package/dist/services/router.js +1986 -0
- package/dist/services/router.js.map +1 -0
- package/dist/services/scoring.d.ts +156 -0
- package/dist/services/scoring.d.ts.map +1 -0
- package/dist/services/scoring.js +435 -0
- package/dist/services/scoring.js.map +1 -0
- package/dist/services/url-tokens.d.ts +15 -0
- package/dist/services/url-tokens.d.ts.map +1 -0
- package/dist/services/url-tokens.js +55 -0
- package/dist/services/url-tokens.js.map +1 -0
- package/package.json +77 -4
- package/README.md +0 -3
|
@@ -0,0 +1,1041 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
import { getDb, getSetting, setSetting } from '../db/index.js';
|
|
3
|
+
import { hasProvider } from '../providers/index.js';
|
|
4
|
+
import { MEDIA_PLATFORMS, TRANSCRIPTION_PLATFORMS, VIDEO_PLATFORMS } from './media.js';
|
|
5
|
+
import { EMBEDDING_PLATFORMS } from './embeddings.js';
|
|
6
|
+
import { applyAllModelOverrides, refreshModelOverrideBaselines, routableContextWindow, applyModelOverrides, deleteTombstonedCatalogModels, isCatalogModelTombstoned, reinstateUpstreamRetiredCatalogModel, } from './model-state.js';
|
|
7
|
+
import { ensureAllModelsInProfiles } from './profile-models.js';
|
|
8
|
+
// Generative-media modalities are routed into the separate media_models table
|
|
9
|
+
// (see services/media.ts), never into the chat `models` table.
|
|
10
|
+
const MEDIA_MODALITIES = new Set(['image', 'audio']);
|
|
11
|
+
/**
|
|
12
|
+
* catalog-sync — keeps the local model catalog in step with the published one.
|
|
13
|
+
*
|
|
14
|
+
* Twice a day (and on on boot) the server pulls the signed catalog from the
|
|
15
|
+
* catalog service. Everyone gets the monthly snapshot, which self-heals on a
|
|
16
|
+
* regular cadence. The response is verified against a pinned Ed25519 public
|
|
17
|
+
* key over the exact bytes received; anything unsigned or tampered with is
|
|
18
|
+
* discarded, which means a compromised CDN or MITM cannot inject models or
|
|
19
|
+
* quirks into the router.
|
|
20
|
+
*
|
|
21
|
+
* The bundled migrations remain the baseline: a fetched catalog is applied
|
|
22
|
+
* only when it is NEWER than what the binary shipped with (MIN_CATALOG_VERSION
|
|
23
|
+
* below), so a stale monthly snapshot can never roll back models that a newer
|
|
24
|
+
* app version added via migrations.
|
|
25
|
+
*/
|
|
26
|
+
const DEFAULT_BASE_URL = 'https://api.freellmapi.co';
|
|
27
|
+
// The Ed25519 public key the production catalog is signed with. The private
|
|
28
|
+
// half was generated on the catalog host and has never left it. Self-hosters
|
|
29
|
+
// running their own catalog server can override both via env.
|
|
30
|
+
const PINNED_CATALOG_PUBKEY = `-----BEGIN PUBLIC KEY-----
|
|
31
|
+
MCowBQYDK2VwAyEAq9yv4+3EeyMHKsfVYBhkcz1lYgIXSUeHNnN6tNgYX3k=
|
|
32
|
+
-----END PUBLIC KEY-----
|
|
33
|
+
`;
|
|
34
|
+
// Catalogs older than this are ignored. Bump to today's date whenever a model
|
|
35
|
+
// migration lands, so the bundled DB is always the floor.
|
|
36
|
+
export const MIN_CATALOG_VERSION = '2026.06.07';
|
|
37
|
+
const DEFAULT_SYNC_INTERVAL_MS = 12 * 60 * 60 * 1000; // default: 12h
|
|
38
|
+
const BOOT_DELAY_MS = 10 * 1000; // let the server settle before first sync
|
|
39
|
+
const FETCH_TIMEOUT_MS = 20 * 1000;
|
|
40
|
+
// settings table keys
|
|
41
|
+
const SETTING_APPLIED_VERSION = 'catalog_applied_version';
|
|
42
|
+
const SETTING_APPLIED_TIER = 'catalog_applied_tier';
|
|
43
|
+
const SETTING_APPLIED_JSON = 'catalog_applied_json';
|
|
44
|
+
const SETTING_LAST_SYNC_MS = 'catalog_last_sync_ms';
|
|
45
|
+
const SETTING_LAST_ERROR = 'catalog_last_error';
|
|
46
|
+
const SETTING_SYNC_INTERVAL_HOURS = 'sync_interval_hours';
|
|
47
|
+
const SETTING_NOTIFICATIONS = 'sync_notifications';
|
|
48
|
+
const VALID_INTERVALS = ['off', 6, 12, 24];
|
|
49
|
+
export function getSyncIntervalHours() {
|
|
50
|
+
const raw = getSetting(SETTING_SYNC_INTERVAL_HOURS);
|
|
51
|
+
// Unset (fresh install) keeps the upstream default of twice daily; only an
|
|
52
|
+
// explicit 'off' disables the timer.
|
|
53
|
+
if (!raw)
|
|
54
|
+
return DEFAULT_SYNC_INTERVAL_MS / (60 * 60 * 1000);
|
|
55
|
+
if (raw === 'off')
|
|
56
|
+
return 'off';
|
|
57
|
+
const n = Number(raw);
|
|
58
|
+
return VALID_INTERVALS.includes(n) ? n : 'off';
|
|
59
|
+
}
|
|
60
|
+
export function setSyncIntervalHours(interval) {
|
|
61
|
+
setSetting(SETTING_SYNC_INTERVAL_HOURS, interval === 'off' ? 'off' : String(interval));
|
|
62
|
+
}
|
|
63
|
+
function syncIntervalMs() {
|
|
64
|
+
const hours = getSyncIntervalHours();
|
|
65
|
+
return hours === 'off' ? 0 : hours * 60 * 60 * 1000;
|
|
66
|
+
}
|
|
67
|
+
export function getNotifications() {
|
|
68
|
+
const raw = getSetting(SETTING_NOTIFICATIONS);
|
|
69
|
+
if (!raw)
|
|
70
|
+
return [];
|
|
71
|
+
try {
|
|
72
|
+
const parsed = JSON.parse(raw);
|
|
73
|
+
if (!Array.isArray(parsed))
|
|
74
|
+
return [];
|
|
75
|
+
const rows = parsed;
|
|
76
|
+
// A *pending* update notification is pinned above everything else no
|
|
77
|
+
// matter how old it is: the operator must keep seeing it (and the bell
|
|
78
|
+
// badge) until the update lands. Everything else — including a settled
|
|
79
|
+
// update row — is plain newest-first chronological order. Storage stays
|
|
80
|
+
// chronological; the pin is presentation, applied on read.
|
|
81
|
+
const pinned = (n) => n.type === 'update_available' && !n.read ? 1 : 0;
|
|
82
|
+
return [...rows].sort((a, b) => pinned(b) - pinned(a) || b.timestamp - a.timestamp);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export function addNotifications(notifs) {
|
|
89
|
+
if (notifs.length === 0)
|
|
90
|
+
return;
|
|
91
|
+
const existing = getNotifications();
|
|
92
|
+
// Prepend new ones, cap at 50.
|
|
93
|
+
const merged = [...notifs, ...existing].slice(0, 50);
|
|
94
|
+
merged.sort((a, b) => b.timestamp - a.timestamp);
|
|
95
|
+
setSetting(SETTING_NOTIFICATIONS, JSON.stringify(merged));
|
|
96
|
+
}
|
|
97
|
+
export function markNotificationsRead() {
|
|
98
|
+
// A pending update_available row deliberately survives the mark-all: the
|
|
99
|
+
// bell keeps its badge until the update actually lands (the periodic check
|
|
100
|
+
// settles it via settleUpdateNotifications). Everything else reads on cue.
|
|
101
|
+
const all = getNotifications().map(n => n.type === 'update_available' && !n.read ? n : { ...n, read: true });
|
|
102
|
+
setSetting(SETTING_NOTIFICATIONS, JSON.stringify(all));
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Settle the update-available reminder once the installed version caught up:
|
|
106
|
+
* collapse historical duplicates down to the newest row, mark it read (which
|
|
107
|
+
* un-pins it — it sinks into normal chronological order), and keep it in the
|
|
108
|
+
* list so the history still shows what happened.
|
|
109
|
+
*/
|
|
110
|
+
export function settleUpdateNotifications() {
|
|
111
|
+
const all = getNotifications();
|
|
112
|
+
const updates = all.filter(n => n.type === 'update_available');
|
|
113
|
+
if (updates.length === 0)
|
|
114
|
+
return;
|
|
115
|
+
if (updates.length === 1 && updates[0].read)
|
|
116
|
+
return;
|
|
117
|
+
updates.sort((a, b) => b.timestamp - a.timestamp);
|
|
118
|
+
const rest = all.filter(n => n.type !== 'update_available');
|
|
119
|
+
const merged = [{ ...updates[0], read: true }, ...rest]
|
|
120
|
+
.sort((a, b) => b.timestamp - a.timestamp)
|
|
121
|
+
.slice(0, 50);
|
|
122
|
+
setSetting(SETTING_NOTIFICATIONS, JSON.stringify(merged));
|
|
123
|
+
}
|
|
124
|
+
export function unreadNotificationCount() {
|
|
125
|
+
return getNotifications().filter(n => !n.read).length;
|
|
126
|
+
}
|
|
127
|
+
/** Remove all notifications of a given type (e.g. 'update_available' after user updates). */
|
|
128
|
+
export function removeNotificationsByType(type) {
|
|
129
|
+
const all = getNotifications();
|
|
130
|
+
const filtered = all.filter(n => n.type !== type);
|
|
131
|
+
if (filtered.length === all.length)
|
|
132
|
+
return; // nothing matched — skip the write
|
|
133
|
+
setSetting(SETTING_NOTIFICATIONS, JSON.stringify(filtered));
|
|
134
|
+
}
|
|
135
|
+
export function catalogBaseUrl() {
|
|
136
|
+
return (process.env.CATALOG_BASE_URL ?? DEFAULT_BASE_URL).replace(/\/$/, '');
|
|
137
|
+
}
|
|
138
|
+
function catalogPublicKey() {
|
|
139
|
+
const pem = process.env.CATALOG_PUBKEY ? process.env.CATALOG_PUBKEY.replace(/\\n/g, '\n') : PINNED_CATALOG_PUBKEY;
|
|
140
|
+
return crypto.createPublicKey({ key: pem, format: 'pem' });
|
|
141
|
+
}
|
|
142
|
+
/** Minimal structural check — enough to fail loudly on a wrong/garbled body. */
|
|
143
|
+
function isCatalog(value) {
|
|
144
|
+
const c = value;
|
|
145
|
+
return (!!c &&
|
|
146
|
+
typeof c.version === 'string' &&
|
|
147
|
+
(c.tier === 'live' || c.tier === 'monthly') &&
|
|
148
|
+
Array.isArray(c.models) &&
|
|
149
|
+
Array.isArray(c.quirks) &&
|
|
150
|
+
(c.embeddings === undefined ||
|
|
151
|
+
(Array.isArray(c.embeddings) &&
|
|
152
|
+
c.embeddings.every((m) => typeof m?.family === 'string' &&
|
|
153
|
+
typeof m?.platform === 'string' &&
|
|
154
|
+
typeof m?.modelId === 'string' &&
|
|
155
|
+
typeof m?.displayName === 'string' &&
|
|
156
|
+
typeof m?.dimensions === 'number' &&
|
|
157
|
+
typeof m?.priority === 'number' &&
|
|
158
|
+
typeof m?.enabled === 'boolean'))) &&
|
|
159
|
+
(c.transcriptionModels === undefined ||
|
|
160
|
+
(Array.isArray(c.transcriptionModels) &&
|
|
161
|
+
c.transcriptionModels.every((m) => typeof m?.platform === 'string' &&
|
|
162
|
+
typeof m?.modelId === 'string' &&
|
|
163
|
+
typeof m?.displayName === 'string' &&
|
|
164
|
+
typeof m?.priority === 'number' &&
|
|
165
|
+
typeof m?.enabled === 'boolean' &&
|
|
166
|
+
(m.subtitleFormats === undefined ||
|
|
167
|
+
(Array.isArray(m.subtitleFormats) && m.subtitleFormats.every((f) => typeof f === 'string'))) &&
|
|
168
|
+
(m.maxBytes === undefined || m.maxBytes === null || typeof m.maxBytes === 'number') &&
|
|
169
|
+
(m.requestStyle === undefined || m.requestStyle === null || typeof m.requestStyle === 'string')))) &&
|
|
170
|
+
(c.videoModels === undefined ||
|
|
171
|
+
(Array.isArray(c.videoModels) &&
|
|
172
|
+
c.videoModels.every((m) => typeof m?.platform === 'string' &&
|
|
173
|
+
typeof m?.modelId === 'string' &&
|
|
174
|
+
typeof m?.displayName === 'string' &&
|
|
175
|
+
typeof m?.priority === 'number' &&
|
|
176
|
+
typeof m?.enabled === 'boolean' &&
|
|
177
|
+
(m.quotaLabel === undefined || typeof m.quotaLabel === 'string') &&
|
|
178
|
+
(m.providerModelId === undefined || typeof m.providerModelId === 'string')))) &&
|
|
179
|
+
c.models.every((m) => typeof m?.platform === 'string' &&
|
|
180
|
+
typeof m?.modelId === 'string' &&
|
|
181
|
+
typeof m?.displayName === 'string' &&
|
|
182
|
+
typeof m?.enabled === 'boolean' &&
|
|
183
|
+
(m.requestStyle === undefined || m.requestStyle === null || typeof m.requestStyle === 'string') &&
|
|
184
|
+
!!m?.limits &&
|
|
185
|
+
typeof m.limits === 'object') &&
|
|
186
|
+
(c.managedPlatforms === undefined ||
|
|
187
|
+
(Array.isArray(c.managedPlatforms) && c.managedPlatforms.every((p) => typeof p === 'string'))) &&
|
|
188
|
+
c.quirks.every((q) => typeof q?.slug === 'string' && Array.isArray(q?.targets)));
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Apply a verified catalog to the local DB inside one transaction.
|
|
192
|
+
*
|
|
193
|
+
* Rules of engagement with user data:
|
|
194
|
+
* - metadata (name, ranks, limits, context, capabilities) tracks the catalog
|
|
195
|
+
* unless the user has an explicit local override;
|
|
196
|
+
* - catalog enabled=false force-disables (the model is dead upstream), but
|
|
197
|
+
* enabled=true never re-enables a model the user turned off themselves;
|
|
198
|
+
* - rows the user created (models.source = 'user': custom providers,
|
|
199
|
+
* declarative config, admin adds) are never updated, never deleted, and
|
|
200
|
+
* never adopted — on a platform:model_id collision the user row wins and
|
|
201
|
+
* the catalog entry is skipped outright;
|
|
202
|
+
* - catalog models the user deleted stay deleted via tombstones, while models
|
|
203
|
+
* auto-retired from an upstream 410/end-of-life response (#634) are only
|
|
204
|
+
* disabled — a catalog that still lists them lifts the retirement;
|
|
205
|
+
* - models that vanished from the catalog are deleted, exactly like the
|
|
206
|
+
* dead-model migrations do (fallback_config row first, FK order);
|
|
207
|
+
* - rows found by built-in model discovery (models.source = 'discovered',
|
|
208
|
+
* #1348) are a stopgap for platforms the catalog does not manage. The
|
|
209
|
+
* catalog outranks them: a listed platform:model_id adopts the discovered
|
|
210
|
+
* row as a catalog row, and once the catalog manages a platform at all,
|
|
211
|
+
* every other discovered row on it is retired.
|
|
212
|
+
*/
|
|
213
|
+
/** models.source for rows written by built-in model discovery (#1348). */
|
|
214
|
+
export const DISCOVERED_MODEL_SOURCE = 'discovered';
|
|
215
|
+
/** Every platform a catalog document manages: any row in any registry, plus
|
|
216
|
+
* the names-only managedPlatforms list. */
|
|
217
|
+
function catalogPlatforms(catalog) {
|
|
218
|
+
const platforms = new Set();
|
|
219
|
+
for (const m of catalog.models)
|
|
220
|
+
platforms.add(m.platform);
|
|
221
|
+
for (const m of catalog.embeddings ?? [])
|
|
222
|
+
platforms.add(m.platform);
|
|
223
|
+
for (const m of catalog.transcriptionModels ?? [])
|
|
224
|
+
platforms.add(m.platform);
|
|
225
|
+
for (const m of catalog.videoModels ?? [])
|
|
226
|
+
platforms.add(m.platform);
|
|
227
|
+
for (const p of catalog.managedPlatforms ?? [])
|
|
228
|
+
platforms.add(p);
|
|
229
|
+
return platforms;
|
|
230
|
+
}
|
|
231
|
+
let appliedPlatformsCache = null;
|
|
232
|
+
/**
|
|
233
|
+
* Platforms the catalog applied on this install manages (see catalogPlatforms),
|
|
234
|
+
* read from the cached verified document. Empty when no catalog has been
|
|
235
|
+
* applied yet. Built-in model discovery treats every platform in here as off
|
|
236
|
+
* limits, so the audited catalog, not an upstream /models list, decides what
|
|
237
|
+
* those providers serve.
|
|
238
|
+
*/
|
|
239
|
+
export function appliedCatalogPlatforms(db) {
|
|
240
|
+
const row = db.prepare('SELECT value FROM settings WHERE key = ?').get(SETTING_APPLIED_JSON);
|
|
241
|
+
const raw = row?.value ?? '';
|
|
242
|
+
if (appliedPlatformsCache && appliedPlatformsCache.raw === raw)
|
|
243
|
+
return appliedPlatformsCache.platforms;
|
|
244
|
+
let platforms = new Set();
|
|
245
|
+
if (raw) {
|
|
246
|
+
try {
|
|
247
|
+
const parsed = JSON.parse(raw);
|
|
248
|
+
if (isCatalog(parsed))
|
|
249
|
+
platforms = catalogPlatforms(parsed);
|
|
250
|
+
}
|
|
251
|
+
catch { /* an unreadable cache manages nothing; the DB checks still apply */ }
|
|
252
|
+
}
|
|
253
|
+
appliedPlatformsCache = { raw, platforms };
|
|
254
|
+
return platforms;
|
|
255
|
+
}
|
|
256
|
+
export function applyCatalog(db, catalog) {
|
|
257
|
+
return applyCatalogDetailed(db, catalog).counts;
|
|
258
|
+
}
|
|
259
|
+
/** Same apply, but also returns the per-platform ledger of which models
|
|
260
|
+
* changed — what feeds the "3 models added on Groq" bell notifications.
|
|
261
|
+
* Counts alone cannot name a provider or a model. */
|
|
262
|
+
export function applyCatalogDetailed(db, catalog) {
|
|
263
|
+
// One transaction for the whole apply (#1047): the body runs hundreds of
|
|
264
|
+
// individual statements, and under WAL each one outside a transaction is its
|
|
265
|
+
// own commit + fsync executed on the event loop. On slow storage that made
|
|
266
|
+
// the 12-hourly sync freeze every in-flight request — including the 3-query
|
|
267
|
+
// profile-activate POST — for minutes.
|
|
268
|
+
return db.transaction(() => applyCatalogInner(db, catalog))();
|
|
269
|
+
}
|
|
270
|
+
function applyCatalogInner(db, catalog) {
|
|
271
|
+
const counts = { updated: 0, inserted: 0, removed: 0, skippedUnknownPlatform: 0, quirks: 0 };
|
|
272
|
+
const details = { added: new Map(), removed: new Map(), updated: new Map() };
|
|
273
|
+
/** Note one model change against its provider. Names use the catalog
|
|
274
|
+
* display_name where we have it (that is what the user recognises) and fall
|
|
275
|
+
* back to the raw model_id for pruned rows, whose catalog entry is gone. */
|
|
276
|
+
const record = (ledger, platform, name) => {
|
|
277
|
+
const bucket = ledger.get(platform);
|
|
278
|
+
if (bucket)
|
|
279
|
+
bucket.push(name);
|
|
280
|
+
else
|
|
281
|
+
ledger.set(platform, [name]);
|
|
282
|
+
};
|
|
283
|
+
// The previous display_name/enabled are read alongside so an "updated" entry
|
|
284
|
+
// records what actually changed. Without them every catalog row looks
|
|
285
|
+
// updated on every sync, which drowns the real news.
|
|
286
|
+
const selectModel = db.prepare('SELECT id, enabled, source, display_name AS displayName FROM models WHERE platform = ? AND model_id = ?');
|
|
287
|
+
const updateModel = db.prepare(`
|
|
288
|
+
UPDATE models SET
|
|
289
|
+
display_name = @displayName, intelligence_rank = @intelligenceRank, speed_rank = @speedRank,
|
|
290
|
+
size_label = @sizeLabel, rpm_limit = @rpm, rpd_limit = @rpd, tpm_limit = @tpm, tpd_limit = @tpd,
|
|
291
|
+
monthly_token_budget = @monthlyTokenBudget, context_window = @contextWindow,
|
|
292
|
+
supports_vision = @supportsVision, supports_tools = @supportsTools,
|
|
293
|
+
enabled = @enabled
|
|
294
|
+
WHERE id = @id
|
|
295
|
+
`);
|
|
296
|
+
const adoptModel = db.prepare("UPDATE models SET source = 'catalog' WHERE id = ?");
|
|
297
|
+
const insertModel = db.prepare(`
|
|
298
|
+
INSERT INTO models (platform, model_id, display_name, intelligence_rank, speed_rank, size_label,
|
|
299
|
+
rpm_limit, rpd_limit, tpm_limit, tpd_limit, monthly_token_budget, context_window,
|
|
300
|
+
enabled, supports_vision, supports_tools, source)
|
|
301
|
+
VALUES (@platform, @modelId, @displayName, @intelligenceRank, @speedRank, @sizeLabel,
|
|
302
|
+
@rpm, @rpd, @tpm, @tpd, @monthlyTokenBudget, @contextWindow,
|
|
303
|
+
@enabled, @supportsVision, @supportsTools, 'catalog')
|
|
304
|
+
`);
|
|
305
|
+
// Generative-media models go to their own table (never the chat router's pool).
|
|
306
|
+
const selectMedia = db.prepare('SELECT id, enabled, display_name AS displayName FROM media_models WHERE platform = ? AND model_id = ?');
|
|
307
|
+
const updateMedia = db.prepare(`
|
|
308
|
+
UPDATE media_models SET
|
|
309
|
+
display_name = @displayName, modality = @modality, priority = @priority,
|
|
310
|
+
quota_label = @quotaLabel, enabled = @enabled, meta_json = @metaJson
|
|
311
|
+
WHERE id = @id
|
|
312
|
+
`);
|
|
313
|
+
const insertMedia = db.prepare(`
|
|
314
|
+
INSERT INTO media_models (platform, model_id, display_name, modality, priority, enabled, quota_label, meta_json)
|
|
315
|
+
VALUES (@platform, @modelId, @displayName, @modality, @priority, @enabled, @quotaLabel, @metaJson)
|
|
316
|
+
`);
|
|
317
|
+
// Transcription rows share media_models but carry adapter metadata in
|
|
318
|
+
// meta_json (subtitle capability, upload ceiling, request flavor).
|
|
319
|
+
const updateTranscription = db.prepare(`
|
|
320
|
+
UPDATE media_models SET
|
|
321
|
+
display_name = @displayName, modality = 'transcription', priority = @priority,
|
|
322
|
+
quota_label = @quotaLabel, enabled = @enabled, meta_json = @metaJson
|
|
323
|
+
WHERE id = @id
|
|
324
|
+
`);
|
|
325
|
+
const insertTranscription = db.prepare(`
|
|
326
|
+
INSERT INTO media_models (platform, model_id, display_name, modality, priority, enabled, quota_label, meta_json)
|
|
327
|
+
VALUES (@platform, @modelId, @displayName, 'transcription', @priority, @enabled, @quotaLabel, @metaJson)
|
|
328
|
+
`);
|
|
329
|
+
const selectEmbedding = db.prepare('SELECT id, enabled, display_name AS displayName FROM embedding_models WHERE platform = ? AND model_id = ?');
|
|
330
|
+
const updateEmbedding = db.prepare(`
|
|
331
|
+
UPDATE embedding_models SET
|
|
332
|
+
family = @family, display_name = @displayName, dimensions = @dimensions,
|
|
333
|
+
max_input_tokens = @maxInputTokens, priority = @priority,
|
|
334
|
+
quota_label = @quotaLabel, enabled = @enabled
|
|
335
|
+
WHERE id = @id
|
|
336
|
+
`);
|
|
337
|
+
const insertEmbedding = db.prepare(`
|
|
338
|
+
INSERT INTO embedding_models
|
|
339
|
+
(family, platform, model_id, display_name, dimensions, max_input_tokens,
|
|
340
|
+
priority, enabled, quota_label)
|
|
341
|
+
VALUES
|
|
342
|
+
(@family, @platform, @modelId, @displayName, @dimensions, @maxInputTokens,
|
|
343
|
+
@priority, @enabled, @quotaLabel)
|
|
344
|
+
`);
|
|
345
|
+
const apply = db.transaction(() => {
|
|
346
|
+
const inCatalog = new Set();
|
|
347
|
+
const inMediaCatalog = new Set();
|
|
348
|
+
const inEmbeddingCatalog = new Set();
|
|
349
|
+
const inTranscriptionCatalog = new Set();
|
|
350
|
+
const inVideoCatalog = new Set();
|
|
351
|
+
for (const m of catalog.models) {
|
|
352
|
+
// Media modalities are gated on MEDIA_PLATFORMS (decoupled from the chat
|
|
353
|
+
// provider registry) and routed to media_models, then skip the chat path.
|
|
354
|
+
const modality = m.modality ?? 'text';
|
|
355
|
+
if (MEDIA_MODALITIES.has(modality)) {
|
|
356
|
+
if (!MEDIA_PLATFORMS.has(m.platform)) {
|
|
357
|
+
counts.skippedUnknownPlatform++;
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
if (isCatalogModelTombstoned(db, 'media', m.platform, m.modelId))
|
|
361
|
+
continue;
|
|
362
|
+
inMediaCatalog.add(`${m.platform}:${m.modelId}`);
|
|
363
|
+
const mrow = selectMedia.get(m.platform, m.modelId);
|
|
364
|
+
// Generative-media meta carries only the adapter request flavor today;
|
|
365
|
+
// a row without one stores NULL so the adapter keeps its default.
|
|
366
|
+
const mmeta = {};
|
|
367
|
+
if (typeof m.requestStyle === 'string')
|
|
368
|
+
mmeta.requestStyle = m.requestStyle;
|
|
369
|
+
const mfields = {
|
|
370
|
+
displayName: m.displayName,
|
|
371
|
+
modality,
|
|
372
|
+
priority: m.intelligenceRank ?? 0,
|
|
373
|
+
quotaLabel: m.mediaNote ?? '',
|
|
374
|
+
metaJson: Object.keys(mmeta).length > 0 ? JSON.stringify(mmeta) : null,
|
|
375
|
+
};
|
|
376
|
+
if (mrow) {
|
|
377
|
+
const enabled = m.enabled ? mrow.enabled : 0; // catalog disable wins; local disable wins
|
|
378
|
+
updateMedia.run({ ...mfields, id: mrow.id, enabled });
|
|
379
|
+
counts.updated++;
|
|
380
|
+
if (mrow.displayName !== m.displayName)
|
|
381
|
+
record(details.updated, m.platform, m.displayName);
|
|
382
|
+
}
|
|
383
|
+
else {
|
|
384
|
+
insertMedia.run({ ...mfields, platform: m.platform, modelId: m.modelId, enabled: m.enabled ? 1 : 0 });
|
|
385
|
+
counts.inserted++;
|
|
386
|
+
record(details.added, m.platform, m.displayName);
|
|
387
|
+
}
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
if (m.platform === 'custom' || !hasProvider(m.platform)) {
|
|
391
|
+
// An older binary may receive models for providers it cannot route yet;
|
|
392
|
+
// skip them — they will appear after the user updates the app.
|
|
393
|
+
counts.skippedUnknownPlatform++;
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
if (isCatalogModelTombstoned(db, 'chat', m.platform, m.modelId))
|
|
397
|
+
continue;
|
|
398
|
+
// A model auto-retired from a 410/end-of-life response (#634) is disabled,
|
|
399
|
+
// not deleted. A catalog that STILL lists it — and lists it enabled — is
|
|
400
|
+
// newer evidence than that one provider response, so lift the retirement.
|
|
401
|
+
if (m.enabled)
|
|
402
|
+
reinstateUpstreamRetiredCatalogModel(db, m.platform, m.modelId);
|
|
403
|
+
inCatalog.add(`${m.platform}:${m.modelId}`);
|
|
404
|
+
const row = selectModel.get(m.platform, m.modelId);
|
|
405
|
+
// Collision rule: if the user hand-added a model and the catalog later
|
|
406
|
+
// ships the same platform:model_id, the user row wins — the catalog
|
|
407
|
+
// neither clobbers its metadata nor adopts it (same spirit as the
|
|
408
|
+
// never-touch rule for custom-provider models). The row also survives
|
|
409
|
+
// the prune below because the delete pass only considers source='catalog'.
|
|
410
|
+
if (row && row.source === 'user')
|
|
411
|
+
continue;
|
|
412
|
+
// #1348: a DISCOVERED row is the opposite case. Discovery only fills a
|
|
413
|
+
// gap the catalog left, so once the catalog lists the model it takes the
|
|
414
|
+
// row over: the metadata below replaces discovery's guesses and the row
|
|
415
|
+
// becomes catalog-owned (pruned with the catalog from then on). Local
|
|
416
|
+
// disables survive, like on any catalog row.
|
|
417
|
+
if (row && row.source === DISCOVERED_MODEL_SOURCE)
|
|
418
|
+
adoptModel.run(row.id);
|
|
419
|
+
const fields = {
|
|
420
|
+
displayName: m.displayName,
|
|
421
|
+
intelligenceRank: m.intelligenceRank,
|
|
422
|
+
speedRank: m.speedRank,
|
|
423
|
+
sizeLabel: m.sizeLabel,
|
|
424
|
+
rpm: m.limits.rpm,
|
|
425
|
+
rpd: m.limits.rpd,
|
|
426
|
+
tpm: m.limits.tpm,
|
|
427
|
+
tpd: m.limits.tpd,
|
|
428
|
+
monthlyTokenBudget: m.monthlyTokenBudget,
|
|
429
|
+
contextWindow: routableContextWindow(m.platform, m.modelId, m.contextWindow),
|
|
430
|
+
supportsVision: m.supportsVision ? 1 : 0,
|
|
431
|
+
supportsTools: m.supportsTools ? 1 : 0,
|
|
432
|
+
};
|
|
433
|
+
if (row) {
|
|
434
|
+
// Catalog disable wins (dead upstream); local disable also wins.
|
|
435
|
+
const enabled = m.enabled ? row.enabled : 0;
|
|
436
|
+
updateModel.run({ ...fields, id: row.id, enabled });
|
|
437
|
+
refreshModelOverrideBaselines(db, m.platform, m.modelId);
|
|
438
|
+
applyModelOverrides(db, m.platform, m.modelId);
|
|
439
|
+
counts.updated++;
|
|
440
|
+
if (row.displayName !== m.displayName)
|
|
441
|
+
record(details.updated, m.platform, m.displayName);
|
|
442
|
+
}
|
|
443
|
+
else {
|
|
444
|
+
insertModel.run({ ...fields, platform: m.platform, modelId: m.modelId, enabled: m.enabled ? 1 : 0 });
|
|
445
|
+
refreshModelOverrideBaselines(db, m.platform, m.modelId);
|
|
446
|
+
applyModelOverrides(db, m.platform, m.modelId);
|
|
447
|
+
counts.inserted++;
|
|
448
|
+
record(details.added, m.platform, m.displayName);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
// Video models use their own optional full snapshot. Older catalogs omit
|
|
452
|
+
// the key and leave existing video rows untouched; older binaries ignore
|
|
453
|
+
// the key entirely, which is why these rows must not live in models[].
|
|
454
|
+
if (catalog.videoModels) {
|
|
455
|
+
for (const m of catalog.videoModels) {
|
|
456
|
+
if (!VIDEO_PLATFORMS.has(m.platform)) {
|
|
457
|
+
counts.skippedUnknownPlatform++;
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
if (isCatalogModelTombstoned(db, 'media', m.platform, m.modelId))
|
|
461
|
+
continue;
|
|
462
|
+
inVideoCatalog.add(`${m.platform}:${m.modelId}`);
|
|
463
|
+
const meta = typeof m.providerModelId === 'string'
|
|
464
|
+
? JSON.stringify({ providerModelId: m.providerModelId })
|
|
465
|
+
: null;
|
|
466
|
+
const fields = {
|
|
467
|
+
displayName: m.displayName,
|
|
468
|
+
modality: 'video',
|
|
469
|
+
priority: m.priority,
|
|
470
|
+
quotaLabel: m.quotaLabel ?? '',
|
|
471
|
+
metaJson: meta,
|
|
472
|
+
};
|
|
473
|
+
const row = selectMedia.get(m.platform, m.modelId);
|
|
474
|
+
if (row) {
|
|
475
|
+
const enabled = m.enabled ? row.enabled : 0;
|
|
476
|
+
updateMedia.run({ ...fields, id: row.id, enabled });
|
|
477
|
+
counts.updated++;
|
|
478
|
+
if (row.displayName !== m.displayName)
|
|
479
|
+
record(details.updated, m.platform, m.displayName);
|
|
480
|
+
}
|
|
481
|
+
else {
|
|
482
|
+
insertMedia.run({ ...fields, platform: m.platform, modelId: m.modelId, enabled: m.enabled ? 1 : 0 });
|
|
483
|
+
counts.inserted++;
|
|
484
|
+
record(details.added, m.platform, m.displayName);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
// Embeddings are their own full snapshot. Older catalogs omit this field;
|
|
489
|
+
// in that case retain the app's bundled embedding baseline untouched.
|
|
490
|
+
if (catalog.embeddings) {
|
|
491
|
+
for (const m of catalog.embeddings) {
|
|
492
|
+
if (!EMBEDDING_PLATFORMS.has(m.platform)) {
|
|
493
|
+
counts.skippedUnknownPlatform++;
|
|
494
|
+
continue;
|
|
495
|
+
}
|
|
496
|
+
inEmbeddingCatalog.add(`${m.platform}:${m.modelId}`);
|
|
497
|
+
const row = selectEmbedding.get(m.platform, m.modelId);
|
|
498
|
+
const fields = {
|
|
499
|
+
family: m.family,
|
|
500
|
+
displayName: m.displayName,
|
|
501
|
+
dimensions: m.dimensions,
|
|
502
|
+
maxInputTokens: m.maxInputTokens,
|
|
503
|
+
priority: m.priority,
|
|
504
|
+
quotaLabel: m.quotaLabel,
|
|
505
|
+
};
|
|
506
|
+
if (row) {
|
|
507
|
+
const enabled = m.enabled ? row.enabled : 0; // catalog and local disables both win
|
|
508
|
+
updateEmbedding.run({ ...fields, id: row.id, enabled });
|
|
509
|
+
counts.updated++;
|
|
510
|
+
if (row.displayName !== m.displayName)
|
|
511
|
+
record(details.updated, m.platform, m.displayName);
|
|
512
|
+
}
|
|
513
|
+
else {
|
|
514
|
+
insertEmbedding.run({
|
|
515
|
+
...fields,
|
|
516
|
+
platform: m.platform,
|
|
517
|
+
modelId: m.modelId,
|
|
518
|
+
enabled: m.enabled ? 1 : 0,
|
|
519
|
+
});
|
|
520
|
+
counts.inserted++;
|
|
521
|
+
record(details.added, m.platform, m.displayName);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
// Transcription models are their own full snapshot, routed into
|
|
526
|
+
// media_models with modality='transcription' and gated on
|
|
527
|
+
// TRANSCRIPTION_PLATFORMS the way MEDIA_PLATFORMS gates the generative
|
|
528
|
+
// rows. Older catalogs omit this key; keep existing rows untouched then.
|
|
529
|
+
if (catalog.transcriptionModels) {
|
|
530
|
+
for (const m of catalog.transcriptionModels) {
|
|
531
|
+
if (!TRANSCRIPTION_PLATFORMS.has(m.platform)) {
|
|
532
|
+
counts.skippedUnknownPlatform++;
|
|
533
|
+
continue;
|
|
534
|
+
}
|
|
535
|
+
if (isCatalogModelTombstoned(db, 'media', m.platform, m.modelId))
|
|
536
|
+
continue;
|
|
537
|
+
inTranscriptionCatalog.add(`${m.platform}:${m.modelId}`);
|
|
538
|
+
const meta = {};
|
|
539
|
+
if (m.subtitleFormats?.length)
|
|
540
|
+
meta.subtitleFormats = m.subtitleFormats;
|
|
541
|
+
if (typeof m.maxBytes === 'number')
|
|
542
|
+
meta.maxBytes = m.maxBytes;
|
|
543
|
+
if (typeof m.requestStyle === 'string')
|
|
544
|
+
meta.requestStyle = m.requestStyle;
|
|
545
|
+
const fields = {
|
|
546
|
+
displayName: m.displayName,
|
|
547
|
+
priority: m.priority,
|
|
548
|
+
quotaLabel: m.quotaLabel ?? '',
|
|
549
|
+
metaJson: Object.keys(meta).length > 0 ? JSON.stringify(meta) : null,
|
|
550
|
+
};
|
|
551
|
+
const row = selectMedia.get(m.platform, m.modelId);
|
|
552
|
+
if (row) {
|
|
553
|
+
const enabled = m.enabled ? row.enabled : 0; // catalog and local disables both win
|
|
554
|
+
updateTranscription.run({ ...fields, id: row.id, enabled });
|
|
555
|
+
counts.updated++;
|
|
556
|
+
if (row.displayName !== m.displayName)
|
|
557
|
+
record(details.updated, m.platform, m.displayName);
|
|
558
|
+
}
|
|
559
|
+
else {
|
|
560
|
+
insertTranscription.run({ ...fields, platform: m.platform, modelId: m.modelId, enabled: m.enabled ? 1 : 0 });
|
|
561
|
+
counts.inserted++;
|
|
562
|
+
record(details.added, m.platform, m.displayName);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
counts.removed += deleteTombstonedCatalogModels(db);
|
|
567
|
+
applyAllModelOverrides(db);
|
|
568
|
+
// Ensure every model has a fallback_config row (same invariant migrations keep).
|
|
569
|
+
const missingFb = db
|
|
570
|
+
.prepare(`SELECT m.id FROM models m LEFT JOIN fallback_config f ON m.id = f.model_db_id WHERE f.id IS NULL`)
|
|
571
|
+
.all();
|
|
572
|
+
if (missingFb.length > 0) {
|
|
573
|
+
const maxPriority = db.prepare('SELECT COALESCE(MAX(priority), 0) AS mx FROM fallback_config').get().mx;
|
|
574
|
+
const addFb = db.prepare('INSERT INTO fallback_config (model_db_id, priority, enabled) VALUES (?, ?, 1)');
|
|
575
|
+
missingFb.forEach((r, i) => addFb.run(r.id, maxPriority + 1 + i));
|
|
576
|
+
}
|
|
577
|
+
ensureAllModelsInProfiles(db);
|
|
578
|
+
// Remove catalog-managed models that the catalog no longer lists.
|
|
579
|
+
// Ownership is decided by the `source` provenance column: only rows the
|
|
580
|
+
// catalog itself created are prune candidates. Rows with source='user'
|
|
581
|
+
// (declarative config, admin adds, custom endpoints) are never deleted
|
|
582
|
+
// here, no matter what their size_label or platform says — that replaces
|
|
583
|
+
// the old size_label NOT IN ('User','Custom') heuristic, which lost user
|
|
584
|
+
// rows whose label didn't follow the convention. The platform/key_id
|
|
585
|
+
// predicates stay as belt and braces.
|
|
586
|
+
const candidates = db
|
|
587
|
+
.prepare(`
|
|
588
|
+
SELECT id, platform, model_id, display_name AS displayName
|
|
589
|
+
FROM models
|
|
590
|
+
WHERE platform != 'custom'
|
|
591
|
+
AND key_id IS NULL
|
|
592
|
+
AND source = 'catalog'
|
|
593
|
+
`)
|
|
594
|
+
.all();
|
|
595
|
+
const deleteFb = db.prepare('DELETE FROM fallback_config WHERE model_db_id = ?');
|
|
596
|
+
const deleteModel = db.prepare('DELETE FROM models WHERE id = ?');
|
|
597
|
+
for (const c of candidates) {
|
|
598
|
+
if (!hasProvider(c.platform))
|
|
599
|
+
continue; // not catalog-managed by this binary
|
|
600
|
+
if (!inCatalog.has(`${c.platform}:${c.model_id}`)) {
|
|
601
|
+
deleteFb.run(c.id);
|
|
602
|
+
deleteModel.run(c.id);
|
|
603
|
+
counts.removed++;
|
|
604
|
+
record(details.removed, c.platform, c.displayName ?? c.model_id);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
// #1348: retire discovered rows on every platform the catalog now
|
|
608
|
+
// manages. A platform counts as managed when this catalog carries ANY row
|
|
609
|
+
// for it (chat, media, embedding, STT or video; enabled or disabled, since
|
|
610
|
+
// a disabled row is still an audit verdict) or names it in
|
|
611
|
+
// managedPlatforms. Rows the catalog listed were adopted above and are
|
|
612
|
+
// source='catalog' by now, so what is left is exactly the discovered
|
|
613
|
+
// stopgap the catalog has superseded.
|
|
614
|
+
const managed = catalogPlatforms(catalog);
|
|
615
|
+
const discoveredRows = db
|
|
616
|
+
.prepare('SELECT id, platform, model_id, display_name AS displayName FROM models WHERE source = ?')
|
|
617
|
+
.all(DISCOVERED_MODEL_SOURCE);
|
|
618
|
+
for (const d of discoveredRows) {
|
|
619
|
+
if (!managed.has(d.platform))
|
|
620
|
+
continue;
|
|
621
|
+
deleteFb.run(d.id);
|
|
622
|
+
deleteModel.run(d.id);
|
|
623
|
+
counts.removed++;
|
|
624
|
+
record(details.removed, d.platform, d.displayName ?? d.model_id);
|
|
625
|
+
}
|
|
626
|
+
// Remove media models the catalog no longer lists (own table, no
|
|
627
|
+
// fallback_config). Deliberately an ALLOWLIST of the two modalities that
|
|
628
|
+
// `models[]` maintains, not "everything except transcription": video and
|
|
629
|
+
// transcription rows come from their own optional snapshots below, so a
|
|
630
|
+
// catalog that omits those keys must leave them alone. Widening this back
|
|
631
|
+
// to a `!=` filter would silently delete every video row on the first sync
|
|
632
|
+
// from an older catalog.
|
|
633
|
+
const mediaCandidates = db
|
|
634
|
+
.prepare("SELECT id, platform, model_id, display_name AS displayName FROM media_models WHERE modality IN ('image', 'audio')")
|
|
635
|
+
.all();
|
|
636
|
+
const deleteMedia = db.prepare('DELETE FROM media_models WHERE id = ?');
|
|
637
|
+
for (const c of mediaCandidates) {
|
|
638
|
+
if (!MEDIA_PLATFORMS.has(c.platform))
|
|
639
|
+
continue; // not media-managed by this binary
|
|
640
|
+
if (!inMediaCatalog.has(`${c.platform}:${c.model_id}`)) {
|
|
641
|
+
deleteMedia.run(c.id);
|
|
642
|
+
counts.removed++;
|
|
643
|
+
record(details.removed, c.platform, c.displayName ?? c.model_id);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
// Prune video rows only when this catalog actually carries the dedicated
|
|
647
|
+
// snapshot. An older catalog cannot know whether a video row was retired.
|
|
648
|
+
if (catalog.videoModels) {
|
|
649
|
+
const videoCandidates = db
|
|
650
|
+
.prepare("SELECT id, platform, model_id, display_name AS displayName FROM media_models WHERE modality = 'video'")
|
|
651
|
+
.all();
|
|
652
|
+
for (const c of videoCandidates) {
|
|
653
|
+
if (!VIDEO_PLATFORMS.has(c.platform))
|
|
654
|
+
continue;
|
|
655
|
+
if (!inVideoCatalog.has(`${c.platform}:${c.model_id}`)) {
|
|
656
|
+
deleteMedia.run(c.id);
|
|
657
|
+
counts.removed++;
|
|
658
|
+
record(details.removed, c.platform, c.displayName ?? c.model_id);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
// Prune transcription rows only when the catalog actually carries the
|
|
663
|
+
// snapshot (mirrors the embeddings rule), scoped to the modality so
|
|
664
|
+
// image/audio rows are never touched by it.
|
|
665
|
+
if (catalog.transcriptionModels) {
|
|
666
|
+
const sttCandidates = db
|
|
667
|
+
.prepare("SELECT id, platform, model_id, display_name AS displayName FROM media_models WHERE modality = 'transcription'")
|
|
668
|
+
.all();
|
|
669
|
+
for (const c of sttCandidates) {
|
|
670
|
+
if (!TRANSCRIPTION_PLATFORMS.has(c.platform))
|
|
671
|
+
continue;
|
|
672
|
+
if (!inTranscriptionCatalog.has(`${c.platform}:${c.model_id}`)) {
|
|
673
|
+
deleteMedia.run(c.id);
|
|
674
|
+
counts.removed++;
|
|
675
|
+
record(details.removed, c.platform, c.displayName ?? c.model_id);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
// Embeddings are their own full snapshot. Older catalogs omit this field,
|
|
680
|
+
// AND catalogs may publish `embeddings: []` while still shipping model
|
|
681
|
+
// rows — both cases mean "retain the app's bundled embedding baseline
|
|
682
|
+
// untouched". The JS truthy check on a non-empty array object would
|
|
683
|
+
// misfire on `[]`, wiping the seeded rows; gate on length instead.
|
|
684
|
+
if (catalog.embeddings && catalog.embeddings.length > 0) {
|
|
685
|
+
const embeddingCandidates = db
|
|
686
|
+
.prepare(`
|
|
687
|
+
SELECT id, platform, model_id, display_name AS displayName
|
|
688
|
+
FROM embedding_models
|
|
689
|
+
WHERE platform != 'custom' AND key_id IS NULL
|
|
690
|
+
`)
|
|
691
|
+
.all();
|
|
692
|
+
const deleteEmbedding = db.prepare('DELETE FROM embedding_models WHERE id = ?');
|
|
693
|
+
for (const c of embeddingCandidates) {
|
|
694
|
+
if (!EMBEDDING_PLATFORMS.has(c.platform))
|
|
695
|
+
continue;
|
|
696
|
+
if (!inEmbeddingCatalog.has(`${c.platform}:${c.model_id}`)) {
|
|
697
|
+
deleteEmbedding.run(c.id);
|
|
698
|
+
counts.removed++;
|
|
699
|
+
record(details.removed, c.platform, c.displayName ?? c.model_id);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
// Quirks are pure content: replace wholesale.
|
|
704
|
+
db.prepare('DELETE FROM quirk_targets').run();
|
|
705
|
+
db.prepare('DELETE FROM quirks').run();
|
|
706
|
+
const insertQuirk = db.prepare(`INSERT INTO quirks (slug, title, body, severity, created_at_ms, updated_at_ms) VALUES (?, ?, ?, ?, ?, ?)`);
|
|
707
|
+
const insertTarget = db.prepare(`INSERT INTO quirk_targets (quirk_id, platform, model_glob) VALUES (?, ?, ?)`);
|
|
708
|
+
const now = Date.now();
|
|
709
|
+
for (const q of catalog.quirks) {
|
|
710
|
+
const info = insertQuirk.run(q.slug, q.title, q.body, q.severity, now, now);
|
|
711
|
+
for (const t of q.targets)
|
|
712
|
+
insertTarget.run(info.lastInsertRowid, t.platform ?? null, t.modelGlob ?? null);
|
|
713
|
+
counts.quirks++;
|
|
714
|
+
}
|
|
715
|
+
});
|
|
716
|
+
apply();
|
|
717
|
+
return { counts, details };
|
|
718
|
+
}
|
|
719
|
+
/**
|
|
720
|
+
* Fetch the catalog, verify its signature, and apply it if it moves us forward.
|
|
721
|
+
* `force` skips the `since` short-circuit — forces a full catalog fetch
|
|
722
|
+
* regardless of version changes.
|
|
723
|
+
*/
|
|
724
|
+
/** In-flight run for syncCatalog — concurrent callers share the same run. */
|
|
725
|
+
let inFlightSync = null;
|
|
726
|
+
export async function syncCatalog(force = false) {
|
|
727
|
+
// In-flight guard: if a run is already active, hand back the same promise
|
|
728
|
+
// instead of starting a duplicate fetch+apply.
|
|
729
|
+
if (inFlightSync) {
|
|
730
|
+
console.log('[catalog-sync] sync already in flight — reusing run');
|
|
731
|
+
return inFlightSync;
|
|
732
|
+
}
|
|
733
|
+
inFlightSync = runSyncCatalog(force);
|
|
734
|
+
try {
|
|
735
|
+
return await inFlightSync;
|
|
736
|
+
}
|
|
737
|
+
finally {
|
|
738
|
+
inFlightSync = null;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
async function runSyncCatalog(force) {
|
|
742
|
+
const db = getDb();
|
|
743
|
+
const applied = getSetting(SETTING_APPLIED_VERSION);
|
|
744
|
+
try {
|
|
745
|
+
const url = new URL(`${catalogBaseUrl()}/v1/latest`);
|
|
746
|
+
if (applied && !force)
|
|
747
|
+
url.searchParams.set('since', applied);
|
|
748
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });
|
|
749
|
+
if (res.status === 304) {
|
|
750
|
+
setSetting(SETTING_LAST_SYNC_MS, String(Date.now()));
|
|
751
|
+
setSetting(SETTING_LAST_ERROR, '');
|
|
752
|
+
return { ok: true, action: 'up_to_date', version: applied };
|
|
753
|
+
}
|
|
754
|
+
if (!res.ok)
|
|
755
|
+
throw new Error(`catalog fetch failed: HTTP ${res.status}`);
|
|
756
|
+
const signature = res.headers.get('x-catalog-signature');
|
|
757
|
+
if (!signature)
|
|
758
|
+
throw new Error('catalog response missing signature');
|
|
759
|
+
const bytes = Buffer.from(await res.arrayBuffer());
|
|
760
|
+
const verified = crypto.verify(null, bytes, catalogPublicKey(), Buffer.from(signature, 'base64'));
|
|
761
|
+
if (!verified)
|
|
762
|
+
throw new Error('catalog signature verification FAILED — discarding response');
|
|
763
|
+
const parsed = JSON.parse(bytes.toString('utf8'));
|
|
764
|
+
if (!isCatalog(parsed))
|
|
765
|
+
throw new Error('catalog payload has unexpected shape');
|
|
766
|
+
const catalog = parsed;
|
|
767
|
+
if (catalog.version < MIN_CATALOG_VERSION) {
|
|
768
|
+
// Older than the bundled baseline (e.g. monthly snapshot lagging a fresh
|
|
769
|
+
// app release) — applying it would roll back migrations. Wait it out.
|
|
770
|
+
setSetting(SETTING_LAST_SYNC_MS, String(Date.now()));
|
|
771
|
+
setSetting(SETTING_LAST_ERROR, '');
|
|
772
|
+
return { ok: true, action: 'skipped_older', version: catalog.version, tier: catalog.tier };
|
|
773
|
+
}
|
|
774
|
+
const sameAsApplied = applied === catalog.version && getSetting(SETTING_APPLIED_TIER) === catalog.tier;
|
|
775
|
+
if (!sameAsApplied) {
|
|
776
|
+
const { counts, details } = applyCatalogDetailed(db, catalog);
|
|
777
|
+
setSetting(SETTING_APPLIED_VERSION, catalog.version);
|
|
778
|
+
setSetting(SETTING_APPLIED_TIER, catalog.tier);
|
|
779
|
+
// Cache the verified document so boots can re-apply it offline (see
|
|
780
|
+
// reapplyCachedCatalog). Stored post-verification: anything that could
|
|
781
|
+
// tamper this row could tamper the models table directly, so the cache
|
|
782
|
+
// adds no new trust surface.
|
|
783
|
+
setSetting(SETTING_APPLIED_JSON, bytes.toString('utf8'));
|
|
784
|
+
console.log(`[catalog-sync] applied ${catalog.tier} v${catalog.version}: ` +
|
|
785
|
+
`${counts.updated} updated, ${counts.inserted} new, ${counts.removed} removed, ` +
|
|
786
|
+
`${counts.quirks} quirks` +
|
|
787
|
+
(counts.skippedUnknownPlatform ? `, ${counts.skippedUnknownPlatform} skipped (unknown platform)` : ''));
|
|
788
|
+
setSetting(SETTING_LAST_SYNC_MS, String(Date.now()));
|
|
789
|
+
setSetting(SETTING_LAST_ERROR, '');
|
|
790
|
+
const syncResult = {
|
|
791
|
+
ok: true,
|
|
792
|
+
action: 'applied',
|
|
793
|
+
version: catalog.version,
|
|
794
|
+
tier: catalog.tier,
|
|
795
|
+
counts,
|
|
796
|
+
details,
|
|
797
|
+
};
|
|
798
|
+
emitSyncNotifications(syncResult);
|
|
799
|
+
return syncResult;
|
|
800
|
+
}
|
|
801
|
+
setSetting(SETTING_LAST_SYNC_MS, String(Date.now()));
|
|
802
|
+
setSetting(SETTING_LAST_ERROR, '');
|
|
803
|
+
return { ok: true, action: 'up_to_date', version: catalog.version, tier: catalog.tier };
|
|
804
|
+
}
|
|
805
|
+
catch (err) {
|
|
806
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
807
|
+
console.warn(`[catalog-sync] ${message}`);
|
|
808
|
+
setSetting(SETTING_LAST_ERROR, message);
|
|
809
|
+
return { ok: false, action: 'error', detail: message };
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
export function getSyncState() {
|
|
813
|
+
return {
|
|
814
|
+
baseUrl: catalogBaseUrl(),
|
|
815
|
+
appliedVersion: getSetting(SETTING_APPLIED_VERSION) ?? null,
|
|
816
|
+
appliedTier: getSetting(SETTING_APPLIED_TIER) ?? null,
|
|
817
|
+
lastSyncMs: Number(getSetting(SETTING_LAST_SYNC_MS)) || null,
|
|
818
|
+
lastError: getSetting(SETTING_LAST_ERROR) || null,
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
/**
|
|
822
|
+
* Re-apply the cached (already signature-verified) catalog after boot.
|
|
823
|
+
*
|
|
824
|
+
* Migrations run on every boot and re-assert the bundled baseline — they
|
|
825
|
+
* INSERT OR IGNORE baseline models the catalog may have deleted and re-run
|
|
826
|
+
* the family-rule resets — while the boot-time network sync 304s on an
|
|
827
|
+
* unchanged version and so would NOT re-apply. Without this step every
|
|
828
|
+
* restart drifts the DB back toward the baseline until the next catalog
|
|
829
|
+
* version bump. Re-applying from the local cache is synchronous, needs no
|
|
830
|
+
* network, and keeps the catalog authoritative even offline.
|
|
831
|
+
*
|
|
832
|
+
* Legacy upgrade path: installs that applied a catalog before the cache
|
|
833
|
+
* existed have an applied-version setting but no cached document. Clearing
|
|
834
|
+
* the applied version makes the next poll fetch the full catalog (no `since`
|
|
835
|
+
* short-circuit), which re-applies it and populates the cache.
|
|
836
|
+
*/
|
|
837
|
+
export function reapplyCachedCatalog() {
|
|
838
|
+
try {
|
|
839
|
+
const raw = getSetting(SETTING_APPLIED_JSON);
|
|
840
|
+
if (!raw) {
|
|
841
|
+
if (getSetting(SETTING_APPLIED_VERSION)) {
|
|
842
|
+
getDb().prepare('DELETE FROM settings WHERE key = ?').run(SETTING_APPLIED_VERSION);
|
|
843
|
+
}
|
|
844
|
+
return { reapplied: false };
|
|
845
|
+
}
|
|
846
|
+
const parsed = JSON.parse(raw);
|
|
847
|
+
if (!isCatalog(parsed) || parsed.version < MIN_CATALOG_VERSION)
|
|
848
|
+
return { reapplied: false };
|
|
849
|
+
applyCatalog(getDb(), parsed);
|
|
850
|
+
console.log(`[catalog-sync] re-applied cached ${parsed.tier} v${parsed.version} after boot`);
|
|
851
|
+
return { reapplied: true, version: parsed.version };
|
|
852
|
+
}
|
|
853
|
+
catch (err) {
|
|
854
|
+
console.warn(`[catalog-sync] cached catalog re-apply failed: ${err instanceof Error ? err.message : err}`);
|
|
855
|
+
return { reapplied: false };
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
let cancelBootTimer = null;
|
|
859
|
+
let cancelInterval = null;
|
|
860
|
+
let currentScheduler = null;
|
|
861
|
+
function restartInterval() {
|
|
862
|
+
if (!currentScheduler)
|
|
863
|
+
return;
|
|
864
|
+
if (cancelInterval) {
|
|
865
|
+
cancelInterval();
|
|
866
|
+
cancelInterval = null;
|
|
867
|
+
}
|
|
868
|
+
const ms = syncIntervalMs();
|
|
869
|
+
if (ms > 0) {
|
|
870
|
+
cancelInterval = currentScheduler.every(ms, () => { void syncCatalog(); });
|
|
871
|
+
console.log(`[catalog-sync] polling ${catalogBaseUrl()} every ${ms / 3600000}h`);
|
|
872
|
+
}
|
|
873
|
+
else {
|
|
874
|
+
console.log('[catalog-sync] auto-sync disabled (manual mode)');
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
export function startCatalogSync(scheduler) {
|
|
878
|
+
if (cancelInterval)
|
|
879
|
+
return;
|
|
880
|
+
if (process.env.CATALOG_SYNC_DISABLED === '1') {
|
|
881
|
+
console.log('[catalog-sync] disabled via CATALOG_SYNC_DISABLED=1');
|
|
882
|
+
stopCatalogSync();
|
|
883
|
+
return;
|
|
884
|
+
}
|
|
885
|
+
currentScheduler = scheduler;
|
|
886
|
+
reapplyCachedCatalog();
|
|
887
|
+
const run = () => {
|
|
888
|
+
void syncCatalog();
|
|
889
|
+
};
|
|
890
|
+
cancelBootTimer = scheduler.after(BOOT_DELAY_MS, async () => {
|
|
891
|
+
run();
|
|
892
|
+
// Auto-discover models for providers that have keys but zero models in
|
|
893
|
+
// the catalog. Bypasses the monthly catalog: instead of waiting for
|
|
894
|
+
// the next snapshot to include a new provider, we query their APIs.
|
|
895
|
+
try {
|
|
896
|
+
const { autoDiscoverAll } = await import('./auto-discover.js');
|
|
897
|
+
await autoDiscoverAll();
|
|
898
|
+
}
|
|
899
|
+
catch (err) {
|
|
900
|
+
console.warn(`[catalog-sync] auto-discover failed: ${err instanceof Error ? err.message : err}`);
|
|
901
|
+
}
|
|
902
|
+
// Check for server updates on boot (if enabled in settings)
|
|
903
|
+
try {
|
|
904
|
+
const { checkUpdateAvailable } = await import('./notification-emitter.js');
|
|
905
|
+
await checkUpdateAvailable();
|
|
906
|
+
}
|
|
907
|
+
catch {
|
|
908
|
+
// Silent — update check is best-effort
|
|
909
|
+
}
|
|
910
|
+
});
|
|
911
|
+
restartInterval();
|
|
912
|
+
}
|
|
913
|
+
export function stopCatalogSync() {
|
|
914
|
+
if (cancelBootTimer) {
|
|
915
|
+
cancelBootTimer();
|
|
916
|
+
cancelBootTimer = null;
|
|
917
|
+
}
|
|
918
|
+
if (cancelInterval) {
|
|
919
|
+
cancelInterval();
|
|
920
|
+
cancelInterval = null;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
/** Call after changing the schedule setting to restart the polling timer. */
|
|
924
|
+
export function updateSyncSchedule() {
|
|
925
|
+
restartInterval();
|
|
926
|
+
// Also restart auto-discover on the same schedule
|
|
927
|
+
import('./auto-discover.js').then(m => m.restartRefreshInterval()).catch(() => { });
|
|
928
|
+
}
|
|
929
|
+
/** How many model names one notification line may name before it elides. */
|
|
930
|
+
const MAX_NOTIFICATION_MODELS = 5;
|
|
931
|
+
/**
|
|
932
|
+
* One notification per provider that actually changed, naming the models.
|
|
933
|
+
*
|
|
934
|
+
* A bare "12 models added" line tells the user nothing actionable, so the
|
|
935
|
+
* per-platform ledger from applyCatalogInner drives this: each entry is a
|
|
936
|
+
* provider plus the models that changed on it. Names are elided past
|
|
937
|
+
* MAX_NOTIFICATION_MODELS — one sync can land a whole new provider's roster —
|
|
938
|
+
* while the count stays the true total, so nothing is hidden.
|
|
939
|
+
*
|
|
940
|
+
* `updated` collapses to one line per provider with a count rather than
|
|
941
|
+
* naming models: the ledger there is every catalog row whose display name
|
|
942
|
+
* moved, which on a routine monthly snapshot is essentially the entire
|
|
943
|
+
* catalog (370 rows on the install this was built for), and a bell listing
|
|
944
|
+
* all of them is noise, not news.
|
|
945
|
+
*/
|
|
946
|
+
function emitSyncNotifications(result) {
|
|
947
|
+
if (!result.counts)
|
|
948
|
+
return;
|
|
949
|
+
const { inserted, removed, updated } = result.counts;
|
|
950
|
+
if (inserted === 0 && removed === 0 && updated === 0)
|
|
951
|
+
return;
|
|
952
|
+
const now = Date.now();
|
|
953
|
+
const notifs = [];
|
|
954
|
+
const ledger = result.details;
|
|
955
|
+
const names = (list) => list.slice(0, MAX_NOTIFICATION_MODELS).join(', ') + (list.length > MAX_NOTIFICATION_MODELS ? '…' : '');
|
|
956
|
+
/** Per-platform lines for added/removed, one line per changed provider. */
|
|
957
|
+
const perPlatform = (changes, type, dataKey) => {
|
|
958
|
+
for (const [platform, list] of changes) {
|
|
959
|
+
const count = list.length;
|
|
960
|
+
notifs.push({
|
|
961
|
+
id: `sync-${now}-${type}-${platform}`,
|
|
962
|
+
type,
|
|
963
|
+
modelId: '*',
|
|
964
|
+
platform,
|
|
965
|
+
displayName: `${count} model ${type === 'added' ? 'baru' : 'dihapus'}`,
|
|
966
|
+
message: names(list),
|
|
967
|
+
data: { key: dataKey, vars: { count, platform } },
|
|
968
|
+
timestamp: now,
|
|
969
|
+
read: false,
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
};
|
|
973
|
+
if (inserted > 0) {
|
|
974
|
+
if (ledger && ledger.added.size > 0) {
|
|
975
|
+
perPlatform(ledger.added, 'added', 'notifications.catalogAddedFrom');
|
|
976
|
+
}
|
|
977
|
+
else {
|
|
978
|
+
notifs.push({
|
|
979
|
+
id: `sync-${now}-added`,
|
|
980
|
+
type: 'added',
|
|
981
|
+
modelId: '*',
|
|
982
|
+
platform: '',
|
|
983
|
+
displayName: `${inserted} model baru`,
|
|
984
|
+
message: `${inserted} model baru ditambahkan dari katalog`,
|
|
985
|
+
data: { key: 'notifications.catalogAdded', vars: { count: inserted } },
|
|
986
|
+
timestamp: now,
|
|
987
|
+
read: false,
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
if (removed > 0) {
|
|
992
|
+
if (ledger && ledger.removed.size > 0) {
|
|
993
|
+
perPlatform(ledger.removed, 'removed', 'notifications.catalogRemovedFrom');
|
|
994
|
+
}
|
|
995
|
+
else {
|
|
996
|
+
notifs.push({
|
|
997
|
+
id: `sync-${now}-removed`,
|
|
998
|
+
type: 'removed',
|
|
999
|
+
modelId: '*',
|
|
1000
|
+
platform: '',
|
|
1001
|
+
displayName: `${removed} model dihapus`,
|
|
1002
|
+
message: `${removed} model dihapus karena sudah tidak ada di katalog`,
|
|
1003
|
+
data: { key: 'notifications.catalogRemoved', vars: { count: removed } },
|
|
1004
|
+
timestamp: now,
|
|
1005
|
+
read: false,
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
if (updated > 0) {
|
|
1010
|
+
if (ledger && ledger.updated.size > 0) {
|
|
1011
|
+
for (const [platform, list] of ledger.updated) {
|
|
1012
|
+
notifs.push({
|
|
1013
|
+
id: `sync-${now}-updated-${platform}`,
|
|
1014
|
+
type: 'updated',
|
|
1015
|
+
modelId: '*',
|
|
1016
|
+
platform,
|
|
1017
|
+
displayName: `${list.length} model diperbarui`,
|
|
1018
|
+
message: '',
|
|
1019
|
+
data: { key: 'notifications.catalogUpdatedFrom', vars: { count: list.length, platform } },
|
|
1020
|
+
timestamp: now,
|
|
1021
|
+
read: false,
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
else {
|
|
1026
|
+
notifs.push({
|
|
1027
|
+
id: `sync-${now}-updated`,
|
|
1028
|
+
type: 'updated',
|
|
1029
|
+
modelId: '*',
|
|
1030
|
+
platform: '',
|
|
1031
|
+
displayName: `${updated} model diperbarui`,
|
|
1032
|
+
message: `${updated} model diperbarui dari katalog terbaru`,
|
|
1033
|
+
data: { key: 'notifications.catalogUpdated', vars: { count: updated } },
|
|
1034
|
+
timestamp: now,
|
|
1035
|
+
read: false,
|
|
1036
|
+
});
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
addNotifications(notifs);
|
|
1040
|
+
}
|
|
1041
|
+
//# sourceMappingURL=catalog-sync.js.map
|