@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.
Files changed (1027) hide show
  1. package/LICENSE +21 -0
  2. package/client/dist/assets/am-TQ7Jmdqe.js +1 -0
  3. package/client/dist/assets/ar-Bf5a7yfC.js +1 -0
  4. package/client/dist/assets/az-CrosE1xH.js +1 -0
  5. package/client/dist/assets/bg-BvWnmcUz.js +1 -0
  6. package/client/dist/assets/bn-ams5TsJp.js +1 -0
  7. package/client/dist/assets/cs-Dh67CaJ_.js +1 -0
  8. package/client/dist/assets/da-9WlT8Iq7.js +1 -0
  9. package/client/dist/assets/de-lWhJJzz7.js +1 -0
  10. package/client/dist/assets/el-Cu7_GhNq.js +1 -0
  11. package/client/dist/assets/es-BZuzsBcP.js +1 -0
  12. package/client/dist/assets/fa-Dwdn-jKS.js +1 -0
  13. package/client/dist/assets/fi-BFrFyOTy.js +1 -0
  14. package/client/dist/assets/fr-XhbtmPpj.js +1 -0
  15. package/client/dist/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  16. package/client/dist/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  17. package/client/dist/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  18. package/client/dist/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  19. package/client/dist/assets/geist-mono-cyrillic-ext-wght-normal-I4S5GZfc.woff2 +0 -0
  20. package/client/dist/assets/geist-mono-cyrillic-wght-normal-BmXc_FBt.woff2 +0 -0
  21. package/client/dist/assets/geist-mono-latin-ext-wght-normal-DrnZ1wKl.woff2 +0 -0
  22. package/client/dist/assets/geist-mono-latin-wght-normal-B_7UjwxQ.woff2 +0 -0
  23. package/client/dist/assets/geist-mono-symbols2-wght-normal-GZpp1pK2.woff2 +0 -0
  24. package/client/dist/assets/geist-mono-vietnamese-wght-normal-D8KDMBhC.woff2 +0 -0
  25. package/client/dist/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  26. package/client/dist/assets/gu-D0uhLUB8.js +1 -0
  27. package/client/dist/assets/ha-CVlm9pqp.js +1 -0
  28. package/client/dist/assets/he-CMIFeoWr.js +1 -0
  29. package/client/dist/assets/hi-ENTwhBpn.js +1 -0
  30. package/client/dist/assets/hr-fc03vMy_.js +1 -0
  31. package/client/dist/assets/hu-YBo2BIYt.js +1 -0
  32. package/client/dist/assets/id-BEWXYX69.js +1 -0
  33. package/client/dist/assets/ig-BKLZFKka.js +1 -0
  34. package/client/dist/assets/index-D9I4KtvR.js +145 -0
  35. package/client/dist/assets/index-txhEmCdb.css +2 -0
  36. package/client/dist/assets/it-Bzmei1Y2.js +1 -0
  37. package/client/dist/assets/ja-BfcpuqEl.js +1 -0
  38. package/client/dist/assets/ka-CnWjRgLo.js +1 -0
  39. package/client/dist/assets/km-BAjPR3Qj.js +1 -0
  40. package/client/dist/assets/kn-B15WKU5m.js +1 -0
  41. package/client/dist/assets/ko-C61fAB-w.js +1 -0
  42. package/client/dist/assets/lt-CYTr4_YW.js +1 -0
  43. package/client/dist/assets/ml-CtqxgyH3.js +1 -0
  44. package/client/dist/assets/mr-X5TMY7aR.js +1 -0
  45. package/client/dist/assets/ms-DSg8RqPS.js +1 -0
  46. package/client/dist/assets/my-D7RqR8uE.js +1 -0
  47. package/client/dist/assets/ne-DaflxkDj.js +1 -0
  48. package/client/dist/assets/nl-BOqqtsC8.js +1 -0
  49. package/client/dist/assets/no-BSHp_StB.js +1 -0
  50. package/client/dist/assets/or-BkLR9Vub.js +1 -0
  51. package/client/dist/assets/pa-CarJ2XgI.js +1 -0
  52. package/client/dist/assets/pl-C2n0V-0k.js +1 -0
  53. package/client/dist/assets/pt-BR-CqJdNhg2.js +1 -0
  54. package/client/dist/assets/pt-PT-C_HpMyKz.js +1 -0
  55. package/client/dist/assets/ro-DVRZqSAS.js +1 -0
  56. package/client/dist/assets/ru-DwAJG7VC.js +1 -0
  57. package/client/dist/assets/si-BxUezXm0.js +1 -0
  58. package/client/dist/assets/sk-eI1VcIfY.js +1 -0
  59. package/client/dist/assets/sr-CJF3auCr.js +1 -0
  60. package/client/dist/assets/srouter-logo-C6ZfjGIi.svg +14 -0
  61. package/client/dist/assets/sv-nqvwHAq_.js +1 -0
  62. package/client/dist/assets/sw-DWynE2pW.js +1 -0
  63. package/client/dist/assets/ta-BVM12knC.js +1 -0
  64. package/client/dist/assets/te-oyzEkF7_.js +1 -0
  65. package/client/dist/assets/th-COVeE0ZQ.js +1 -0
  66. package/client/dist/assets/tl-DsJANnQz.js +1 -0
  67. package/client/dist/assets/tr-B-Fl-urO.js +1 -0
  68. package/client/dist/assets/uk-C5qketAO.js +1 -0
  69. package/client/dist/assets/ur-QU9KugfV.js +1 -0
  70. package/client/dist/assets/uz-DL9cG_mY.js +1 -0
  71. package/client/dist/assets/vi-CQiVy3Pu.js +1 -0
  72. package/client/dist/assets/yo-DqazMdrZ.js +1 -0
  73. package/client/dist/assets/zh-CN-DnYL_Kio.js +1 -0
  74. package/client/dist/assets/zh-TW-5bdbosKj.js +1 -0
  75. package/client/dist/favicon.svg +14 -0
  76. package/client/dist/icons.svg +24 -0
  77. package/client/dist/index.html +37 -0
  78. package/dist/app.d.ts +4 -0
  79. package/dist/app.d.ts.map +1 -0
  80. package/dist/app.js +372 -0
  81. package/dist/app.js.map +1 -0
  82. package/dist/db/index.d.ts +49 -0
  83. package/dist/db/index.d.ts.map +1 -0
  84. package/dist/db/index.js +203 -0
  85. package/dist/db/index.js.map +1 -0
  86. package/dist/db/migrate/TEMPLATE.d.ts +4 -0
  87. package/dist/db/migrate/TEMPLATE.d.ts.map +1 -0
  88. package/dist/db/migrate/TEMPLATE.js +18 -0
  89. package/dist/db/migrate/TEMPLATE.js.map +1 -0
  90. package/dist/db/migrate/cli.d.ts +2 -0
  91. package/dist/db/migrate/cli.d.ts.map +1 -0
  92. package/dist/db/migrate/cli.js +177 -0
  93. package/dist/db/migrate/cli.js.map +1 -0
  94. package/dist/db/migrate/defaults.d.ts +52 -0
  95. package/dist/db/migrate/defaults.d.ts.map +1 -0
  96. package/dist/db/migrate/defaults.js +126 -0
  97. package/dist/db/migrate/defaults.js.map +1 -0
  98. package/dist/db/migrate/runner.d.ts +16 -0
  99. package/dist/db/migrate/runner.d.ts.map +1 -0
  100. package/dist/db/migrate/runner.js +178 -0
  101. package/dist/db/migrate/runner.js.map +1 -0
  102. package/dist/db/migrations/20260101_000000_legacy_baseline.d.ts +4 -0
  103. package/dist/db/migrations/20260101_000000_legacy_baseline.d.ts.map +1 -0
  104. package/dist/db/migrations/20260101_000000_legacy_baseline.js +2240 -0
  105. package/dist/db/migrations/20260101_000000_legacy_baseline.js.map +1 -0
  106. package/dist/db/migrations/20260627_000001_custom_provider_modalities.d.ts +4 -0
  107. package/dist/db/migrations/20260627_000001_custom_provider_modalities.d.ts.map +1 -0
  108. package/dist/db/migrations/20260627_000001_custom_provider_modalities.js +27 -0
  109. package/dist/db/migrations/20260627_000001_custom_provider_modalities.js.map +1 -0
  110. package/dist/db/migrations/20260627_000002_catalog_model_state.d.ts +4 -0
  111. package/dist/db/migrations/20260627_000002_catalog_model_state.d.ts.map +1 -0
  112. package/dist/db/migrations/20260627_000002_catalog_model_state.js +30 -0
  113. package/dist/db/migrations/20260627_000002_catalog_model_state.js.map +1 -0
  114. package/dist/db/migrations/20260628_120000_request_aggregates.d.ts +6 -0
  115. package/dist/db/migrations/20260628_120000_request_aggregates.d.ts.map +1 -0
  116. package/dist/db/migrations/20260628_120000_request_aggregates.js +104 -0
  117. package/dist/db/migrations/20260628_120000_request_aggregates.js.map +1 -0
  118. package/dist/db/migrations/20260630_000001_github_gpt41_context.d.ts +9 -0
  119. package/dist/db/migrations/20260630_000001_github_gpt41_context.d.ts.map +1 -0
  120. package/dist/db/migrations/20260630_000001_github_gpt41_context.js +22 -0
  121. package/dist/db/migrations/20260630_000001_github_gpt41_context.js.map +1 -0
  122. package/dist/db/migrations/20260706_000001_request_client_info.d.ts +4 -0
  123. package/dist/db/migrations/20260706_000001_request_client_info.d.ts.map +1 -0
  124. package/dist/db/migrations/20260706_000001_request_client_info.js +30 -0
  125. package/dist/db/migrations/20260706_000001_request_client_info.js.map +1 -0
  126. package/dist/db/migrations/20260706_000002_custom_model_tool_support.d.ts +17 -0
  127. package/dist/db/migrations/20260706_000002_custom_model_tool_support.d.ts.map +1 -0
  128. package/dist/db/migrations/20260706_000002_custom_model_tool_support.js +20 -0
  129. package/dist/db/migrations/20260706_000002_custom_model_tool_support.js.map +1 -0
  130. package/dist/db/migrations/20260714_000001_profile_chain_backfill.d.ts +12 -0
  131. package/dist/db/migrations/20260714_000001_profile_chain_backfill.d.ts.map +1 -0
  132. package/dist/db/migrations/20260714_000001_profile_chain_backfill.js +45 -0
  133. package/dist/db/migrations/20260714_000001_profile_chain_backfill.js.map +1 -0
  134. package/dist/db/migrations/20260720_000001_key_health_error.d.ts +5 -0
  135. package/dist/db/migrations/20260720_000001_key_health_error.d.ts.map +1 -0
  136. package/dist/db/migrations/20260720_000001_key_health_error.js +16 -0
  137. package/dist/db/migrations/20260720_000001_key_health_error.js.map +1 -0
  138. package/dist/db/migrations/20260726_000001_cooldown_probe_provenance.d.ts +4 -0
  139. package/dist/db/migrations/20260726_000001_cooldown_probe_provenance.d.ts.map +1 -0
  140. package/dist/db/migrations/20260726_000001_cooldown_probe_provenance.js +38 -0
  141. package/dist/db/migrations/20260726_000001_cooldown_probe_provenance.js.map +1 -0
  142. package/dist/db/migrations/20260726_000002_request_attempts.d.ts +4 -0
  143. package/dist/db/migrations/20260726_000002_request_attempts.d.ts.map +1 -0
  144. package/dist/db/migrations/20260726_000002_request_attempts.js +43 -0
  145. package/dist/db/migrations/20260726_000002_request_attempts.js.map +1 -0
  146. package/dist/db/migrations/20260726_000003_model_source_provenance.d.ts +4 -0
  147. package/dist/db/migrations/20260726_000003_model_source_provenance.d.ts.map +1 -0
  148. package/dist/db/migrations/20260726_000003_model_source_provenance.js +80 -0
  149. package/dist/db/migrations/20260726_000003_model_source_provenance.js.map +1 -0
  150. package/dist/db/migrations/20260726_000004_media_model_meta.d.ts +4 -0
  151. package/dist/db/migrations/20260726_000004_media_model_meta.d.ts.map +1 -0
  152. package/dist/db/migrations/20260726_000004_media_model_meta.js +34 -0
  153. package/dist/db/migrations/20260726_000004_media_model_meta.js.map +1 -0
  154. package/dist/db/migrations/20260726_000005_request_served_model.d.ts +4 -0
  155. package/dist/db/migrations/20260726_000005_request_served_model.d.ts.map +1 -0
  156. package/dist/db/migrations/20260726_000005_request_served_model.js +31 -0
  157. package/dist/db/migrations/20260726_000005_request_served_model.js.map +1 -0
  158. package/dist/db/migrations/20260726_000006_attempt_error_summary.d.ts +4 -0
  159. package/dist/db/migrations/20260726_000006_attempt_error_summary.d.ts.map +1 -0
  160. package/dist/db/migrations/20260726_000006_attempt_error_summary.js +29 -0
  161. package/dist/db/migrations/20260726_000006_attempt_error_summary.js.map +1 -0
  162. package/dist/db/migrations/20260727_000001_agent_compatibility.d.ts +4 -0
  163. package/dist/db/migrations/20260727_000001_agent_compatibility.d.ts.map +1 -0
  164. package/dist/db/migrations/20260727_000001_agent_compatibility.js +41 -0
  165. package/dist/db/migrations/20260727_000001_agent_compatibility.js.map +1 -0
  166. package/dist/db/migrations/20260728_000001_tombstone_provenance.d.ts +12 -0
  167. package/dist/db/migrations/20260728_000001_tombstone_provenance.d.ts.map +1 -0
  168. package/dist/db/migrations/20260728_000001_tombstone_provenance.js +37 -0
  169. package/dist/db/migrations/20260728_000001_tombstone_provenance.js.map +1 -0
  170. package/dist/db/migrations/20260729_000001_custom_model_endpoint_identity.d.ts +4 -0
  171. package/dist/db/migrations/20260729_000001_custom_model_endpoint_identity.d.ts.map +1 -0
  172. package/dist/db/migrations/20260729_000001_custom_model_endpoint_identity.js +145 -0
  173. package/dist/db/migrations/20260729_000001_custom_model_endpoint_identity.js.map +1 -0
  174. package/dist/db/migrations/20260802_000001_custom_endpoint_host_labels.d.ts +6 -0
  175. package/dist/db/migrations/20260802_000001_custom_endpoint_host_labels.d.ts.map +1 -0
  176. package/dist/db/migrations/20260802_000001_custom_endpoint_host_labels.js +52 -0
  177. package/dist/db/migrations/20260802_000001_custom_endpoint_host_labels.js.map +1 -0
  178. package/dist/db/migrations/20260805_000001_key_model_scope.d.ts +6 -0
  179. package/dist/db/migrations/20260805_000001_key_model_scope.d.ts.map +1 -0
  180. package/dist/db/migrations/20260805_000001_key_model_scope.js +17 -0
  181. package/dist/db/migrations/20260805_000001_key_model_scope.js.map +1 -0
  182. package/dist/db/migrations/20260805_000002_client_profiles.d.ts +4 -0
  183. package/dist/db/migrations/20260805_000002_client_profiles.d.ts.map +1 -0
  184. package/dist/db/migrations/20260805_000002_client_profiles.js +33 -0
  185. package/dist/db/migrations/20260805_000002_client_profiles.js.map +1 -0
  186. package/dist/db/migrations/20260810_000001_api_key_proxy.d.ts +4 -0
  187. package/dist/db/migrations/20260810_000001_api_key_proxy.d.ts.map +1 -0
  188. package/dist/db/migrations/20260810_000001_api_key_proxy.js +33 -0
  189. package/dist/db/migrations/20260810_000001_api_key_proxy.js.map +1 -0
  190. package/dist/db/migrations/20260819_000001_custom_model_tombstones.d.ts +4 -0
  191. package/dist/db/migrations/20260819_000001_custom_model_tombstones.d.ts.map +1 -0
  192. package/dist/db/migrations/20260819_000001_custom_model_tombstones.js +19 -0
  193. package/dist/db/migrations/20260819_000001_custom_model_tombstones.js.map +1 -0
  194. package/dist/db/migrations/20260820_000001_playground_conversations.d.ts +4 -0
  195. package/dist/db/migrations/20260820_000001_playground_conversations.d.ts.map +1 -0
  196. package/dist/db/migrations/20260820_000001_playground_conversations.js +47 -0
  197. package/dist/db/migrations/20260820_000001_playground_conversations.js.map +1 -0
  198. package/dist/db/migrations/20260823_000001_server_logs.d.ts +4 -0
  199. package/dist/db/migrations/20260823_000001_server_logs.d.ts.map +1 -0
  200. package/dist/db/migrations/20260823_000001_server_logs.js +53 -0
  201. package/dist/db/migrations/20260823_000001_server_logs.js.map +1 -0
  202. package/dist/db/migrations/20260823_000002_backups_table.d.ts +8 -0
  203. package/dist/db/migrations/20260823_000002_backups_table.d.ts.map +1 -0
  204. package/dist/db/migrations/20260823_000002_backups_table.js +22 -0
  205. package/dist/db/migrations/20260823_000002_backups_table.js.map +1 -0
  206. package/dist/db/migrations/20260823_000003_attempt_key_label.d.ts +19 -0
  207. package/dist/db/migrations/20260823_000003_attempt_key_label.d.ts.map +1 -0
  208. package/dist/db/migrations/20260823_000003_attempt_key_label.js +28 -0
  209. package/dist/db/migrations/20260823_000003_attempt_key_label.js.map +1 -0
  210. package/dist/db/migrations/20260823_000004_profile_auto_include.d.ts +14 -0
  211. package/dist/db/migrations/20260823_000004_profile_auto_include.d.ts.map +1 -0
  212. package/dist/db/migrations/20260823_000004_profile_auto_include.js +25 -0
  213. package/dist/db/migrations/20260823_000004_profile_auto_include.js.map +1 -0
  214. package/dist/db/migrations/20260901_000001_idempotency_claims.d.ts +4 -0
  215. package/dist/db/migrations/20260901_000001_idempotency_claims.d.ts.map +1 -0
  216. package/dist/db/migrations/20260901_000001_idempotency_claims.js +46 -0
  217. package/dist/db/migrations/20260901_000001_idempotency_claims.js.map +1 -0
  218. package/dist/db/migrations/20260901_000002_quota_observation_lookup.d.ts +4 -0
  219. package/dist/db/migrations/20260901_000002_quota_observation_lookup.d.ts.map +1 -0
  220. package/dist/db/migrations/20260901_000002_quota_observation_lookup.js +37 -0
  221. package/dist/db/migrations/20260901_000002_quota_observation_lookup.js.map +1 -0
  222. package/dist/db/migrations/20260901_000003_request_caller.d.ts +4 -0
  223. package/dist/db/migrations/20260901_000003_request_caller.d.ts.map +1 -0
  224. package/dist/db/migrations/20260901_000003_request_caller.js +27 -0
  225. package/dist/db/migrations/20260901_000003_request_caller.js.map +1 -0
  226. package/dist/db/migrations/20260902_000001_analytics_latency_percentile_index.d.ts +4 -0
  227. package/dist/db/migrations/20260902_000001_analytics_latency_percentile_index.d.ts.map +1 -0
  228. package/dist/db/migrations/20260902_000001_analytics_latency_percentile_index.js +35 -0
  229. package/dist/db/migrations/20260902_000001_analytics_latency_percentile_index.js.map +1 -0
  230. package/dist/db/migrations/20260903_000001_mcp_enabled_default.d.ts +4 -0
  231. package/dist/db/migrations/20260903_000001_mcp_enabled_default.d.ts.map +1 -0
  232. package/dist/db/migrations/20260903_000001_mcp_enabled_default.js +37 -0
  233. package/dist/db/migrations/20260903_000001_mcp_enabled_default.js.map +1 -0
  234. package/dist/db/migrations/20260903_000002_response_cache.d.ts +4 -0
  235. package/dist/db/migrations/20260903_000002_response_cache.d.ts.map +1 -0
  236. package/dist/db/migrations/20260903_000002_response_cache.js +53 -0
  237. package/dist/db/migrations/20260903_000002_response_cache.js.map +1 -0
  238. package/dist/db/migrations/20260904_000001_key_monthly_budget.d.ts +13 -0
  239. package/dist/db/migrations/20260904_000001_key_monthly_budget.d.ts.map +1 -0
  240. package/dist/db/migrations/20260904_000001_key_monthly_budget.js +35 -0
  241. package/dist/db/migrations/20260904_000001_key_monthly_budget.js.map +1 -0
  242. package/dist/db/migrations/20260913_000001_request_model_attribution.d.ts +4 -0
  243. package/dist/db/migrations/20260913_000001_request_model_attribution.d.ts.map +1 -0
  244. package/dist/db/migrations/20260913_000001_request_model_attribution.js +68 -0
  245. package/dist/db/migrations/20260913_000001_request_model_attribution.js.map +1 -0
  246. package/dist/db/migrations/20260914_000001_key_monthly_usage.d.ts +5 -0
  247. package/dist/db/migrations/20260914_000001_key_monthly_usage.d.ts.map +1 -0
  248. package/dist/db/migrations/20260914_000001_key_monthly_usage.js +37 -0
  249. package/dist/db/migrations/20260914_000001_key_monthly_usage.js.map +1 -0
  250. package/dist/db/migrations/20260915_000001_quota_snapshot_freshness.d.ts +4 -0
  251. package/dist/db/migrations/20260915_000001_quota_snapshot_freshness.d.ts.map +1 -0
  252. package/dist/db/migrations/20260915_000001_quota_snapshot_freshness.js +28 -0
  253. package/dist/db/migrations/20260915_000001_quota_snapshot_freshness.js.map +1 -0
  254. package/dist/db/migrations/20261004_000001_unified_key_sr_prefix.d.ts +4 -0
  255. package/dist/db/migrations/20261004_000001_unified_key_sr_prefix.d.ts.map +1 -0
  256. package/dist/db/migrations/20261004_000001_unified_key_sr_prefix.js +25 -0
  257. package/dist/db/migrations/20261004_000001_unified_key_sr_prefix.js.map +1 -0
  258. package/dist/db/migrations/20261005_000001_key_budget_period.d.ts +10 -0
  259. package/dist/db/migrations/20261005_000001_key_budget_period.d.ts.map +1 -0
  260. package/dist/db/migrations/20261005_000001_key_budget_period.js +78 -0
  261. package/dist/db/migrations/20261005_000001_key_budget_period.js.map +1 -0
  262. package/dist/db/migrations/20261005_000002_key_budget_period_auto.d.ts +21 -0
  263. package/dist/db/migrations/20261005_000002_key_budget_period_auto.d.ts.map +1 -0
  264. package/dist/db/migrations/20261005_000002_key_budget_period_auto.js +41 -0
  265. package/dist/db/migrations/20261005_000002_key_budget_period_auto.js.map +1 -0
  266. package/dist/db/model-pricing.d.ts +39 -0
  267. package/dist/db/model-pricing.d.ts.map +1 -0
  268. package/dist/db/model-pricing.js +274 -0
  269. package/dist/db/model-pricing.js.map +1 -0
  270. package/dist/db/node-sqlite.d.ts +9 -0
  271. package/dist/db/node-sqlite.d.ts.map +1 -0
  272. package/dist/db/node-sqlite.js +87 -0
  273. package/dist/db/node-sqlite.js.map +1 -0
  274. package/dist/db/types.d.ts +22 -0
  275. package/dist/db/types.d.ts.map +1 -0
  276. package/dist/db/types.js +2 -0
  277. package/dist/db/types.js.map +1 -0
  278. package/dist/docs/docs-page.d.ts +2 -0
  279. package/dist/docs/docs-page.d.ts.map +1 -0
  280. package/dist/docs/docs-page.js +319 -0
  281. package/dist/docs/docs-page.js.map +1 -0
  282. package/dist/docs/openapi.d.ts +1896 -0
  283. package/dist/docs/openapi.d.ts.map +1 -0
  284. package/dist/docs/openapi.js +1096 -0
  285. package/dist/docs/openapi.js.map +1 -0
  286. package/dist/env.d.ts +2 -0
  287. package/dist/env.d.ts.map +1 -0
  288. package/dist/env.js +9 -0
  289. package/dist/env.js.map +1 -0
  290. package/dist/index.d.ts +2 -0
  291. package/dist/index.d.ts.map +1 -0
  292. package/dist/index.js +144 -0
  293. package/dist/index.js.map +1 -0
  294. package/dist/lib/anthropic-documents.d.ts +41 -0
  295. package/dist/lib/anthropic-documents.d.ts.map +1 -0
  296. package/dist/lib/anthropic-documents.js +132 -0
  297. package/dist/lib/anthropic-documents.js.map +1 -0
  298. package/dist/lib/app-version.d.ts +5 -0
  299. package/dist/lib/app-version.d.ts.map +1 -0
  300. package/dist/lib/app-version.js +61 -0
  301. package/dist/lib/app-version.js.map +1 -0
  302. package/dist/lib/attempt-trace.d.ts +23 -0
  303. package/dist/lib/attempt-trace.d.ts.map +1 -0
  304. package/dist/lib/attempt-trace.js +32 -0
  305. package/dist/lib/attempt-trace.js.map +1 -0
  306. package/dist/lib/budget.d.ts +6 -0
  307. package/dist/lib/budget.d.ts.map +1 -0
  308. package/dist/lib/budget.js +40 -0
  309. package/dist/lib/budget.js.map +1 -0
  310. package/dist/lib/client-classifier.d.ts +10 -0
  311. package/dist/lib/client-classifier.d.ts.map +1 -0
  312. package/dist/lib/client-classifier.js +126 -0
  313. package/dist/lib/client-classifier.js.map +1 -0
  314. package/dist/lib/client-context.d.ts +10 -0
  315. package/dist/lib/client-context.d.ts.map +1 -0
  316. package/dist/lib/client-context.js +39 -0
  317. package/dist/lib/client-context.js.map +1 -0
  318. package/dist/lib/config.d.ts +33 -0
  319. package/dist/lib/config.d.ts.map +1 -0
  320. package/dist/lib/config.js +94 -0
  321. package/dist/lib/config.js.map +1 -0
  322. package/dist/lib/content.d.ts +33 -0
  323. package/dist/lib/content.d.ts.map +1 -0
  324. package/dist/lib/content.js +201 -0
  325. package/dist/lib/content.js.map +1 -0
  326. package/dist/lib/credential.d.ts +11 -0
  327. package/dist/lib/credential.d.ts.map +1 -0
  328. package/dist/lib/credential.js +18 -0
  329. package/dist/lib/credential.js.map +1 -0
  330. package/dist/lib/crypto.d.ts +31 -0
  331. package/dist/lib/crypto.d.ts.map +1 -0
  332. package/dist/lib/crypto.js +206 -0
  333. package/dist/lib/crypto.js.map +1 -0
  334. package/dist/lib/custom-provider-cleanup.d.ts +13 -0
  335. package/dist/lib/custom-provider-cleanup.d.ts.map +1 -0
  336. package/dist/lib/custom-provider-cleanup.js +43 -0
  337. package/dist/lib/custom-provider-cleanup.js.map +1 -0
  338. package/dist/lib/db-backup.d.ts +31 -0
  339. package/dist/lib/db-backup.d.ts.map +1 -0
  340. package/dist/lib/db-backup.js +255 -0
  341. package/dist/lib/db-backup.js.map +1 -0
  342. package/dist/lib/endpoint-scope.d.ts +42 -0
  343. package/dist/lib/endpoint-scope.d.ts.map +1 -0
  344. package/dist/lib/endpoint-scope.js +95 -0
  345. package/dist/lib/endpoint-scope.js.map +1 -0
  346. package/dist/lib/env-drift.d.ts +17 -0
  347. package/dist/lib/env-drift.d.ts.map +1 -0
  348. package/dist/lib/env-drift.js +108 -0
  349. package/dist/lib/env-drift.js.map +1 -0
  350. package/dist/lib/error-classify.d.ts +40 -0
  351. package/dist/lib/error-classify.d.ts.map +1 -0
  352. package/dist/lib/error-classify.js +687 -0
  353. package/dist/lib/error-classify.js.map +1 -0
  354. package/dist/lib/error-redaction.d.ts +8 -0
  355. package/dist/lib/error-redaction.d.ts.map +1 -0
  356. package/dist/lib/error-redaction.js +45 -0
  357. package/dist/lib/error-redaction.js.map +1 -0
  358. package/dist/lib/fallback-loop.d.ts +306 -0
  359. package/dist/lib/fallback-loop.d.ts.map +1 -0
  360. package/dist/lib/fallback-loop.js +1336 -0
  361. package/dist/lib/fallback-loop.js.map +1 -0
  362. package/dist/lib/file-permissions.d.ts +98 -0
  363. package/dist/lib/file-permissions.d.ts.map +1 -0
  364. package/dist/lib/file-permissions.js +159 -0
  365. package/dist/lib/file-permissions.js.map +1 -0
  366. package/dist/lib/gemini-wire.d.ts +76 -0
  367. package/dist/lib/gemini-wire.d.ts.map +1 -0
  368. package/dist/lib/gemini-wire.js +392 -0
  369. package/dist/lib/gemini-wire.js.map +1 -0
  370. package/dist/lib/guardrails.d.ts +37 -0
  371. package/dist/lib/guardrails.d.ts.map +1 -0
  372. package/dist/lib/guardrails.js +106 -0
  373. package/dist/lib/guardrails.js.map +1 -0
  374. package/dist/lib/header-value.d.ts +8 -0
  375. package/dist/lib/header-value.d.ts.map +1 -0
  376. package/dist/lib/header-value.js +55 -0
  377. package/dist/lib/header-value.js.map +1 -0
  378. package/dist/lib/image-normalize.d.ts +21 -0
  379. package/dist/lib/image-normalize.d.ts.map +1 -0
  380. package/dist/lib/image-normalize.js +218 -0
  381. package/dist/lib/image-normalize.js.map +1 -0
  382. package/dist/lib/inbound-chat.d.ts +48 -0
  383. package/dist/lib/inbound-chat.d.ts.map +1 -0
  384. package/dist/lib/inbound-chat.js +406 -0
  385. package/dist/lib/inbound-chat.js.map +1 -0
  386. package/dist/lib/key-parser.d.ts +79 -0
  387. package/dist/lib/key-parser.d.ts.map +1 -0
  388. package/dist/lib/key-parser.js +701 -0
  389. package/dist/lib/key-parser.js.map +1 -0
  390. package/dist/lib/key-proxy.d.ts +49 -0
  391. package/dist/lib/key-proxy.d.ts.map +1 -0
  392. package/dist/lib/key-proxy.js +92 -0
  393. package/dist/lib/key-proxy.js.map +1 -0
  394. package/dist/lib/log-redaction.d.ts +28 -0
  395. package/dist/lib/log-redaction.d.ts.map +1 -0
  396. package/dist/lib/log-redaction.js +166 -0
  397. package/dist/lib/log-redaction.js.map +1 -0
  398. package/dist/lib/model-scope.d.ts +9 -0
  399. package/dist/lib/model-scope.d.ts.map +1 -0
  400. package/dist/lib/model-scope.js +29 -0
  401. package/dist/lib/model-scope.js.map +1 -0
  402. package/dist/lib/one-time-code.d.ts +15 -0
  403. package/dist/lib/one-time-code.d.ts.map +1 -0
  404. package/dist/lib/one-time-code.js +41 -0
  405. package/dist/lib/one-time-code.js.map +1 -0
  406. package/dist/lib/output-cap.d.ts +24 -0
  407. package/dist/lib/output-cap.d.ts.map +1 -0
  408. package/dist/lib/output-cap.js +80 -0
  409. package/dist/lib/output-cap.js.map +1 -0
  410. package/dist/lib/password.d.ts +3 -0
  411. package/dist/lib/password.d.ts.map +1 -0
  412. package/dist/lib/password.js +27 -0
  413. package/dist/lib/password.js.map +1 -0
  414. package/dist/lib/process-safety-net.d.ts +32 -0
  415. package/dist/lib/process-safety-net.d.ts.map +1 -0
  416. package/dist/lib/process-safety-net.js +123 -0
  417. package/dist/lib/process-safety-net.js.map +1 -0
  418. package/dist/lib/provider-identity.d.ts +37 -0
  419. package/dist/lib/provider-identity.d.ts.map +1 -0
  420. package/dist/lib/provider-identity.js +110 -0
  421. package/dist/lib/provider-identity.js.map +1 -0
  422. package/dist/lib/provider-size-parser.d.ts +6 -0
  423. package/dist/lib/provider-size-parser.d.ts.map +1 -0
  424. package/dist/lib/provider-size-parser.js +72 -0
  425. package/dist/lib/provider-size-parser.js.map +1 -0
  426. package/dist/lib/provider-timeout.d.ts +17 -0
  427. package/dist/lib/provider-timeout.d.ts.map +1 -0
  428. package/dist/lib/provider-timeout.js +70 -0
  429. package/dist/lib/provider-timeout.js.map +1 -0
  430. package/dist/lib/proxy.d.ts +180 -0
  431. package/dist/lib/proxy.d.ts.map +1 -0
  432. package/dist/lib/proxy.js +1002 -0
  433. package/dist/lib/proxy.js.map +1 -0
  434. package/dist/lib/request-log.d.ts +4 -0
  435. package/dist/lib/request-log.d.ts.map +1 -0
  436. package/dist/lib/request-log.js +161 -0
  437. package/dist/lib/request-log.js.map +1 -0
  438. package/dist/lib/reset-code.d.ts +5 -0
  439. package/dist/lib/reset-code.d.ts.map +1 -0
  440. package/dist/lib/reset-code.js +39 -0
  441. package/dist/lib/reset-code.js.map +1 -0
  442. package/dist/lib/retry-hint.d.ts +14 -0
  443. package/dist/lib/retry-hint.d.ts.map +1 -0
  444. package/dist/lib/retry-hint.js +36 -0
  445. package/dist/lib/retry-hint.js.map +1 -0
  446. package/dist/lib/route-live.d.ts +29 -0
  447. package/dist/lib/route-live.d.ts.map +1 -0
  448. package/dist/lib/route-live.js +97 -0
  449. package/dist/lib/route-live.js.map +1 -0
  450. package/dist/lib/sampling-params.d.ts +183 -0
  451. package/dist/lib/sampling-params.d.ts.map +1 -0
  452. package/dist/lib/sampling-params.js +386 -0
  453. package/dist/lib/sampling-params.js.map +1 -0
  454. package/dist/lib/scheduler.d.ts +13 -0
  455. package/dist/lib/scheduler.d.ts.map +1 -0
  456. package/dist/lib/scheduler.js +11 -0
  457. package/dist/lib/scheduler.js.map +1 -0
  458. package/dist/lib/served-model.d.ts +16 -0
  459. package/dist/lib/served-model.d.ts.map +1 -0
  460. package/dist/lib/served-model.js +0 -0
  461. package/dist/lib/served-model.js.map +1 -0
  462. package/dist/lib/server-logs.d.ts +132 -0
  463. package/dist/lib/server-logs.d.ts.map +1 -0
  464. package/dist/lib/server-logs.js +473 -0
  465. package/dist/lib/server-logs.js.map +1 -0
  466. package/dist/lib/setup-code.d.ts +5 -0
  467. package/dist/lib/setup-code.d.ts.map +1 -0
  468. package/dist/lib/setup-code.js +35 -0
  469. package/dist/lib/setup-code.js.map +1 -0
  470. package/dist/lib/structured-output.d.ts +9 -0
  471. package/dist/lib/structured-output.d.ts.map +1 -0
  472. package/dist/lib/structured-output.js +72 -0
  473. package/dist/lib/structured-output.js.map +1 -0
  474. package/dist/lib/system-prompt.d.ts +30 -0
  475. package/dist/lib/system-prompt.d.ts.map +1 -0
  476. package/dist/lib/system-prompt.js +66 -0
  477. package/dist/lib/system-prompt.js.map +1 -0
  478. package/dist/lib/task-type.d.ts +18 -0
  479. package/dist/lib/task-type.d.ts.map +1 -0
  480. package/dist/lib/task-type.js +92 -0
  481. package/dist/lib/task-type.js.map +1 -0
  482. package/dist/lib/think-tags.d.ts +37 -0
  483. package/dist/lib/think-tags.d.ts.map +1 -0
  484. package/dist/lib/think-tags.js +203 -0
  485. package/dist/lib/think-tags.js.map +1 -0
  486. package/dist/lib/tool-args.d.ts +36 -0
  487. package/dist/lib/tool-args.d.ts.map +1 -0
  488. package/dist/lib/tool-args.js +190 -0
  489. package/dist/lib/tool-args.js.map +1 -0
  490. package/dist/lib/tool-call-rescue.d.ts +62 -0
  491. package/dist/lib/tool-call-rescue.d.ts.map +1 -0
  492. package/dist/lib/tool-call-rescue.js +258 -0
  493. package/dist/lib/tool-call-rescue.js.map +1 -0
  494. package/dist/lib/tool-capability.d.ts +13 -0
  495. package/dist/lib/tool-capability.d.ts.map +1 -0
  496. package/dist/lib/tool-capability.js +70 -0
  497. package/dist/lib/tool-capability.js.map +1 -0
  498. package/dist/lib/tool-validate.d.ts +44 -0
  499. package/dist/lib/tool-validate.d.ts.map +1 -0
  500. package/dist/lib/tool-validate.js +165 -0
  501. package/dist/lib/tool-validate.js.map +1 -0
  502. package/dist/lib/ttfb-budget.d.ts +13 -0
  503. package/dist/lib/ttfb-budget.d.ts.map +1 -0
  504. package/dist/lib/ttfb-budget.js +153 -0
  505. package/dist/lib/ttfb-budget.js.map +1 -0
  506. package/dist/lib/url-guard.d.ts +47 -0
  507. package/dist/lib/url-guard.d.ts.map +1 -0
  508. package/dist/lib/url-guard.js +229 -0
  509. package/dist/lib/url-guard.js.map +1 -0
  510. package/dist/lib/wake-detect.d.ts +12 -0
  511. package/dist/lib/wake-detect.d.ts.map +1 -0
  512. package/dist/lib/wake-detect.js +99 -0
  513. package/dist/lib/wake-detect.js.map +1 -0
  514. package/dist/middleware/errorHandler.d.ts +3 -0
  515. package/dist/middleware/errorHandler.d.ts.map +1 -0
  516. package/dist/middleware/errorHandler.js +45 -0
  517. package/dist/middleware/errorHandler.js.map +1 -0
  518. package/dist/middleware/rateLimit.d.ts +4 -0
  519. package/dist/middleware/rateLimit.d.ts.map +1 -0
  520. package/dist/middleware/rateLimit.js +125 -0
  521. package/dist/middleware/rateLimit.js.map +1 -0
  522. package/dist/middleware/requireAuth.d.ts +3 -0
  523. package/dist/middleware/requireAuth.d.ts.map +1 -0
  524. package/dist/middleware/requireAuth.js +17 -0
  525. package/dist/middleware/requireAuth.js.map +1 -0
  526. package/dist/providers/aclide.d.ts +18 -0
  527. package/dist/providers/aclide.d.ts.map +1 -0
  528. package/dist/providers/aclide.js +174 -0
  529. package/dist/providers/aclide.js.map +1 -0
  530. package/dist/providers/aihorde.d.ts +42 -0
  531. package/dist/providers/aihorde.d.ts.map +1 -0
  532. package/dist/providers/aihorde.js +192 -0
  533. package/dist/providers/aihorde.js.map +1 -0
  534. package/dist/providers/airforce.d.ts +11 -0
  535. package/dist/providers/airforce.d.ts.map +1 -0
  536. package/dist/providers/airforce.js +32 -0
  537. package/dist/providers/airforce.js.map +1 -0
  538. package/dist/providers/base.d.ts +177 -0
  539. package/dist/providers/base.d.ts.map +1 -0
  540. package/dist/providers/base.js +354 -0
  541. package/dist/providers/base.js.map +1 -0
  542. package/dist/providers/blaze.d.ts +11 -0
  543. package/dist/providers/blaze.d.ts.map +1 -0
  544. package/dist/providers/blaze.js +33 -0
  545. package/dist/providers/blaze.js.map +1 -0
  546. package/dist/providers/blockrun.d.ts +14 -0
  547. package/dist/providers/blockrun.d.ts.map +1 -0
  548. package/dist/providers/blockrun.js +36 -0
  549. package/dist/providers/blockrun.js.map +1 -0
  550. package/dist/providers/clod.d.ts +11 -0
  551. package/dist/providers/clod.d.ts.map +1 -0
  552. package/dist/providers/clod.js +39 -0
  553. package/dist/providers/clod.js.map +1 -0
  554. package/dist/providers/cloudflare.d.ts +15 -0
  555. package/dist/providers/cloudflare.d.ts.map +1 -0
  556. package/dist/providers/cloudflare.js +175 -0
  557. package/dist/providers/cloudflare.js.map +1 -0
  558. package/dist/providers/cohere.d.ts +11 -0
  559. package/dist/providers/cohere.d.ts.map +1 -0
  560. package/dist/providers/cohere.js +117 -0
  561. package/dist/providers/cohere.js.map +1 -0
  562. package/dist/providers/dreamprompting.d.ts +11 -0
  563. package/dist/providers/dreamprompting.d.ts.map +1 -0
  564. package/dist/providers/dreamprompting.js +41 -0
  565. package/dist/providers/dreamprompting.js.map +1 -0
  566. package/dist/providers/electronhub.d.ts +10 -0
  567. package/dist/providers/electronhub.d.ts.map +1 -0
  568. package/dist/providers/electronhub.js +56 -0
  569. package/dist/providers/electronhub.js.map +1 -0
  570. package/dist/providers/experiential.d.ts +10 -0
  571. package/dist/providers/experiential.d.ts.map +1 -0
  572. package/dist/providers/experiential.js +33 -0
  573. package/dist/providers/experiential.js.map +1 -0
  574. package/dist/providers/gizmo.d.ts +15 -0
  575. package/dist/providers/gizmo.d.ts.map +1 -0
  576. package/dist/providers/gizmo.js +54 -0
  577. package/dist/providers/gizmo.js.map +1 -0
  578. package/dist/providers/google.d.ts +30 -0
  579. package/dist/providers/google.d.ts.map +1 -0
  580. package/dist/providers/google.js +766 -0
  581. package/dist/providers/google.js.map +1 -0
  582. package/dist/providers/index.d.ts +13 -0
  583. package/dist/providers/index.d.ts.map +1 -0
  584. package/dist/providers/index.js +561 -0
  585. package/dist/providers/index.js.map +1 -0
  586. package/dist/providers/llmtr.d.ts +15 -0
  587. package/dist/providers/llmtr.d.ts.map +1 -0
  588. package/dist/providers/llmtr.js +51 -0
  589. package/dist/providers/llmtr.js.map +1 -0
  590. package/dist/providers/logfare.d.ts +11 -0
  591. package/dist/providers/logfare.d.ts.map +1 -0
  592. package/dist/providers/logfare.js +33 -0
  593. package/dist/providers/logfare.js.map +1 -0
  594. package/dist/providers/lucidity.d.ts +11 -0
  595. package/dist/providers/lucidity.d.ts.map +1 -0
  596. package/dist/providers/lucidity.js +37 -0
  597. package/dist/providers/lucidity.js.map +1 -0
  598. package/dist/providers/modelscope.d.ts +50 -0
  599. package/dist/providers/modelscope.d.ts.map +1 -0
  600. package/dist/providers/modelscope.js +143 -0
  601. package/dist/providers/modelscope.js.map +1 -0
  602. package/dist/providers/moondream.d.ts +17 -0
  603. package/dist/providers/moondream.d.ts.map +1 -0
  604. package/dist/providers/moondream.js +140 -0
  605. package/dist/providers/moondream.js.map +1 -0
  606. package/dist/providers/openai-compat.d.ts +140 -0
  607. package/dist/providers/openai-compat.d.ts.map +1 -0
  608. package/dist/providers/openai-compat.js +537 -0
  609. package/dist/providers/openai-compat.js.map +1 -0
  610. package/dist/providers/opencode-free.d.ts +24 -0
  611. package/dist/providers/opencode-free.d.ts.map +1 -0
  612. package/dist/providers/opencode-free.js +262 -0
  613. package/dist/providers/opencode-free.js.map +1 -0
  614. package/dist/providers/pollinations.d.ts +32 -0
  615. package/dist/providers/pollinations.d.ts.map +1 -0
  616. package/dist/providers/pollinations.js +65 -0
  617. package/dist/providers/pollinations.js.map +1 -0
  618. package/dist/providers/router9.d.ts +16 -0
  619. package/dist/providers/router9.d.ts.map +1 -0
  620. package/dist/providers/router9.js +42 -0
  621. package/dist/providers/router9.js.map +1 -0
  622. package/dist/providers/sail.d.ts +44 -0
  623. package/dist/providers/sail.d.ts.map +1 -0
  624. package/dist/providers/sail.js +302 -0
  625. package/dist/providers/sail.js.map +1 -0
  626. package/dist/providers/septor.d.ts +10 -0
  627. package/dist/providers/septor.d.ts.map +1 -0
  628. package/dist/providers/septor.js +36 -0
  629. package/dist/providers/septor.js.map +1 -0
  630. package/dist/providers/speechify.d.ts +14 -0
  631. package/dist/providers/speechify.d.ts.map +1 -0
  632. package/dist/providers/speechify.js +28 -0
  633. package/dist/providers/speechify.js.map +1 -0
  634. package/dist/providers/speka.d.ts +12 -0
  635. package/dist/providers/speka.d.ts.map +1 -0
  636. package/dist/providers/speka.js +37 -0
  637. package/dist/providers/speka.js.map +1 -0
  638. package/dist/providers/waterfall.d.ts +11 -0
  639. package/dist/providers/waterfall.d.ts.map +1 -0
  640. package/dist/providers/waterfall.js +33 -0
  641. package/dist/providers/waterfall.js.map +1 -0
  642. package/dist/providers/xkiro.d.ts +71 -0
  643. package/dist/providers/xkiro.d.ts.map +1 -0
  644. package/dist/providers/xkiro.js +119 -0
  645. package/dist/providers/xkiro.js.map +1 -0
  646. package/dist/providers/zhipu.d.ts +43 -0
  647. package/dist/providers/zhipu.d.ts.map +1 -0
  648. package/dist/providers/zhipu.js +95 -0
  649. package/dist/providers/zhipu.js.map +1 -0
  650. package/dist/routes/analytics.d.ts +2 -0
  651. package/dist/routes/analytics.d.ts.map +1 -0
  652. package/dist/routes/analytics.js +738 -0
  653. package/dist/routes/analytics.js.map +1 -0
  654. package/dist/routes/anthropic.d.ts +7 -0
  655. package/dist/routes/anthropic.d.ts.map +1 -0
  656. package/dist/routes/anthropic.js +1076 -0
  657. package/dist/routes/anthropic.js.map +1 -0
  658. package/dist/routes/auth.d.ts +2 -0
  659. package/dist/routes/auth.d.ts.map +1 -0
  660. package/dist/routes/auth.js +310 -0
  661. package/dist/routes/auth.js.map +1 -0
  662. package/dist/routes/backups.d.ts +2 -0
  663. package/dist/routes/backups.d.ts.map +1 -0
  664. package/dist/routes/backups.js +127 -0
  665. package/dist/routes/backups.js.map +1 -0
  666. package/dist/routes/cache.d.ts +2 -0
  667. package/dist/routes/cache.d.ts.map +1 -0
  668. package/dist/routes/cache.js +40 -0
  669. package/dist/routes/cache.js.map +1 -0
  670. package/dist/routes/client-profiles.d.ts +2 -0
  671. package/dist/routes/client-profiles.d.ts.map +1 -0
  672. package/dist/routes/client-profiles.js +128 -0
  673. package/dist/routes/client-profiles.js.map +1 -0
  674. package/dist/routes/compression.d.ts +2 -0
  675. package/dist/routes/compression.d.ts.map +1 -0
  676. package/dist/routes/compression.js +82 -0
  677. package/dist/routes/compression.js.map +1 -0
  678. package/dist/routes/conversations.d.ts +3 -0
  679. package/dist/routes/conversations.d.ts.map +1 -0
  680. package/dist/routes/conversations.js +215 -0
  681. package/dist/routes/conversations.js.map +1 -0
  682. package/dist/routes/docs.d.ts +2 -0
  683. package/dist/routes/docs.d.ts.map +1 -0
  684. package/dist/routes/docs.js +21 -0
  685. package/dist/routes/docs.js.map +1 -0
  686. package/dist/routes/embeddings.d.ts +2 -0
  687. package/dist/routes/embeddings.d.ts.map +1 -0
  688. package/dist/routes/embeddings.js +255 -0
  689. package/dist/routes/embeddings.js.map +1 -0
  690. package/dist/routes/fallback.d.ts +2 -0
  691. package/dist/routes/fallback.d.ts.map +1 -0
  692. package/dist/routes/fallback.js +631 -0
  693. package/dist/routes/fallback.js.map +1 -0
  694. package/dist/routes/free-tier.d.ts +22 -0
  695. package/dist/routes/free-tier.d.ts.map +1 -0
  696. package/dist/routes/free-tier.js +175 -0
  697. package/dist/routes/free-tier.js.map +1 -0
  698. package/dist/routes/gemini.d.ts +2 -0
  699. package/dist/routes/gemini.d.ts.map +1 -0
  700. package/dist/routes/gemini.js +218 -0
  701. package/dist/routes/gemini.js.map +1 -0
  702. package/dist/routes/health.d.ts +2 -0
  703. package/dist/routes/health.d.ts.map +1 -0
  704. package/dist/routes/health.js +72 -0
  705. package/dist/routes/health.js.map +1 -0
  706. package/dist/routes/keys.d.ts +17 -0
  707. package/dist/routes/keys.d.ts.map +1 -0
  708. package/dist/routes/keys.js +1787 -0
  709. package/dist/routes/keys.js.map +1 -0
  710. package/dist/routes/logs.d.ts +2 -0
  711. package/dist/routes/logs.d.ts.map +1 -0
  712. package/dist/routes/logs.js +88 -0
  713. package/dist/routes/logs.js.map +1 -0
  714. package/dist/routes/mcp.d.ts +4 -0
  715. package/dist/routes/mcp.d.ts.map +1 -0
  716. package/dist/routes/mcp.js +563 -0
  717. package/dist/routes/mcp.js.map +1 -0
  718. package/dist/routes/media.d.ts +2 -0
  719. package/dist/routes/media.d.ts.map +1 -0
  720. package/dist/routes/media.js +180 -0
  721. package/dist/routes/media.js.map +1 -0
  722. package/dist/routes/models.d.ts +2 -0
  723. package/dist/routes/models.d.ts.map +1 -0
  724. package/dist/routes/models.js +439 -0
  725. package/dist/routes/models.js.map +1 -0
  726. package/dist/routes/ollama.d.ts +7 -0
  727. package/dist/routes/ollama.d.ts.map +1 -0
  728. package/dist/routes/ollama.js +595 -0
  729. package/dist/routes/ollama.js.map +1 -0
  730. package/dist/routes/profiles.d.ts +3 -0
  731. package/dist/routes/profiles.d.ts.map +1 -0
  732. package/dist/routes/profiles.js +431 -0
  733. package/dist/routes/profiles.js.map +1 -0
  734. package/dist/routes/proxy.d.ts +32 -0
  735. package/dist/routes/proxy.d.ts.map +1 -0
  736. package/dist/routes/proxy.js +2793 -0
  737. package/dist/routes/proxy.js.map +1 -0
  738. package/dist/routes/responses.d.ts +1161 -0
  739. package/dist/routes/responses.d.ts.map +1 -0
  740. package/dist/routes/responses.js +1521 -0
  741. package/dist/routes/responses.js.map +1 -0
  742. package/dist/routes/settings.d.ts +2 -0
  743. package/dist/routes/settings.d.ts.map +1 -0
  744. package/dist/routes/settings.js +549 -0
  745. package/dist/routes/settings.js.map +1 -0
  746. package/dist/routes/status.d.ts +3 -0
  747. package/dist/routes/status.d.ts.map +1 -0
  748. package/dist/routes/status.js +214 -0
  749. package/dist/routes/status.js.map +1 -0
  750. package/dist/routes/update.d.ts +31 -0
  751. package/dist/routes/update.d.ts.map +1 -0
  752. package/dist/routes/update.js +536 -0
  753. package/dist/routes/update.js.map +1 -0
  754. package/dist/routes/url-tokens.d.ts +2 -0
  755. package/dist/routes/url-tokens.d.ts.map +1 -0
  756. package/dist/routes/url-tokens.js +31 -0
  757. package/dist/routes/url-tokens.js.map +1 -0
  758. package/dist/scripts/export-catalog.d.ts +2 -0
  759. package/dist/scripts/export-catalog.d.ts.map +1 -0
  760. package/dist/scripts/export-catalog.js +114 -0
  761. package/dist/scripts/export-catalog.js.map +1 -0
  762. package/dist/scripts/rotate-encryption-key.d.ts +81 -0
  763. package/dist/scripts/rotate-encryption-key.d.ts.map +1 -0
  764. package/dist/scripts/rotate-encryption-key.js +232 -0
  765. package/dist/scripts/rotate-encryption-key.js.map +1 -0
  766. package/dist/scripts/routing-sim.d.ts +2 -0
  767. package/dist/scripts/routing-sim.d.ts.map +1 -0
  768. package/dist/scripts/routing-sim.js +130 -0
  769. package/dist/scripts/routing-sim.js.map +1 -0
  770. package/dist/scripts/test-all-models.d.ts +2 -0
  771. package/dist/scripts/test-all-models.d.ts.map +1 -0
  772. package/dist/scripts/test-all-models.js +56 -0
  773. package/dist/scripts/test-all-models.js.map +1 -0
  774. package/dist/services/anthropic-map.d.ts +39 -0
  775. package/dist/services/anthropic-map.d.ts.map +1 -0
  776. package/dist/services/anthropic-map.js +138 -0
  777. package/dist/services/anthropic-map.js.map +1 -0
  778. package/dist/services/auth.d.ts +30 -0
  779. package/dist/services/auth.d.ts.map +1 -0
  780. package/dist/services/auth.js +124 -0
  781. package/dist/services/auth.js.map +1 -0
  782. package/dist/services/auto-discover.d.ts +77 -0
  783. package/dist/services/auto-discover.d.ts.map +1 -0
  784. package/dist/services/auto-discover.js +1047 -0
  785. package/dist/services/auto-discover.js.map +1 -0
  786. package/dist/services/backups.d.ts +67 -0
  787. package/dist/services/backups.d.ts.map +1 -0
  788. package/dist/services/backups.js +444 -0
  789. package/dist/services/backups.js.map +1 -0
  790. package/dist/services/builtin-model-discovery.d.ts +107 -0
  791. package/dist/services/builtin-model-discovery.d.ts.map +1 -0
  792. package/dist/services/builtin-model-discovery.js +296 -0
  793. package/dist/services/builtin-model-discovery.js.map +1 -0
  794. package/dist/services/cache.d.ts +190 -0
  795. package/dist/services/cache.d.ts.map +1 -0
  796. package/dist/services/cache.js +617 -0
  797. package/dist/services/cache.js.map +1 -0
  798. package/dist/services/catalog-sync.d.ts +257 -0
  799. package/dist/services/catalog-sync.d.ts.map +1 -0
  800. package/dist/services/catalog-sync.js +1041 -0
  801. package/dist/services/catalog-sync.js.map +1 -0
  802. package/dist/services/compression/config.d.ts +33 -0
  803. package/dist/services/compression/config.d.ts.map +1 -0
  804. package/dist/services/compression/config.js +158 -0
  805. package/dist/services/compression/config.js.map +1 -0
  806. package/dist/services/compression/engines/aging.d.ts +2 -0
  807. package/dist/services/compression/engines/aging.d.ts.map +1 -0
  808. package/dist/services/compression/engines/aging.js +53 -0
  809. package/dist/services/compression/engines/aging.js.map +1 -0
  810. package/dist/services/compression/engines/custom-filters.d.ts +4 -0
  811. package/dist/services/compression/engines/custom-filters.d.ts.map +1 -0
  812. package/dist/services/compression/engines/custom-filters.js +49 -0
  813. package/dist/services/compression/engines/custom-filters.js.map +1 -0
  814. package/dist/services/compression/engines/dedup.d.ts +2 -0
  815. package/dist/services/compression/engines/dedup.d.ts.map +1 -0
  816. package/dist/services/compression/engines/dedup.js +61 -0
  817. package/dist/services/compression/engines/dedup.js.map +1 -0
  818. package/dist/services/compression/engines/filter-definitions.d.ts +75 -0
  819. package/dist/services/compression/engines/filter-definitions.d.ts.map +1 -0
  820. package/dist/services/compression/engines/filter-definitions.js +45 -0
  821. package/dist/services/compression/engines/filter-definitions.js.map +1 -0
  822. package/dist/services/compression/engines/hard-budget.d.ts +2 -0
  823. package/dist/services/compression/engines/hard-budget.d.ts.map +1 -0
  824. package/dist/services/compression/engines/hard-budget.js +52 -0
  825. package/dist/services/compression/engines/hard-budget.js.map +1 -0
  826. package/dist/services/compression/engines/index.d.ts +9 -0
  827. package/dist/services/compression/engines/index.d.ts.map +1 -0
  828. package/dist/services/compression/engines/index.js +9 -0
  829. package/dist/services/compression/engines/index.js.map +1 -0
  830. package/dist/services/compression/engines/jsoncompact.d.ts +3 -0
  831. package/dist/services/compression/engines/jsoncompact.d.ts.map +1 -0
  832. package/dist/services/compression/engines/jsoncompact.js +145 -0
  833. package/dist/services/compression/engines/jsoncompact.js.map +1 -0
  834. package/dist/services/compression/engines/lite.d.ts +2 -0
  835. package/dist/services/compression/engines/lite.d.ts.map +1 -0
  836. package/dist/services/compression/engines/lite.js +34 -0
  837. package/dist/services/compression/engines/lite.js.map +1 -0
  838. package/dist/services/compression/engines/read-lifecycle.d.ts +2 -0
  839. package/dist/services/compression/engines/read-lifecycle.d.ts.map +1 -0
  840. package/dist/services/compression/engines/read-lifecycle.js +70 -0
  841. package/dist/services/compression/engines/read-lifecycle.js.map +1 -0
  842. package/dist/services/compression/engines/relevance.d.ts +2 -0
  843. package/dist/services/compression/engines/relevance.d.ts.map +1 -0
  844. package/dist/services/compression/engines/relevance.js +74 -0
  845. package/dist/services/compression/engines/relevance.js.map +1 -0
  846. package/dist/services/compression/engines/toolfilter.d.ts +2 -0
  847. package/dist/services/compression/engines/toolfilter.d.ts.map +1 -0
  848. package/dist/services/compression/engines/toolfilter.js +195 -0
  849. package/dist/services/compression/engines/toolfilter.js.map +1 -0
  850. package/dist/services/compression/fidelity-gate.d.ts +12 -0
  851. package/dist/services/compression/fidelity-gate.d.ts.map +1 -0
  852. package/dist/services/compression/fidelity-gate.js +90 -0
  853. package/dist/services/compression/fidelity-gate.js.map +1 -0
  854. package/dist/services/compression/helpers.d.ts +9 -0
  855. package/dist/services/compression/helpers.d.ts.map +1 -0
  856. package/dist/services/compression/helpers.js +52 -0
  857. package/dist/services/compression/helpers.js.map +1 -0
  858. package/dist/services/compression/pipeline.d.ts +7 -0
  859. package/dist/services/compression/pipeline.d.ts.map +1 -0
  860. package/dist/services/compression/pipeline.js +245 -0
  861. package/dist/services/compression/pipeline.js.map +1 -0
  862. package/dist/services/compression/preservation.d.ts +27 -0
  863. package/dist/services/compression/preservation.d.ts.map +1 -0
  864. package/dist/services/compression/preservation.js +109 -0
  865. package/dist/services/compression/preservation.js.map +1 -0
  866. package/dist/services/compression/registry.d.ts +6 -0
  867. package/dist/services/compression/registry.d.ts.map +1 -0
  868. package/dist/services/compression/registry.js +16 -0
  869. package/dist/services/compression/registry.js.map +1 -0
  870. package/dist/services/compression/stats.d.ts +29 -0
  871. package/dist/services/compression/stats.d.ts.map +1 -0
  872. package/dist/services/compression/stats.js +55 -0
  873. package/dist/services/compression/stats.js.map +1 -0
  874. package/dist/services/compression/types.d.ts +90 -0
  875. package/dist/services/compression/types.d.ts.map +1 -0
  876. package/dist/services/compression/types.js +2 -0
  877. package/dist/services/compression/types.js.map +1 -0
  878. package/dist/services/context-handoff.d.ts +22 -0
  879. package/dist/services/context-handoff.d.ts.map +1 -0
  880. package/dist/services/context-handoff.js +164 -0
  881. package/dist/services/context-handoff.js.map +1 -0
  882. package/dist/services/cooldown-probe.d.ts +24 -0
  883. package/dist/services/cooldown-probe.d.ts.map +1 -0
  884. package/dist/services/cooldown-probe.js +181 -0
  885. package/dist/services/cooldown-probe.js.map +1 -0
  886. package/dist/services/custom-endpoint.d.ts +58 -0
  887. package/dist/services/custom-endpoint.d.ts.map +1 -0
  888. package/dist/services/custom-endpoint.js +167 -0
  889. package/dist/services/custom-endpoint.js.map +1 -0
  890. package/dist/services/custom-media-register.d.ts +22 -0
  891. package/dist/services/custom-media-register.d.ts.map +1 -0
  892. package/dist/services/custom-media-register.js +45 -0
  893. package/dist/services/custom-media-register.js.map +1 -0
  894. package/dist/services/custom-model-register.d.ts +50 -0
  895. package/dist/services/custom-model-register.d.ts.map +1 -0
  896. package/dist/services/custom-model-register.js +103 -0
  897. package/dist/services/custom-model-register.js.map +1 -0
  898. package/dist/services/custom-model-seed.d.ts +18 -0
  899. package/dist/services/custom-model-seed.d.ts.map +1 -0
  900. package/dist/services/custom-model-seed.js +42 -0
  901. package/dist/services/custom-model-seed.js.map +1 -0
  902. package/dist/services/custom-model-sync.d.ts +37 -0
  903. package/dist/services/custom-model-sync.d.ts.map +1 -0
  904. package/dist/services/custom-model-sync.js +118 -0
  905. package/dist/services/custom-model-sync.js.map +1 -0
  906. package/dist/services/custom-model-tombstone.d.ts +5 -0
  907. package/dist/services/custom-model-tombstone.d.ts.map +1 -0
  908. package/dist/services/custom-model-tombstone.js +30 -0
  909. package/dist/services/custom-model-tombstone.js.map +1 -0
  910. package/dist/services/declarative-config.d.ts +356 -0
  911. package/dist/services/declarative-config.d.ts.map +1 -0
  912. package/dist/services/declarative-config.js +427 -0
  913. package/dist/services/declarative-config.js.map +1 -0
  914. package/dist/services/degradation.d.ts +40 -0
  915. package/dist/services/degradation.d.ts.map +1 -0
  916. package/dist/services/degradation.js +119 -0
  917. package/dist/services/degradation.js.map +1 -0
  918. package/dist/services/embeddings.d.ts +76 -0
  919. package/dist/services/embeddings.d.ts.map +1 -0
  920. package/dist/services/embeddings.js +319 -0
  921. package/dist/services/embeddings.js.map +1 -0
  922. package/dist/services/fusion.d.ts +162 -0
  923. package/dist/services/fusion.d.ts.map +1 -0
  924. package/dist/services/fusion.js +789 -0
  925. package/dist/services/fusion.js.map +1 -0
  926. package/dist/services/gemini-map.d.ts +14 -0
  927. package/dist/services/gemini-map.d.ts.map +1 -0
  928. package/dist/services/gemini-map.js +78 -0
  929. package/dist/services/gemini-map.js.map +1 -0
  930. package/dist/services/health.d.ts +52 -0
  931. package/dist/services/health.d.ts.map +1 -0
  932. package/dist/services/health.js +352 -0
  933. package/dist/services/health.js.map +1 -0
  934. package/dist/services/idempotency.d.ts +49 -0
  935. package/dist/services/idempotency.d.ts.map +1 -0
  936. package/dist/services/idempotency.js +140 -0
  937. package/dist/services/idempotency.js.map +1 -0
  938. package/dist/services/key-budget.d.ts +74 -0
  939. package/dist/services/key-budget.d.ts.map +1 -0
  940. package/dist/services/key-budget.js +200 -0
  941. package/dist/services/key-budget.js.map +1 -0
  942. package/dist/services/media.d.ts +123 -0
  943. package/dist/services/media.d.ts.map +1 -0
  944. package/dist/services/media.js +954 -0
  945. package/dist/services/media.js.map +1 -0
  946. package/dist/services/model-discovery.d.ts +126 -0
  947. package/dist/services/model-discovery.d.ts.map +1 -0
  948. package/dist/services/model-discovery.js +662 -0
  949. package/dist/services/model-discovery.js.map +1 -0
  950. package/dist/services/model-groups.d.ts +149 -0
  951. package/dist/services/model-groups.d.ts.map +1 -0
  952. package/dist/services/model-groups.js +314 -0
  953. package/dist/services/model-groups.js.map +1 -0
  954. package/dist/services/model-listing.d.ts +18 -0
  955. package/dist/services/model-listing.d.ts.map +1 -0
  956. package/dist/services/model-listing.js +94 -0
  957. package/dist/services/model-listing.js.map +1 -0
  958. package/dist/services/model-retirement.d.ts +29 -0
  959. package/dist/services/model-retirement.d.ts.map +1 -0
  960. package/dist/services/model-retirement.js +86 -0
  961. package/dist/services/model-retirement.js.map +1 -0
  962. package/dist/services/model-state.d.ts +90 -0
  963. package/dist/services/model-state.d.ts.map +1 -0
  964. package/dist/services/model-state.js +342 -0
  965. package/dist/services/model-state.js.map +1 -0
  966. package/dist/services/model-weight-overrides.d.ts +48 -0
  967. package/dist/services/model-weight-overrides.d.ts.map +1 -0
  968. package/dist/services/model-weight-overrides.js +127 -0
  969. package/dist/services/model-weight-overrides.js.map +1 -0
  970. package/dist/services/notification-emitter.d.ts +50 -0
  971. package/dist/services/notification-emitter.d.ts.map +1 -0
  972. package/dist/services/notification-emitter.js +177 -0
  973. package/dist/services/notification-emitter.js.map +1 -0
  974. package/dist/services/opencode-free-sync.d.ts +29 -0
  975. package/dist/services/opencode-free-sync.d.ts.map +1 -0
  976. package/dist/services/opencode-free-sync.js +206 -0
  977. package/dist/services/opencode-free-sync.js.map +1 -0
  978. package/dist/services/penalty-inspector.d.ts +56 -0
  979. package/dist/services/penalty-inspector.d.ts.map +1 -0
  980. package/dist/services/penalty-inspector.js +167 -0
  981. package/dist/services/penalty-inspector.js.map +1 -0
  982. package/dist/services/profile-models.d.ts +5 -0
  983. package/dist/services/profile-models.d.ts.map +1 -0
  984. package/dist/services/profile-models.js +62 -0
  985. package/dist/services/profile-models.js.map +1 -0
  986. package/dist/services/provider-credential.d.ts +19 -0
  987. package/dist/services/provider-credential.d.ts.map +1 -0
  988. package/dist/services/provider-credential.js +51 -0
  989. package/dist/services/provider-credential.js.map +1 -0
  990. package/dist/services/provider-quota.d.ts +64 -0
  991. package/dist/services/provider-quota.d.ts.map +1 -0
  992. package/dist/services/provider-quota.js +563 -0
  993. package/dist/services/provider-quota.js.map +1 -0
  994. package/dist/services/quirks.d.ts +0 -0
  995. package/dist/services/quirks.d.ts.map +1 -0
  996. package/dist/services/quirks.js +0 -0
  997. package/dist/services/quirks.js.map +1 -0
  998. package/dist/services/quota-forecast.d.ts +50 -0
  999. package/dist/services/quota-forecast.d.ts.map +1 -0
  1000. package/dist/services/quota-forecast.js +150 -0
  1001. package/dist/services/quota-forecast.js.map +1 -0
  1002. package/dist/services/quota-outlook.d.ts +4 -0
  1003. package/dist/services/quota-outlook.d.ts.map +1 -0
  1004. package/dist/services/quota-outlook.js +92 -0
  1005. package/dist/services/quota-outlook.js.map +1 -0
  1006. package/dist/services/ratelimit.d.ts +249 -0
  1007. package/dist/services/ratelimit.d.ts.map +1 -0
  1008. package/dist/services/ratelimit.js +1182 -0
  1009. package/dist/services/ratelimit.js.map +1 -0
  1010. package/dist/services/request-retention.d.ts +60 -0
  1011. package/dist/services/request-retention.d.ts.map +1 -0
  1012. package/dist/services/request-retention.js +229 -0
  1013. package/dist/services/request-retention.js.map +1 -0
  1014. package/dist/services/router.d.ts +384 -0
  1015. package/dist/services/router.d.ts.map +1 -0
  1016. package/dist/services/router.js +1986 -0
  1017. package/dist/services/router.js.map +1 -0
  1018. package/dist/services/scoring.d.ts +156 -0
  1019. package/dist/services/scoring.d.ts.map +1 -0
  1020. package/dist/services/scoring.js +435 -0
  1021. package/dist/services/scoring.js.map +1 -0
  1022. package/dist/services/url-tokens.d.ts +15 -0
  1023. package/dist/services/url-tokens.d.ts.map +1 -0
  1024. package/dist/services/url-tokens.js +55 -0
  1025. package/dist/services/url-tokens.js.map +1 -0
  1026. package/package.json +77 -4
  1027. package/README.md +0 -3
@@ -0,0 +1,1047 @@
1
+ /**
2
+ * auto-discover — Automatically discover and register ALL model types
3
+ * (chat, embeddings, media) when a provider key is added but the catalog
4
+ * hasn't shipped models for it yet.
5
+ *
6
+ * This bypasses the catalog gate: instead of telling users to "add as custom
7
+ * endpoint", we query the provider's own /v1/models endpoint with the user's
8
+ * key and register discovered models directly.
9
+ *
10
+ * All modalities are discovered:
11
+ * - Chat models → models table
12
+ * - Embedding models → embedding_models table (dimensions auto-guessed)
13
+ * - Image/audio/video/transcription → media_models table
14
+ * - Provider quirks auto-assigned from known patterns
15
+ *
16
+ * Models are inserted with source='user' so catalog syncs never delete them.
17
+ */
18
+ import { getDb } from '../db/index.js';
19
+ import { resolveProvider } from '../providers/index.js';
20
+ import { OpenAICompatProvider } from '../providers/openai-compat.js';
21
+ import { parseModelCatalog } from './model-discovery.js';
22
+ import { ensureModelInProfiles } from './profile-models.js';
23
+ import { addNotifications, getSyncIntervalHours } from './catalog-sync.js';
24
+ // Large provider list — skip auto-discover for these well-known providers
25
+ // that either don't have /v1/models, are handled by the catalog already,
26
+ // or are 100% paid and we don't want to register any of their models.
27
+ const SKIP_AUTO_DISCOVER = new Set([
28
+ 'ollama', // local, handled separately
29
+ 'custom', // custom endpoints use their own flow
30
+ 'aihorde', // uses its own API format
31
+ 'bai', // 100% paid — "Deposit required to unlock premium models"
32
+ 'cerebras', // paid/exhausted — returns 402
33
+ ]);
34
+ /** How many models to register per platform before giving up */
35
+ const MAX_AUTO_MODELS = 300;
36
+ // How many model names one bell row may name before it elides the rest.
37
+ // A provider can gain dozens of models in one refresh; naming all of them
38
+ // turns the bell into a wall of text.
39
+ const DISCOVER_NOTIFICATION_MODELS = 5;
40
+ const PLATFORM_DEFAULTS = {
41
+ // ── Aggregators with published/observed limits ──
42
+ openrouter: { rpm: 5, rpd: 200, tpm: 200000, tpd: null }, // docs 20 rpm/200 rpd; live observed 5 rpm
43
+ routeway: { rpm: 5, rpd: 200, tpm: 200000, tpd: null }, // same pool as OpenRouter backend
44
+ kilo: { rpm: 3, rpd: 200, tpm: null, tpd: null }, // 200 req/hr keyless per IP
45
+ llm7: { rpm: 2, rpd: 100, tpm: null, tpd: null }, // 100 req/hr
46
+ unorouter: { rpm: 1, rpd: 60, tpm: null, tpd: null }, // 1 req/min per account per model
47
+ bazaarlink: { rpm: 1, rpd: 60, tpm: null, tpd: null }, // auto:free route
48
+ orcarouter: { rpm: 1, rpd: 60, tpm: null, tpd: null }, // limits unpublished, tight free tier
49
+ nara: { rpm: 3, rpd: 100, tpm: null, tpd: null }, // free plan, conservative
50
+ 'opencode-free': { rpm: 5, rpd: 200, tpm: null, tpd: null }, // zen free tier, unpublished limits
51
+ 'opencode-go': { rpm: 10, rpd: 500, tpm: null, tpd: null }, // go tier, separate pool from zen
52
+ // ── First-party providers (documented free tier) ──
53
+ groq: { rpm: 30, rpd: 14400, tpm: 131072, tpd: 5000000 }, // free tier; varies by model
54
+ navy: { rpm: 20, rpd: null, tpm: null, tpd: 150000 }, // 150K tokens/day, 20 RPM
55
+ airforce: { rpm: 1, rpd: 60, tpm: null, tpd: null }, // 1 req/min per account
56
+ ovh: { rpm: 2, rpd: null, tpm: null, tpd: null }, // 2 req/min keyless per model
57
+ modelscope: { rpm: 1, rpd: 2000, tpm: null, tpd: null }, // 2000 req/day
58
+ sealion: { rpm: 10, rpd: null, tpm: null, tpd: null }, // 10 RPM free tier
59
+ radeon: { rpm: 30, rpd: null, tpm: null, tpd: null }, // 30 RPM; $10/day credits
60
+ agnes: { rpm: 30, rpd: null, tpm: null, tpd: null }, // ~30 concurrent before 429
61
+ // ── Providers with generous/unpublished limits (conservative) ──
62
+ mistral: { rpm: 30, rpd: null, tpm: null, tpd: null }, // free plan, ~30 rpm observed
63
+ nvidia: { rpm: 40, rpd: null, tpm: null, tpd: null }, // generous free tier
64
+ google: { rpm: 15, rpd: null, tpm: 1000000, tpd: 50000000 }, // generous but bounded
65
+ anyapi: { rpm: null, rpd: null, tpm: null, tpd: 100000 }, // 100K tokens/day documented
66
+ xai: { rpm: null, rpd: null, tpm: null, tpd: null },
67
+ github: { rpm: null, rpd: null, tpm: null, tpd: null },
68
+ zhipu: { rpm: null, rpd: null, tpm: null, tpd: null },
69
+ huggingface: { rpm: null, rpd: null, tpm: null, tpd: null },
70
+ siliconflow: { rpm: null, rpd: null, tpm: null, tpd: null },
71
+ ollama: { rpm: null, rpd: null, tpm: null, tpd: null },
72
+ };
73
+ /** Conservative fallback when a platform is not in the map. */
74
+ const DEFAULT_FALLBACK = { rpm: 30, rpd: 1000, tpm: 100000, tpd: 1000000 };
75
+ /** Resolve the rate-limit defaults for a platform. */
76
+ function platformRates(platform) {
77
+ return PLATFORM_DEFAULTS[platform] ?? DEFAULT_FALLBACK;
78
+ }
79
+ function inferModelRanks(modelId) {
80
+ const lower = modelId.toLowerCase();
81
+ let intel = 100;
82
+ let spd = 100;
83
+ // Speed boosts (lower = faster / lighter)
84
+ if (/(?:^|-)(?:flash|mini|fast|lite|tiny|nano)(?:$|-)/i.test(lower))
85
+ spd = 80;
86
+ if (/(?:^|-)(?:turbo|instant|quick)(?:$|-)/i.test(lower))
87
+ spd = 85;
88
+ if (/(?:^|-)(?:haiku|small)(?:$|-)/i.test(lower))
89
+ spd = 85;
90
+ if (/(?:^|-)(?:medium)(?:$|-)/i.test(lower))
91
+ spd = 95;
92
+ // Speed penalties (slower / heavier)
93
+ if (/70b|405b|120b/i.test(lower))
94
+ spd = 120;
95
+ if (/(?:^|-)(?:large|ultra|thinking|reasoning)(?:$|-)/i.test(lower))
96
+ spd = 115;
97
+ if (/(?:^|-)(?:pro(?!mo)|max)(?:$|-)/i.test(lower))
98
+ spd = 110;
99
+ // Intelligence boosts (lower = smarter)
100
+ if (/405b/i.test(lower))
101
+ intel = 80;
102
+ if (/70b|120b/i.test(lower))
103
+ intel = 85;
104
+ if (/(?:^|-)(?:opus)(?:$|-)/i.test(lower))
105
+ intel = 82;
106
+ if (/(?:^|-)(?:sonnet)(?:$|-)/i.test(lower))
107
+ intel = 88;
108
+ if (/(?:^|-)(?:pro(?!mo)|max|ultra)(?:$|-)/i.test(lower))
109
+ intel = 85;
110
+ if (/(?:^|-)(?:plus)(?:$|-)/i.test(lower))
111
+ intel = 90;
112
+ // Intelligence penalties (weaker / distilled)
113
+ if (/(?:^|-)(?:flash|mini|tiny|nano|lite)(?:$|-)/i.test(lower))
114
+ intel = 120;
115
+ if (/(?:^|-)(?:small)(?:$|-)/i.test(lower))
116
+ intel = 115;
117
+ return { intelligence: intel, speed: spd };
118
+ }
119
+ // ── Embedding dimension guessing ──────────────────────────────────
120
+ // When the provider doesn't report dimensions, we estimate from the model id.
121
+ function guessEmbeddingDimensions(modelId) {
122
+ const lower = modelId.toLowerCase();
123
+ if (/3072/.test(lower))
124
+ return 3072;
125
+ if (/1536/.test(lower))
126
+ return 1536;
127
+ if (/1024/.test(lower))
128
+ return 1024;
129
+ if (/768/.test(lower))
130
+ return 768;
131
+ if (/512/.test(lower))
132
+ return 512;
133
+ if (/384/.test(lower))
134
+ return 384;
135
+ if (/256/.test(lower))
136
+ return 256;
137
+ if (/128/.test(lower))
138
+ return 128;
139
+ // Well-known model families
140
+ if (/text-embedding-3-small/.test(lower))
141
+ return 1536;
142
+ if (/text-embedding-3-large/.test(lower))
143
+ return 3072;
144
+ if (/text-embedding-ada/.test(lower))
145
+ return 1536;
146
+ if (/nomic/.test(lower))
147
+ return 768;
148
+ if (/bge-large/.test(lower))
149
+ return 1024;
150
+ if (/bge-base/.test(lower))
151
+ return 768;
152
+ if (/bge-small/.test(lower))
153
+ return 512;
154
+ if (/e5-large/.test(lower))
155
+ return 1024;
156
+ if (/e5-base/.test(lower))
157
+ return 768;
158
+ if (/e5-small/.test(lower))
159
+ return 512;
160
+ if (/cohere/.test(lower))
161
+ return 1024;
162
+ return 0; // unknown
163
+ }
164
+ /** Extract dimensions from the raw API response, or fall back to heuristics. */
165
+ function extractDimensions(modelId, rawModels) {
166
+ const raw = rawModels.find((m) => {
167
+ const id = m?.id ?? m?.name ?? m?.model ?? m?.model_id ?? m?.modelId ?? m?.slug;
168
+ return id === modelId;
169
+ });
170
+ if (raw) {
171
+ const dim = raw.dimensions ?? raw.dim ?? raw.embedding_dim ?? raw.dimension;
172
+ if (typeof dim === 'number' && dim > 0)
173
+ return dim;
174
+ }
175
+ return guessEmbeddingDimensions(modelId);
176
+ }
177
+ /** Derive embedding family name from model id using well-known patterns. */
178
+ function guessEmbeddingFamily(modelId) {
179
+ const lower = modelId.toLowerCase();
180
+ // Well-known embedding families — check specific patterns first
181
+ if (/^bge[-_]/.test(lower))
182
+ return 'bge';
183
+ if (/^e5[-_]/.test(lower))
184
+ return 'e5';
185
+ if (/^jina[-_]/.test(lower))
186
+ return 'jina';
187
+ if (/^gte[-_]/.test(lower))
188
+ return 'gte';
189
+ if (/^gte[-_]?embed/.test(lower))
190
+ return 'gte';
191
+ if (/^nomic[-_]/.test(lower))
192
+ return 'nomic';
193
+ if (/^cohere[-_]|^embed[-_]/.test(lower))
194
+ return 'cohere';
195
+ if (/^openai[-_]|^text-embedding[-_]/.test(lower))
196
+ return 'openai';
197
+ if (/^sentence[-_]transformer/.test(lower))
198
+ return 'sentence-transformer';
199
+ if (/^instructor[-_]/.test(lower))
200
+ return 'instructor';
201
+ if (/^sfr[-_]/.test(lower))
202
+ return 'sfr';
203
+ if (/^snowflake[-_]/.test(lower))
204
+ return 'snowflake';
205
+ if (/^mxbai[-_]/.test(lower))
206
+ return 'mxbai';
207
+ if (/^voyage[-_]/.test(lower))
208
+ return 'voyage';
209
+ if (/^arctic[-_]/.test(lower))
210
+ return 'arctic';
211
+ if (/^multilingual[-_]/.test(lower))
212
+ return 'multilingual-e5';
213
+ // Fallback: strip version suffixes
214
+ return lower.replace(/[-_]v?\d+$/, '').replace(/[-_]0\d+$/, '') || modelId;
215
+ }
216
+ // ── Quirk auto-detection ──────────────────────────────────────────
217
+ /** Known provider patterns we auto-detect from the /v1/models response. */
218
+ const AUTO_QUIRK_DETECTORS = [
219
+ {
220
+ detect: (_p, models) => models.length > 3 && models.every(m => !m.priceNote && !m.isFree),
221
+ slug: 'auto-no-pricing',
222
+ title: 'No pricing info from upstream',
223
+ body: 'This provider did not advertise pricing in /v1/models. Models may be free or paid — verify before relying on them.',
224
+ severity: 'info',
225
+ },
226
+ {
227
+ detect: (_p, models) => {
228
+ const chat = models.filter(m => !m.kind);
229
+ if (chat.length < 3)
230
+ return false;
231
+ return chat.filter(m => (m.contextWindow ?? 128000) < 8192).length > chat.length * 0.5;
232
+ },
233
+ slug: 'auto-short-context',
234
+ title: 'Most chat models have short context',
235
+ body: 'More than half of discovered chat models have context windows under 8K tokens. Consider a different provider for long-context tasks.',
236
+ severity: 'warning',
237
+ },
238
+ {
239
+ detect: (_p, models) => {
240
+ const hasChat = models.some(m => !m.kind);
241
+ const hasEmbed = models.some(m => m.kind === 'embedding');
242
+ return !hasChat && hasEmbed && models.length > 0;
243
+ },
244
+ slug: 'auto-embeddings-only',
245
+ title: 'Embeddings-only provider',
246
+ body: 'This provider only serves embedding models. It cannot be used for chat completions.',
247
+ severity: 'info',
248
+ },
249
+ {
250
+ detect: (_p, models) => models.length > 0 && models.every(m => m.isFree === true),
251
+ slug: 'auto-all-free',
252
+ title: 'All models reported as free',
253
+ body: 'Every model from this provider is advertised as free. Free tiers can change without notice.',
254
+ severity: 'info',
255
+ },
256
+ // ── P4: expanded quirk detectors ─────────────────────────────
257
+ // NOTE: auto-empty-response-prone is now evidence-based (see queryEmptyResponseRatio + autoAssignQuirks).
258
+ // It is NOT a static detector — it queries the requests table for actual empty-completion rates.
259
+ {
260
+ // Many models lack context window info
261
+ detect: (_p, models) => {
262
+ if (models.length < 5)
263
+ return false;
264
+ return models.filter(m => !m.contextWindow || m.contextWindow === 0).length > models.length * 0.7;
265
+ },
266
+ slug: 'auto-missing-context-info',
267
+ title: 'Most models lack context window info',
268
+ body: 'More than 70% of models from this provider have no context window data. Default 128K is used; actual limits may differ.',
269
+ severity: 'info',
270
+ },
271
+ {
272
+ // Large model catalog — likely an aggregator
273
+ detect: (_p, models) => models.length > 50,
274
+ slug: 'auto-many-models',
275
+ title: 'Large model catalog (50+)',
276
+ body: 'This provider offers 50+ models, suggesting it is an aggregator. Some models may be behind paid gates despite appearing free.',
277
+ severity: 'info',
278
+ },
279
+ {
280
+ // All models have small context windows (<=32K)
281
+ detect: (_p, models) => {
282
+ const chat = models.filter(m => !m.kind);
283
+ if (chat.length < 3)
284
+ return false;
285
+ return chat.every(m => (m.contextWindow ?? 128000) <= 32768);
286
+ },
287
+ slug: 'auto-small-context-only',
288
+ title: 'All chat models have small context (<=32K)',
289
+ body: 'All chat models from this provider have context windows of 32K or less. Not suitable for long-context tasks.',
290
+ severity: 'warning',
291
+ },
292
+ {
293
+ // Only media models (no chat, no embeddings)
294
+ detect: (_p, models) => {
295
+ const hasChat = models.some(m => !m.kind);
296
+ const hasEmb = models.some(m => m.kind === 'embedding');
297
+ const hasMedia = models.some(m => m.kind && m.kind !== 'embedding');
298
+ return !hasChat && !hasEmb && hasMedia && models.length > 0;
299
+ },
300
+ slug: 'auto-media-only',
301
+ title: 'Media-only provider',
302
+ body: 'This provider only serves generative media models (image/audio/video). It cannot be used for chat or embeddings.',
303
+ severity: 'info',
304
+ },
305
+ {
306
+ // Only 1-2 models — limited provider
307
+ detect: (_p, models) => {
308
+ const chat = models.filter(m => !m.kind);
309
+ return chat.length >= 1 && chat.length <= 2;
310
+ },
311
+ slug: 'auto-few-models',
312
+ title: 'Very few chat models (1-2)',
313
+ body: 'This provider only offers 1-2 chat models. Consider using it only as a fallback, not a primary route.',
314
+ severity: 'info',
315
+ },
316
+ ];
317
+ // ── Evidence-based empty response detection ─────────────────────
318
+ // Instead of hardcoding known-bad platforms, we query the requests table
319
+ // for actual empty-completion rates. A platform gets flagged when:
320
+ // - ≥50 requests in the last 7 days (enough data to judge)
321
+ // - >15% of those requests resulted in empty completions
322
+ // The quirk is automatically removed when the ratio drops below 10%
323
+ // (platform improved) or when there isn't enough data yet.
324
+ const EMPTY_RESPONSE_RATIO_ADD = 0.15; // 15% → add quirk
325
+ const EMPTY_RESPONSE_RATIO_REMOVE = 0.10; // 10% → remove quirk (improved)
326
+ const EMPTY_RESPONSE_MIN_REQUESTS = 50; // need at least this many to judge
327
+ const EMPTY_RESPONSE_WINDOW_DAYS = 7;
328
+ /**
329
+ * Query the requests table for empty-completion evidence on a platform.
330
+ * Returns { ratio, total, empty } or null if insufficient data.
331
+ */
332
+ function queryEmptyResponseRatio(db, platform) {
333
+ const since = new Date(Date.now() - EMPTY_RESPONSE_WINDOW_DAYS * 86_400_000).toISOString();
334
+ const row = db.prepare(`
335
+ SELECT
336
+ COUNT(*) AS total,
337
+ SUM(CASE WHEN error LIKE '%empty completion%' THEN 1 ELSE 0 END) AS empty
338
+ FROM requests
339
+ WHERE platform = ? AND created_at >= ?
340
+ `).get(platform, since);
341
+ if (!row || row.total < EMPTY_RESPONSE_MIN_REQUESTS)
342
+ return null;
343
+ return { ratio: row.empty / row.total, total: row.total, empty: row.empty };
344
+ }
345
+ /**
346
+ * Auto-assign quirks to a platform based on what we observe in its /v1/models.
347
+ * Only inserts quirks that don't already exist (operator or catalog quirks win).
348
+ */
349
+ function autoAssignQuirks(db, platform, models, _rawModels) {
350
+ const insertQuirk = db.prepare(`
351
+ INSERT OR IGNORE INTO quirks (slug, title, body, severity, created_at_ms, updated_at_ms)
352
+ VALUES (?, ?, ?, ?, ?, ?)
353
+ `);
354
+ const getQuirkId = db.prepare('SELECT id FROM quirks WHERE slug = ?');
355
+ const insertTarget = db.prepare('INSERT OR IGNORE INTO quirk_targets (quirk_id, platform, model_glob) VALUES (?, ?, ?)');
356
+ const now = Date.now();
357
+ let count = 0;
358
+ for (const detector of AUTO_QUIRK_DETECTORS) {
359
+ if (!detector.detect(platform, models))
360
+ continue;
361
+ // Scoped slug: platform prefix prevents cross-platform collision
362
+ const scopedSlug = `${detector.slug}--${platform}`;
363
+ // Skip if already exists (operator or catalog already set it)
364
+ const existing = db.prepare('SELECT 1 FROM quirks WHERE slug = ?').get(scopedSlug);
365
+ if (existing)
366
+ continue;
367
+ insertQuirk.run(scopedSlug, detector.title, detector.body, detector.severity, now, now);
368
+ const quirkRow = getQuirkId.get(scopedSlug);
369
+ if (quirkRow) {
370
+ insertTarget.run(quirkRow.id, platform, null);
371
+ count++;
372
+ }
373
+ }
374
+ // ── Evidence-based empty response detection ────────────────────
375
+ // Instead of hardcoding platform names, we query actual request logs.
376
+ // The quirk is added when empty-completion ratio >15% (min 50 requests
377
+ // in 7 days), and auto-removed when the ratio drops below 10%.
378
+ const emptySlug = `auto-empty-response-prone--${platform}`;
379
+ const emptyEvidence = queryEmptyResponseRatio(db, platform);
380
+ if (emptyEvidence) {
381
+ const { ratio, total, empty } = emptyEvidence;
382
+ if (ratio > EMPTY_RESPONSE_RATIO_ADD) {
383
+ // Ratio is high → add quirk if not already present, or update evidence
384
+ const existing = db.prepare('SELECT id FROM quirks WHERE slug = ?').get(emptySlug);
385
+ if (!existing) {
386
+ insertQuirk.run(emptySlug, 'Provider has high empty-completion rate (evidence-based)', `${Math.round(ratio * 100)}% of ${total} requests in the last ${EMPTY_RESPONSE_WINDOW_DAYS} days returned empty completions (${empty} empty). A retry/backoff strategy is recommended.`, 'warning', now, now);
387
+ const quirkRow = getQuirkId.get(emptySlug);
388
+ if (quirkRow) {
389
+ insertTarget.run(quirkRow.id, platform, null);
390
+ count++;
391
+ }
392
+ }
393
+ else {
394
+ // Update the evidence body so stale hardcoded descriptions get refreshed
395
+ const newBody = `${Math.round(ratio * 100)}% of ${total} requests in the last ${EMPTY_RESPONSE_WINDOW_DAYS} days returned empty completions (${empty} empty). A retry/backoff strategy is recommended.`;
396
+ db.prepare('UPDATE quirks SET body = ?, updated_at_ms = ? WHERE id = ?').run(newBody, now, existing.id);
397
+ }
398
+ }
399
+ else if (ratio < EMPTY_RESPONSE_RATIO_REMOVE) {
400
+ // Ratio is low → remove the auto-detected quirk (platform improved)
401
+ const existing = db.prepare('SELECT id FROM quirks WHERE slug = ?').get(emptySlug);
402
+ if (existing) {
403
+ // Only remove if it was auto-detected (not operator/catalog-set)
404
+ // Auto-detected quirks are identified by the 'auto-' prefix
405
+ db.prepare('DELETE FROM quirk_targets WHERE quirk_id = ?').run(existing.id);
406
+ db.prepare('DELETE FROM quirks WHERE id = ?').run(existing.id);
407
+ count--; // signal that we removed a quirk (negative count convention)
408
+ }
409
+ }
410
+ // If ratio is between REMOVE and ADD thresholds (10-15%), keep current state
411
+ }
412
+ return count;
413
+ }
414
+ // ── Monthly token budget estimation (P4) ────────────────────────
415
+ // Auto-discovered models have no monthly_token_budget label. We derive a
416
+ // human-readable estimate from the platform's rate limits so analytics
417
+ // ("Est. savings") and the headroom guardrail work right out of the box.
418
+ function estimateMonthlyBudget(tpd, tpm, rpd) {
419
+ // tpd (tokens per day) is the strongest signal
420
+ if (tpd != null && tpd > 0) {
421
+ const monthly = tpd * 30;
422
+ if (monthly >= 1_000_000_000)
423
+ return `~${Math.round(monthly / 1_000_000_000)}B`;
424
+ if (monthly >= 1_000_000)
425
+ return `~${Math.round(monthly / 1_000_000)}M`;
426
+ if (monthly >= 1_000)
427
+ return `~${Math.round(monthly / 1_000)}K`;
428
+ return `~${monthly}`;
429
+ }
430
+ // tpm (tokens per minute) → extrapolate to 30-day month
431
+ if (tpm != null && tpm > 0) {
432
+ const monthly = tpm * 60 * 24 * 30;
433
+ if (monthly >= 1_000_000_000)
434
+ return `~${Math.round(monthly / 1_000_000_000)}B`;
435
+ if (monthly >= 1_000_000)
436
+ return `~${Math.round(monthly / 1_000_000)}M`;
437
+ if (monthly >= 1_000)
438
+ return `~${Math.round(monthly / 1_000)}K`;
439
+ return `~${monthly}`;
440
+ }
441
+ // rpd (requests per day) → estimate ~1K tokens per request on average
442
+ if (rpd != null && rpd > 0) {
443
+ const monthly = rpd * 30 * 1000;
444
+ if (monthly >= 1_000_000)
445
+ return `~${Math.round(monthly / 1_000_000)}M`;
446
+ if (monthly >= 1_000)
447
+ return `~${Math.round(monthly / 1_000)}K`;
448
+ return `~${monthly}`;
449
+ }
450
+ return '';
451
+ }
452
+ /**
453
+ * Count enabled models for a platform across all model tables.
454
+ */
455
+ export function enabledModelCountForPlatform(platform) {
456
+ const db = getDb();
457
+ const row = db.prepare(`
458
+ SELECT (SELECT COUNT(*) FROM models WHERE platform = ? AND enabled = 1)
459
+ + (SELECT COUNT(*) FROM embedding_models WHERE platform = ? AND enabled = 1)
460
+ + (SELECT COUNT(*) FROM media_models WHERE platform = ? AND enabled = 1) AS c
461
+ `).get(platform, platform, platform);
462
+ return row.c;
463
+ }
464
+ /**
465
+ * Discover and register models for a platform that has API keys but zero
466
+ * models. Uses the provider's /v1/models endpoint with the user's own key.
467
+ *
468
+ * Returns a DiscoverResult with counts and model-name lists, or an empty
469
+ * result if nothing was needed or discoverable.
470
+ */
471
+ export async function autoDiscoverModels(platform, opts = {}) {
472
+ const empty = { registered: 0, refreshed: 0, retired: 0, embeddingsRegistered: 0, embeddingsRefreshed: 0, mediaRegistered: 0, mediaRefreshed: 0, quirksAssigned: 0, newModels: [], updatedModels: [], retiredModels: [] };
473
+ if (SKIP_AUTO_DISCOVER.has(platform))
474
+ return empty;
475
+ // Check if the platform already has all three model types — only skip if
476
+ // chat + embeddings + media are all populated. Platforms that have chat
477
+ // models but lack embeddings or media should still be discovered.
478
+ if (!opts.refresh) {
479
+ const dbPre = getDb();
480
+ const row = dbPre.prepare(`
481
+ SELECT
482
+ (SELECT COUNT(*) FROM models WHERE platform = ? AND enabled = 1) AS chat,
483
+ (SELECT COUNT(*) FROM embedding_models WHERE platform = ? AND enabled = 1) AS emb,
484
+ (SELECT COUNT(*) FROM media_models WHERE platform = ? AND enabled = 1) AS media
485
+ `).get(platform, platform, platform);
486
+ if (row.chat > 0 && row.emb > 0 && row.media > 0)
487
+ return empty;
488
+ }
489
+ const db = getDb();
490
+ // Find an enabled key for this platform
491
+ const keyRow = db.prepare('SELECT id, encrypted_key, iv, auth_tag FROM api_keys WHERE platform = ? AND enabled = 1 LIMIT 1').get(platform);
492
+ if (!keyRow)
493
+ return empty;
494
+ // Resolve the provider
495
+ const provider = resolveProvider(platform);
496
+ if (!provider || !(provider instanceof OpenAICompatProvider))
497
+ return empty;
498
+ // Decrypt the key (import dynamically to avoid circular deps)
499
+ const { decrypt } = await import('../lib/crypto.js');
500
+ let apiKey;
501
+ try {
502
+ apiKey = decrypt(keyRow.encrypted_key, keyRow.iv, keyRow.auth_tag);
503
+ }
504
+ catch {
505
+ return empty;
506
+ }
507
+ // Fetch the provider's model catalog
508
+ let models;
509
+ let rawBody;
510
+ try {
511
+ const res = await provider.fetchModelCatalog(apiKey);
512
+ if (!res.ok) {
513
+ console.log(`[auto-discover] ${platform}/models returned HTTP ${res.status} — skipping`);
514
+ return empty;
515
+ }
516
+ rawBody = await res.json();
517
+ models = parseModelCatalog(rawBody);
518
+ }
519
+ catch (err) {
520
+ const msg = err instanceof Error ? err.message : String(err);
521
+ console.warn(`[auto-discover] ${platform} discovery failed: ${msg}`);
522
+ return empty;
523
+ }
524
+ if (models.length === 0) {
525
+ console.log(`[auto-discover] ${platform} returned 0 models — skipping`);
526
+ return empty;
527
+ }
528
+ // ── Free-tier filter ──────────────────────────────────────────────
529
+ // We only want free models — loading paid ones causes 402/403 errors at
530
+ // request time and clutters the routing chain with unusable entries.
531
+ //
532
+ // Strategy (ordered by priority):
533
+ // 0. Providers that publish an explicit per-row `access_tier` (xkiro):
534
+ // keep access_tier === 'free'. This is the AUTHORITATIVE signal where it
535
+ // exists — xkiro's `:free` id suffix (rule 2) lists only 28 of its 58
536
+ // free rows, so the suffix filter silently dropped 30 free models, while
537
+ // no `:free` row is ever paid/premium. See providers/xkiro.ts.
538
+ // 1. Providers with a `tier` field (e.g. airforce): only keep tier='free'
539
+ // 2. Providers that use `:free` suffix convention (e.g. unorouter):
540
+ // only keep models whose id ends with ':free'
541
+ // 3. Everything else: pass through (assumed free)
542
+ let freeModels = models;
543
+ const body = rawBody;
544
+ const rawModels = Array.isArray(body?.data) ? body.data
545
+ : Array.isArray(body?.models) ? body.models
546
+ : Array.isArray(rawBody) ? rawBody
547
+ : [];
548
+ const hasAccessTierField = rawModels.some((m) => typeof m?.access_tier === 'string');
549
+ const hasTierField = rawModels.some((m) => typeof m?.tier === 'string');
550
+ if (hasAccessTierField) {
551
+ // Authoritative: the upstream tells us the tier per row. Match on the raw
552
+ // list (which carries the field) and project onto the parsed models.
553
+ const freeIds = new Set();
554
+ for (const m of rawModels) {
555
+ if (m.access_tier === 'free')
556
+ freeIds.add(m.id);
557
+ }
558
+ const beforeCount = freeModels.length;
559
+ freeModels = freeModels.filter(m => freeIds.has(m.id));
560
+ console.log(`[auto-discover] ${platform}: access_tier filter (${beforeCount} → ${freeModels.length} models)`);
561
+ }
562
+ else if (hasTierField) {
563
+ // Build a set of free model ids from the raw response
564
+ const freeIds = new Set();
565
+ for (const m of rawModels) {
566
+ if (m.tier === 'free')
567
+ freeIds.add(m.id);
568
+ }
569
+ freeModels = models.filter(m => freeIds.has(m.id));
570
+ console.log(`[auto-discover] ${platform}: ${freeIds.size}/${rawModels.length} models are free-tier (${models.length} parsed → ${freeModels.length} after filter)`);
571
+ }
572
+ else {
573
+ // No tier field — check if models use the `:free` suffix convention.
574
+ // If ANY model id ends with ':free', assume this provider uses the pattern
575
+ // and only keep those. Otherwise pass everything through.
576
+ const hasColonFree = rawModels.some((m) => typeof m?.id === 'string' && m.id.endsWith(':free'));
577
+ if (hasColonFree) {
578
+ const beforeCount = freeModels.length;
579
+ freeModels = freeModels.filter(m => m.id.endsWith(':free'));
580
+ console.log(`[auto-discover] ${platform}: :free suffix filter (${beforeCount} → ${freeModels.length} models)`);
581
+ }
582
+ }
583
+ // ── Split models by kind ──────────────────────────────────────────
584
+ const chatModels = freeModels.filter(m => !m.kind);
585
+ const embeddingModels = freeModels.filter(m => m.kind === 'embedding');
586
+ const mediaModels = freeModels.filter(m => m.kind && m.kind !== 'embedding');
587
+ if (chatModels.length === 0 && embeddingModels.length === 0 && mediaModels.length === 0) {
588
+ console.log(`[auto-discover] ${platform} has ${freeModels.length} free models but none are registerable — skipping`);
589
+ return empty;
590
+ }
591
+ // ── Verify free ACCESS for chat models only ─────────────────────
592
+ // A tier label is a claim; only a live request is evidence. xkiro labels all
593
+ // five `deepseek/*` rows `access_tier: 'free'` with zero pricing, yet every
594
+ // one answers a free-plan key with 403 "available to paying customers only".
595
+ let verifiedChatModels = chatModels;
596
+ if (hasAccessTierField) {
597
+ const { probeFreeAccess } = await import('../providers/xkiro.js');
598
+ verifiedChatModels = [];
599
+ let paid = 0;
600
+ let gone = 0;
601
+ for (const m of chatModels) {
602
+ const verdict = await probeFreeAccess(apiKey, m.id);
603
+ if (verdict === 'unauthorized') {
604
+ console.log(`[auto-discover] ${platform}: key rejected mid-verification — keeping remaining unverified`);
605
+ verifiedChatModels.push(m);
606
+ continue;
607
+ }
608
+ if (verdict === 'paid') {
609
+ paid++;
610
+ continue;
611
+ }
612
+ if (verdict === 'gone') {
613
+ gone++;
614
+ continue;
615
+ }
616
+ verifiedChatModels.push(m);
617
+ }
618
+ console.log(`[auto-discover] ${platform}: chat access verification — ${verifiedChatModels.length} usable, ${paid} paid-gated, ${gone} gone`);
619
+ }
620
+ // ── Classify model size from name heuristics ──────────────────────
621
+ function classifySize(modelId) {
622
+ const lower = modelId.toLowerCase();
623
+ if (/70b|405b|120b|large|ultra|pro(?!mo)|max/i.test(lower))
624
+ return 'Large';
625
+ if (/(?:8|9|14|20|27|32|34)b|medium/i.test(lower))
626
+ return 'Medium';
627
+ if (/(?:3|4|7)b|tiny|nano|micro|small|lite|flash/i.test(lower))
628
+ return 'Small';
629
+ return 'Medium';
630
+ }
631
+ // ── Prepare statements (all types) ─────────────────────────────
632
+ // P0: rate limits are now platform-specific (see PLATFORM_DEFAULTS above)
633
+ const rates = platformRates(platform);
634
+ // P3: catalog metadata fallback — look up same model_id in catalog
635
+ const lookupCatalogMeta = db.prepare(`
636
+ SELECT rpm_limit, rpd_limit, tpm_limit, tpd_limit, intelligence_rank, speed_rank
637
+ FROM models WHERE model_id = ? AND source = 'catalog' AND enabled = 1
638
+ LIMIT 1
639
+ `);
640
+ const selectExistingChat = db.prepare('SELECT id, source, display_name, context_window, size_label, supports_vision, rpm_limit, rpd_limit, intelligence_rank, speed_rank FROM models WHERE platform = ? AND model_id = ?');
641
+ const insertModel = db.prepare(`
642
+ INSERT INTO models (platform, model_id, display_name, source, enabled,
643
+ intelligence_rank, speed_rank, size_label,
644
+ supports_vision, supports_tools, context_window,
645
+ rpm_limit, rpd_limit, tpm_limit, tpd_limit, monthly_token_budget)
646
+ VALUES (?, ?, ?, 'user', 1,
647
+ ?, ?, ?,
648
+ ?, ?, ?,
649
+ ?, ?, ?, ?, ?)
650
+ `);
651
+ const refreshModel = db.prepare('UPDATE models SET display_name = ?, size_label = ?, supports_vision = ?, supports_tools = ?, context_window = ? WHERE id = ?');
652
+ // P0+P2: upgrade limits/ranks for existing models still on old hardcoded defaults
653
+ const upgradeLimits = db.prepare('UPDATE models SET rpm_limit = ?, rpd_limit = ?, tpm_limit = ?, tpd_limit = ?, intelligence_rank = ?, speed_rank = ?, monthly_token_budget = ? WHERE id = ?');
654
+ const benchModel = db.prepare('UPDATE models SET enabled = 0 WHERE id = ?');
655
+ // Embedding statements
656
+ const selectExistingEmbedding = db.prepare('SELECT id, display_name, dimensions FROM embedding_models WHERE platform = ? AND model_id = ?');
657
+ const insertEmbedding = db.prepare(`
658
+ INSERT INTO embedding_models (family, platform, model_id, display_name, dimensions, max_input_tokens, priority, enabled, quota_label, key_id)
659
+ VALUES (?, ?, ?, ?, ?, ?, ?, 1, 'auto-discovered', ?)
660
+ `);
661
+ const refreshEmbedding = db.prepare('UPDATE embedding_models SET display_name = ?, dimensions = ?, max_input_tokens = ? WHERE id = ?');
662
+ // Media statements
663
+ const selectExistingMedia = db.prepare('SELECT id, display_name, modality FROM media_models WHERE platform = ? AND model_id = ?');
664
+ const insertMedia = db.prepare(`
665
+ INSERT INTO media_models (platform, model_id, display_name, modality, priority, enabled, quota_label, key_id)
666
+ VALUES (?, ?, ?, ?, ?, 1, 'auto-discovered', ?)
667
+ `);
668
+ const refreshMedia = db.prepare('UPDATE media_models SET display_name = ?, modality = ? WHERE id = ?');
669
+ const maxP = db.prepare('SELECT COALESCE(MAX(priority), 0) AS mx FROM fallback_config').get().mx;
670
+ const insertFb = db.prepare('INSERT INTO fallback_config (model_db_id, priority, enabled) VALUES (?, ?, 1)');
671
+ // ── Transaction: register all model types + quirks ──────────────
672
+ let registered = 0;
673
+ let refreshed = 0;
674
+ let benched = 0;
675
+ const newModelNames = [];
676
+ const updatedModelNames = [];
677
+ const retiredModelNames = [];
678
+ const tx = db.transaction(() => {
679
+ let idx = 0;
680
+ const stillOffered = new Set();
681
+ // ──── 1. CHAT MODELS ────────────────────────────────────────
682
+ for (const m of verifiedChatModels.slice(0, MAX_AUTO_MODELS)) {
683
+ const size = classifySize(m.id);
684
+ const vision = m.vision ? 1 : 0;
685
+ const tools = /gpt|gemini|claude|mistral|qwen|llama.*3|deepseek|command/i.test(m.id) ? 1 : 0;
686
+ const ctx = m.contextWindow ?? 128000;
687
+ const display = m.id.replace(/[-_]/g, ' ');
688
+ const ranks = inferModelRanks(m.id); // P2: name-based rank inference
689
+ const existing = selectExistingChat.get(platform, m.id);
690
+ if (existing) {
691
+ stillOffered.add(existing.id);
692
+ if (existing.source === 'user') {
693
+ // Basic metadata refresh
694
+ const needsRefresh = existing.display_name !== display
695
+ || existing.context_window !== ctx
696
+ || existing.size_label !== size
697
+ || existing.supports_vision !== vision;
698
+ if (needsRefresh) {
699
+ refreshModel.run(display, size, vision, tools, ctx, existing.id);
700
+ refreshed++;
701
+ updatedModelNames.push(m.id);
702
+ }
703
+ // P0+P2: upgrade limits/ranks when still on old hardcoded defaults
704
+ // (rpm=60, rpd=1000 was the previous universal default)
705
+ const onOldDefaults = existing.rpm_limit === 60 && existing.rpd_limit === 1000;
706
+ const onFlatRanks = existing.intelligence_rank != null && existing.speed_rank != null
707
+ && Math.abs(existing.intelligence_rank - existing.speed_rank) <= 2
708
+ && existing.intelligence_rank >= 100 && existing.intelligence_rank <= 100 + 300;
709
+ if (onOldDefaults || onFlatRanks) {
710
+ // Prefer catalog metadata for ranks if available (P3)
711
+ const catMeta = lookupCatalogMeta.get(m.id);
712
+ const upRpm = catMeta?.rpm_limit ?? rates.rpm;
713
+ const upRpd = catMeta?.rpd_limit ?? rates.rpd;
714
+ const upTpm = catMeta?.tpm_limit ?? rates.tpm;
715
+ const upTpd = catMeta?.tpd_limit ?? rates.tpd;
716
+ const upIntel = catMeta?.intelligence_rank ?? ranks.intelligence;
717
+ const upSpeed = catMeta?.speed_rank ?? ranks.speed;
718
+ // P4: also backfill budget for old models that lack it
719
+ const upBudget = estimateMonthlyBudget(upTpd, upTpm, upRpd);
720
+ upgradeLimits.run(upRpm, upRpd, upTpm, upTpd, upIntel, upSpeed, upBudget, existing.id);
721
+ }
722
+ }
723
+ continue;
724
+ }
725
+ // P3: use catalog metadata as fallback for limits and ranks
726
+ const catalogMeta = lookupCatalogMeta.get(m.id);
727
+ const finalRpm = catalogMeta?.rpm_limit ?? rates.rpm;
728
+ const finalRpd = catalogMeta?.rpd_limit ?? rates.rpd;
729
+ const finalTpm = catalogMeta?.tpm_limit ?? rates.tpm;
730
+ const finalTpd = catalogMeta?.tpd_limit ?? rates.tpd;
731
+ const finalIntel = catalogMeta?.intelligence_rank ?? ranks.intelligence;
732
+ const finalSpeed = catalogMeta?.speed_rank ?? ranks.speed;
733
+ // P4: auto-estimate monthly_token_budget from rate limits
734
+ const estBudget = estimateMonthlyBudget(finalTpd, finalTpm, finalRpd);
735
+ const info = insertModel.run(platform, m.id, display, finalIntel, finalSpeed, size, vision, tools, ctx, finalRpm, finalRpd, finalTpm, finalTpd, estBudget);
736
+ const modelDbId = info.lastInsertRowid;
737
+ insertFb.run(modelDbId, maxP + 1 + idx);
738
+ ensureModelInProfiles(db, modelDbId);
739
+ stillOffered.add(modelDbId);
740
+ registered++;
741
+ newModelNames.push(m.id);
742
+ idx++;
743
+ }
744
+ // Retire chat rows this platform's discovery created that upstream dropped
745
+ if (stillOffered.size > 0) {
746
+ const ownedRows = db.prepare("SELECT id, model_id FROM models WHERE platform = ? AND source = 'user' AND enabled = 1").all(platform);
747
+ for (const row of ownedRows) {
748
+ if (stillOffered.has(row.id))
749
+ continue;
750
+ benchModel.run(row.id);
751
+ benched++;
752
+ retiredModelNames.push(row.model_id);
753
+ }
754
+ }
755
+ // ──── 2. EMBEDDING MODELS ───────────────────────────────────
756
+ let embRegistered = 0;
757
+ let embRefreshed = 0;
758
+ for (const m of embeddingModels.slice(0, MAX_AUTO_MODELS)) {
759
+ const dims = extractDimensions(m.id, rawModels);
760
+ const family = guessEmbeddingFamily(m.id);
761
+ const display = m.id.replace(/[-_]/g, ' ');
762
+ const maxInput = m.contextWindow ?? null;
763
+ const keyId = keyRow.id;
764
+ const existing = selectExistingEmbedding.get(platform, m.id);
765
+ if (existing) {
766
+ if (existing.display_name !== display || (dims > 0 && existing.dimensions !== dims)) {
767
+ refreshEmbedding.run(display, dims || existing.dimensions, maxInput, existing.id);
768
+ embRefreshed++;
769
+ updatedModelNames.push(m.id);
770
+ }
771
+ continue;
772
+ }
773
+ insertEmbedding.run(family, platform, m.id, display, dims || 0, maxInput, 100 + idx, keyId);
774
+ embRegistered++;
775
+ newModelNames.push(m.id);
776
+ idx++;
777
+ }
778
+ // ──── 3. MEDIA MODELS ───────────────────────────────────────
779
+ let mediaRegistered = 0;
780
+ let mediaRefreshed = 0;
781
+ for (const m of mediaModels.slice(0, MAX_AUTO_MODELS)) {
782
+ const modality = m.kind; // 'image' | 'audio' | 'video' | 'transcription'
783
+ const display = m.id.replace(/[-_]/g, ' ');
784
+ const keyId = keyRow.id;
785
+ const existing = selectExistingMedia.get(platform, m.id);
786
+ if (existing) {
787
+ if (existing.display_name !== display || existing.modality !== modality) {
788
+ refreshMedia.run(display, modality, existing.id);
789
+ mediaRefreshed++;
790
+ updatedModelNames.push(m.id);
791
+ }
792
+ continue;
793
+ }
794
+ insertMedia.run(platform, m.id, display, modality, 100 + idx, keyId);
795
+ mediaRegistered++;
796
+ newModelNames.push(m.id);
797
+ idx++;
798
+ }
799
+ // ──── 4. AUTO-ASSIGN QUIRKS ─────────────────────────────────
800
+ const quirkCount = autoAssignQuirks(db, platform, freeModels, rawModels);
801
+ // Store counts for return
802
+ txResult._embRegistered = embRegistered;
803
+ txResult._embRefreshed = embRefreshed;
804
+ txResult._mediaRegistered = mediaRegistered;
805
+ txResult._mediaRefreshed = mediaRefreshed;
806
+ txResult._quirkCount = quirkCount;
807
+ });
808
+ const txResult = {};
809
+ tx();
810
+ const embRegistered = txResult._embRegistered ?? 0;
811
+ const embRefreshed = txResult._embRefreshed ?? 0;
812
+ const mediaRegistered = txResult._mediaRegistered ?? 0;
813
+ const mediaRefreshed = txResult._mediaRefreshed ?? 0;
814
+ const quirkCount = txResult._quirkCount ?? 0;
815
+ // ── P1: Update auto-discovered models from runtime observations ──
816
+ // After insertion, check provider_quota_state for this platform. If
817
+ // we've observed real rate limits via response headers, use those to
818
+ // update models that still carry the default/initial values.
819
+ try {
820
+ const observed = db.prepare(`
821
+ SELECT metric, MIN(limit_value) AS lim
822
+ FROM provider_quota_state
823
+ WHERE platform = ? AND limit_value IS NOT NULL AND limit_value > 0
824
+ GROUP BY metric
825
+ `).all(platform);
826
+ if (observed.length > 0) {
827
+ const metricMap = {
828
+ rpm: { col: 'rpm_limit', defaultVal: rates.rpm },
829
+ rpd: { col: 'rpd_limit', defaultVal: rates.rpd },
830
+ tpm: { col: 'tpm_limit', defaultVal: rates.tpm },
831
+ tpd: { col: 'tpd_limit', defaultVal: rates.tpd },
832
+ };
833
+ let updated = 0;
834
+ for (const { metric, lim } of observed) {
835
+ const spec = metricMap[metric];
836
+ if (!spec)
837
+ continue;
838
+ // Only update models whose current value matches the initial default
839
+ // (or is NULL). This avoids overwriting catalog-authored or manually
840
+ // tuned limits.
841
+ if (spec.defaultVal != null) {
842
+ const res = db.prepare(`UPDATE models SET ${spec.col} = ? WHERE platform = ? AND source = 'user' AND ${spec.col} = ? AND enabled = 1`).run(lim, platform, spec.defaultVal);
843
+ updated += res.changes;
844
+ }
845
+ }
846
+ if (updated > 0) {
847
+ console.log(`[auto-discover] ${platform}: P1 updated ${updated} model limit(s) from runtime observations`);
848
+ }
849
+ }
850
+ }
851
+ catch {
852
+ // provider_quota_state may not exist yet or may be empty — non-fatal
853
+ }
854
+ const totalChanges = registered + refreshed + benched + embRegistered + embRefreshed + mediaRegistered + mediaRefreshed;
855
+ if (totalChanges > 0) {
856
+ console.log(`[auto-discover] ${platform}: chat ${registered}reg/${refreshed}ref/${benched}ret, ` +
857
+ `embed ${embRegistered}reg/${embRefreshed}ref, media ${mediaRegistered}reg/${mediaRefreshed}ref, ` +
858
+ `${quirkCount} quirks auto-assigned`);
859
+ }
860
+ return {
861
+ registered, refreshed, retired: benched,
862
+ embeddingsRegistered: embRegistered, embeddingsRefreshed: embRefreshed,
863
+ mediaRegistered, mediaRefreshed: mediaRefreshed,
864
+ quirksAssigned: quirkCount,
865
+ newModels: newModelNames, updatedModels: updatedModelNames, retiredModels: retiredModelNames,
866
+ };
867
+ }
868
+ /**
869
+ * Discover models for ALL configured providers that have keys.
870
+ * Called on startup — handles initial registration for platforms with zero
871
+ * models, and also discovers embeddings/media for platforms that only have chat.
872
+ */
873
+ export async function autoDiscoverAll() {
874
+ const db = getDb();
875
+ const configured = db.prepare(`
876
+ SELECT DISTINCT platform FROM api_keys WHERE enabled = 1
877
+ `).all();
878
+ for (const { platform } of configured) {
879
+ if (SKIP_AUTO_DISCOVER.has(platform))
880
+ continue;
881
+ // Skip platforms that already have all three model types
882
+ const counts = db.prepare(`
883
+ SELECT
884
+ (SELECT COUNT(*) FROM models WHERE platform = ? AND enabled = 1) AS chat,
885
+ (SELECT COUNT(*) FROM embedding_models WHERE platform = ? AND enabled = 1) AS emb,
886
+ (SELECT COUNT(*) FROM media_models WHERE platform = ? AND enabled = 1) AS media
887
+ `).get(platform, platform, platform);
888
+ if (counts.chat > 0 && counts.emb > 0 && counts.media > 0)
889
+ continue;
890
+ try {
891
+ await autoDiscoverModels(platform);
892
+ }
893
+ catch (err) {
894
+ console.warn(`[auto-discover] background discovery for ${platform} failed: ${err instanceof Error ? err.message : err}`);
895
+ }
896
+ }
897
+ }
898
+ /**
899
+ * Re-verify every configured platform's roster against its live upstream.
900
+ *
901
+ * A discovery run is a snapshot, and upstream rosters move: models are added,
902
+ * dropped, or flip behind a paid gate without any catalog noticing. This
903
+ * function sweeps ALL model types (chat, embeddings, media) for discovery-owned
904
+ * platforms.
905
+ */
906
+ export async function refreshDiscoveredModels() {
907
+ const db = getDb();
908
+ // Find platforms where ANY table has source='user' rows (chat, embeddings, or media)
909
+ const discoveryOwned = db.prepare(`
910
+ SELECT DISTINCT platform FROM (
911
+ SELECT platform FROM models WHERE source = 'user'
912
+ UNION
913
+ SELECT platform FROM embedding_models WHERE key_id IS NOT NULL
914
+ UNION
915
+ SELECT platform FROM media_models WHERE key_id IS NOT NULL
916
+ )
917
+ `).all();
918
+ const targeted = new Set(discoveryOwned.map(r => r.platform));
919
+ // Platforms whose provider publishes an authoritative `access_tier` are
920
+ // discovery-first by design — xkiro's roster comes from discovery even while
921
+ // its only existing rows are catalog-authored, so a row-source scan alone
922
+ // would skip it forever (the exact gap that let the roster go stale).
923
+ for (const p of configuredPlatforms(db)) {
924
+ const provider = resolveProvider(p);
925
+ if (provider && provider.hasAccessTier)
926
+ targeted.add(p);
927
+ }
928
+ // One bell row per provider that changed, so the entry names WHICH provider
929
+ // gained or lost models. Discovery already runs per platform, so keeping the
930
+ // results keyed by platform is the natural shape — a single rolled-up line
931
+ // across every provider is what made these notifications unreadable.
932
+ const changesByPlatform = new Map();
933
+ const bucket = (platform) => {
934
+ const existing = changesByPlatform.get(platform);
935
+ if (existing)
936
+ return existing;
937
+ const fresh = { added: [], removed: [] };
938
+ changesByPlatform.set(platform, fresh);
939
+ return fresh;
940
+ };
941
+ for (const platform of targeted) {
942
+ if (SKIP_AUTO_DISCOVER.has(platform))
943
+ continue;
944
+ const hasKey = db.prepare('SELECT 1 FROM api_keys WHERE platform = ? AND enabled = 1 LIMIT 1').get(platform);
945
+ if (!hasKey)
946
+ continue;
947
+ try {
948
+ const result = await autoDiscoverModels(platform, { refresh: true });
949
+ if (result.registered === 0 && result.retired === 0)
950
+ continue;
951
+ const entry = bucket(platform);
952
+ entry.added.push(...result.newModels);
953
+ entry.removed.push(...result.retiredModels);
954
+ }
955
+ catch (err) {
956
+ console.warn(`[auto-discover] refresh for ${platform} failed: ${err instanceof Error ? err.message : err}`);
957
+ }
958
+ }
959
+ if (changesByPlatform.size === 0)
960
+ return;
961
+ const now = Date.now();
962
+ const notifs = [];
963
+ const names = (list) => list.slice(0, DISCOVER_NOTIFICATION_MODELS).join(', ') + (list.length > DISCOVER_NOTIFICATION_MODELS ? '…' : '');
964
+ for (const [platform, change] of changesByPlatform) {
965
+ if (change.added.length > 0) {
966
+ notifs.push({
967
+ id: `discover-${now}-added-${platform}`,
968
+ type: 'added',
969
+ modelId: '*',
970
+ platform,
971
+ displayName: `${change.added.length} model baru`,
972
+ message: names(change.added),
973
+ data: { key: 'notifications.discoverAdded', vars: { count: change.added.length, platform } },
974
+ timestamp: now,
975
+ read: false,
976
+ });
977
+ }
978
+ if (change.removed.length > 0) {
979
+ notifs.push({
980
+ id: `discover-${now}-removed-${platform}`,
981
+ type: 'removed',
982
+ modelId: '*',
983
+ platform,
984
+ displayName: `${change.removed.length} model dihapus`,
985
+ message: names(change.removed),
986
+ data: { key: 'notifications.discoverRemoved', vars: { count: change.removed.length, platform } },
987
+ timestamp: now,
988
+ read: false,
989
+ });
990
+ }
991
+ }
992
+ addNotifications(notifs);
993
+ }
994
+ function configuredPlatforms(db) {
995
+ return db.prepare('SELECT DISTINCT platform FROM api_keys WHERE enabled = 1').all()
996
+ .map((r) => r.platform);
997
+ }
998
+ /** First pass after boot: long enough for the DB and catalog sync to settle,
999
+ * short enough that a stale roster discovered before restart self-heals in
1000
+ * minutes rather than at the next tick. */
1001
+ export const MODEL_REFRESH_BOOT_DELAY_MS = 5 * 60 * 1000;
1002
+ let cancelRefresh;
1003
+ let cancelBootRefresh;
1004
+ let currentScheduler = null;
1005
+ /**
1006
+ * Schedule the recurring roster re-verification.
1007
+ * Uses the same configurable interval as catalog sync (sync_interval_hours).
1008
+ * When sync schedule is 'off', auto-discover also stops recurring (manual only).
1009
+ */
1010
+ export function startModelRefresh(scheduler) {
1011
+ if (cancelRefresh && cancelBootRefresh)
1012
+ return;
1013
+ if (process.env.MODEL_REFRESH_DISABLED === '1') {
1014
+ console.log('[auto-discover] refresh disabled via MODEL_REFRESH_DISABLED=1');
1015
+ return;
1016
+ }
1017
+ currentScheduler = scheduler;
1018
+ cancelBootRefresh = scheduler.after(MODEL_REFRESH_BOOT_DELAY_MS, () => refreshDiscoveredModels());
1019
+ restartRefreshInterval();
1020
+ }
1021
+ /** Read the same sync_interval_hours setting used by catalog sync.
1022
+ * Delegates to getSyncIntervalHours() so the default (unset) matches
1023
+ * catalog sync's 12h; only an explicit 0/'off' disables the timer. */
1024
+ function getAutoDiscoverIntervalMs() {
1025
+ const hours = getSyncIntervalHours();
1026
+ if (hours === 'off')
1027
+ return 0;
1028
+ return hours * 60 * 60 * 1000;
1029
+ }
1030
+ /** Restart the recurring auto-discover timer (call when sync schedule changes). */
1031
+ export function restartRefreshInterval() {
1032
+ if (!currentScheduler)
1033
+ return;
1034
+ if (cancelRefresh) {
1035
+ cancelRefresh();
1036
+ cancelRefresh = undefined;
1037
+ }
1038
+ const ms = getAutoDiscoverIntervalMs();
1039
+ if (ms > 0) {
1040
+ cancelRefresh = currentScheduler.every(ms, () => { void refreshDiscoveredModels(); });
1041
+ console.log(`[auto-discover] re-verifying rosters every ${ms / 3600000}h (first pass in ${MODEL_REFRESH_BOOT_DELAY_MS / 60000}min)`);
1042
+ }
1043
+ else {
1044
+ console.log('[auto-discover] recurring refresh disabled (manual mode)');
1045
+ }
1046
+ }
1047
+ //# sourceMappingURL=auto-discover.js.map