omniroute 2.8.4 → 2.8.6

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 (267) hide show
  1. package/app/.next/BUILD_ID +1 -1
  2. package/app/.next/build-manifest.json +2 -2
  3. package/app/.next/prerender-manifest.json +3 -3
  4. package/app/.next/server/app/(dashboard)/dashboard/a2a/page_client-reference-manifest.js +1 -1
  5. package/app/.next/server/app/(dashboard)/dashboard/agents/page_client-reference-manifest.js +1 -1
  6. package/app/.next/server/app/(dashboard)/dashboard/analytics/page_client-reference-manifest.js +1 -1
  7. package/app/.next/server/app/(dashboard)/dashboard/api-manager/page_client-reference-manifest.js +1 -1
  8. package/app/.next/server/app/(dashboard)/dashboard/audit-log/page_client-reference-manifest.js +1 -1
  9. package/app/.next/server/app/(dashboard)/dashboard/auto-combo/page_client-reference-manifest.js +1 -1
  10. package/app/.next/server/app/(dashboard)/dashboard/cli-tools/page_client-reference-manifest.js +1 -1
  11. package/app/.next/server/app/(dashboard)/dashboard/combos/page_client-reference-manifest.js +1 -1
  12. package/app/.next/server/app/(dashboard)/dashboard/costs/page_client-reference-manifest.js +1 -1
  13. package/app/.next/server/app/(dashboard)/dashboard/endpoint/page_client-reference-manifest.js +1 -1
  14. package/app/.next/server/app/(dashboard)/dashboard/health/page_client-reference-manifest.js +1 -1
  15. package/app/.next/server/app/(dashboard)/dashboard/limits/page_client-reference-manifest.js +1 -1
  16. package/app/.next/server/app/(dashboard)/dashboard/logs/page_client-reference-manifest.js +1 -1
  17. package/app/.next/server/app/(dashboard)/dashboard/mcp/page_client-reference-manifest.js +1 -1
  18. package/app/.next/server/app/(dashboard)/dashboard/media/page_client-reference-manifest.js +1 -1
  19. package/app/.next/server/app/(dashboard)/dashboard/onboarding/page_client-reference-manifest.js +1 -1
  20. package/app/.next/server/app/(dashboard)/dashboard/page_client-reference-manifest.js +1 -1
  21. package/app/.next/server/app/(dashboard)/dashboard/playground/page_client-reference-manifest.js +1 -1
  22. package/app/.next/server/app/(dashboard)/dashboard/profile/page_client-reference-manifest.js +1 -1
  23. package/app/.next/server/app/(dashboard)/dashboard/providers/[id]/page_client-reference-manifest.js +1 -1
  24. package/app/.next/server/app/(dashboard)/dashboard/providers/new/page_client-reference-manifest.js +1 -1
  25. package/app/.next/server/app/(dashboard)/dashboard/providers/page_client-reference-manifest.js +1 -1
  26. package/app/.next/server/app/(dashboard)/dashboard/search-tools/page_client-reference-manifest.js +1 -1
  27. package/app/.next/server/app/(dashboard)/dashboard/settings/page_client-reference-manifest.js +1 -1
  28. package/app/.next/server/app/(dashboard)/dashboard/settings/pricing/page_client-reference-manifest.js +1 -1
  29. package/app/.next/server/app/(dashboard)/dashboard/translator/page_client-reference-manifest.js +1 -1
  30. package/app/.next/server/app/(dashboard)/dashboard/usage/page_client-reference-manifest.js +1 -1
  31. package/app/.next/server/app/400/page_client-reference-manifest.js +1 -1
  32. package/app/.next/server/app/401/page_client-reference-manifest.js +1 -1
  33. package/app/.next/server/app/403/page_client-reference-manifest.js +1 -1
  34. package/app/.next/server/app/408/page_client-reference-manifest.js +1 -1
  35. package/app/.next/server/app/429/page_client-reference-manifest.js +1 -1
  36. package/app/.next/server/app/500/page_client-reference-manifest.js +1 -1
  37. package/app/.next/server/app/502/page_client-reference-manifest.js +1 -1
  38. package/app/.next/server/app/503/page_client-reference-manifest.js +1 -1
  39. package/app/.next/server/app/_global-error.html +2 -2
  40. package/app/.next/server/app/_global-error.rsc +1 -1
  41. package/app/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  42. package/app/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  43. package/app/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  44. package/app/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  45. package/app/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  46. package/app/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  47. package/app/.next/server/app/api/acp/agents/route.js +1 -1
  48. package/app/.next/server/app/api/acp/agents/route.js.nft.json +1 -1
  49. package/app/.next/server/app/api/auth/login/route.js +1 -1
  50. package/app/.next/server/app/api/auth/login/route.js.nft.json +1 -1
  51. package/app/.next/server/app/api/cli-tools/antigravity-mitm/alias/route.js +1 -1
  52. package/app/.next/server/app/api/cli-tools/antigravity-mitm/alias/route.js.nft.json +1 -1
  53. package/app/.next/server/app/api/cloud/auth/route.js +1 -1
  54. package/app/.next/server/app/api/cloud/auth/route.js.nft.json +1 -1
  55. package/app/.next/server/app/api/cloud/credentials/update/route.js +1 -1
  56. package/app/.next/server/app/api/cloud/credentials/update/route.js.nft.json +1 -1
  57. package/app/.next/server/app/api/cloud/model/resolve/route.js +1 -1
  58. package/app/.next/server/app/api/cloud/model/resolve/route.js.nft.json +1 -1
  59. package/app/.next/server/app/api/cloud/models/alias/route.js +1 -1
  60. package/app/.next/server/app/api/cloud/models/alias/route.js.nft.json +1 -1
  61. package/app/.next/server/app/api/combos/[id]/route.js +1 -1
  62. package/app/.next/server/app/api/combos/[id]/route.js.nft.json +1 -1
  63. package/app/.next/server/app/api/combos/route.js +1 -1
  64. package/app/.next/server/app/api/combos/route.js.nft.json +1 -1
  65. package/app/.next/server/app/api/combos/test/route.js +1 -1
  66. package/app/.next/server/app/api/combos/test/route.js.nft.json +1 -1
  67. package/app/.next/server/app/api/db-backups/export/route.js +1 -1
  68. package/app/.next/server/app/api/db-backups/export/route.js.nft.json +1 -1
  69. package/app/.next/server/app/api/db-backups/import/route.js +1 -1
  70. package/app/.next/server/app/api/db-backups/import/route.js.nft.json +1 -1
  71. package/app/.next/server/app/api/db-backups/route.js +1 -1
  72. package/app/.next/server/app/api/db-backups/route.js.nft.json +1 -1
  73. package/app/.next/server/app/api/keys/[id]/route.js +1 -1
  74. package/app/.next/server/app/api/keys/[id]/route.js.nft.json +1 -1
  75. package/app/.next/server/app/api/keys/route.js +1 -1
  76. package/app/.next/server/app/api/keys/route.js.nft.json +1 -1
  77. package/app/.next/server/app/api/logs/detail/route.js +1 -1
  78. package/app/.next/server/app/api/logs/detail/route.js.nft.json +1 -1
  79. package/app/.next/server/app/api/models/alias/route.js +1 -1
  80. package/app/.next/server/app/api/models/alias/route.js.nft.json +1 -1
  81. package/app/.next/server/app/api/models/openrouter-catalog/route.js +1 -1
  82. package/app/.next/server/app/api/models/openrouter-catalog/route.js.nft.json +1 -1
  83. package/app/.next/server/app/api/models/route.js +1 -1
  84. package/app/.next/server/app/api/models/route.js.nft.json +1 -1
  85. package/app/.next/server/app/api/monitoring/health/route.js +1 -1
  86. package/app/.next/server/app/api/monitoring/health/route.js.nft.json +1 -1
  87. package/app/.next/server/app/api/oauth/[provider]/[action]/route.js +1 -1
  88. package/app/.next/server/app/api/oauth/[provider]/[action]/route.js.nft.json +1 -1
  89. package/app/.next/server/app/api/oauth/cursor/auto-import/route.js +1 -1
  90. package/app/.next/server/app/api/oauth/cursor/auto-import/route.js.nft.json +1 -1
  91. package/app/.next/server/app/api/oauth/cursor/import/route.js +1 -1
  92. package/app/.next/server/app/api/oauth/cursor/import/route.js.nft.json +1 -1
  93. package/app/.next/server/app/api/oauth/kiro/auto-import/route.js +1 -1
  94. package/app/.next/server/app/api/oauth/kiro/auto-import/route.js.nft.json +1 -1
  95. package/app/.next/server/app/api/oauth/kiro/import/route.js +1 -1
  96. package/app/.next/server/app/api/oauth/kiro/import/route.js.nft.json +1 -1
  97. package/app/.next/server/app/api/oauth/kiro/social-exchange/route.js +1 -1
  98. package/app/.next/server/app/api/oauth/kiro/social-exchange/route.js.nft.json +1 -1
  99. package/app/.next/server/app/api/pricing/models/route.js +1 -1
  100. package/app/.next/server/app/api/pricing/models/route.js.nft.json +1 -1
  101. package/app/.next/server/app/api/pricing/route.js +1 -1
  102. package/app/.next/server/app/api/pricing/route.js.nft.json +1 -1
  103. package/app/.next/server/app/api/provider-models/route.js +2 -2
  104. package/app/.next/server/app/api/provider-models/route.js.nft.json +1 -1
  105. package/app/.next/server/app/api/provider-nodes/[id]/route.js +1 -1
  106. package/app/.next/server/app/api/provider-nodes/[id]/route.js.nft.json +1 -1
  107. package/app/.next/server/app/api/provider-nodes/route.js +1 -1
  108. package/app/.next/server/app/api/provider-nodes/route.js.nft.json +1 -1
  109. package/app/.next/server/app/api/providers/[id]/models/route.js +1 -1
  110. package/app/.next/server/app/api/providers/[id]/models/route.js.nft.json +1 -1
  111. package/app/.next/server/app/api/providers/[id]/refresh/route.js +1 -1
  112. package/app/.next/server/app/api/providers/[id]/refresh/route.js.nft.json +1 -1
  113. package/app/.next/server/app/api/providers/[id]/route.js +1 -1
  114. package/app/.next/server/app/api/providers/[id]/route.js.nft.json +1 -1
  115. package/app/.next/server/app/api/providers/[id]/test/route.js +1 -1
  116. package/app/.next/server/app/api/providers/[id]/test/route.js.nft.json +1 -1
  117. package/app/.next/server/app/api/providers/client/route.js +1 -1
  118. package/app/.next/server/app/api/providers/client/route.js.nft.json +1 -1
  119. package/app/.next/server/app/api/providers/route.js +1 -1
  120. package/app/.next/server/app/api/providers/route.js.nft.json +1 -1
  121. package/app/.next/server/app/api/providers/test-batch/route.js +2 -2
  122. package/app/.next/server/app/api/providers/test-batch/route.js.nft.json +1 -1
  123. package/app/.next/server/app/api/providers/validate/route.js +1 -1
  124. package/app/.next/server/app/api/providers/validate/route.js.nft.json +1 -1
  125. package/app/.next/server/app/api/rate-limits/route.js +1 -1
  126. package/app/.next/server/app/api/rate-limits/route.js.nft.json +1 -1
  127. package/app/.next/server/app/api/resilience/route.js +1 -1
  128. package/app/.next/server/app/api/resilience/route.js.nft.json +1 -1
  129. package/app/.next/server/app/api/search/providers/route.js +1 -1
  130. package/app/.next/server/app/api/search/providers/route.js.nft.json +1 -1
  131. package/app/.next/server/app/api/search/stats/route.js +1 -1
  132. package/app/.next/server/app/api/search/stats/route.js.nft.json +1 -1
  133. package/app/.next/server/app/api/settings/codex-service-tier/route.js +1 -1
  134. package/app/.next/server/app/api/settings/codex-service-tier/route.js.nft.json +1 -1
  135. package/app/.next/server/app/api/settings/combo-defaults/route.js +1 -1
  136. package/app/.next/server/app/api/settings/combo-defaults/route.js.nft.json +1 -1
  137. package/app/.next/server/app/api/settings/proxies/assignments/route.js +2 -2
  138. package/app/.next/server/app/api/settings/proxies/bulk-assign/route.js +2 -2
  139. package/app/.next/server/app/api/settings/proxies/route.js +1 -1
  140. package/app/.next/server/app/api/settings/proxy/route.js +2 -2
  141. package/app/.next/server/app/api/settings/require-login/route.js +1 -1
  142. package/app/.next/server/app/api/settings/require-login/route.js.nft.json +1 -1
  143. package/app/.next/server/app/api/settings/route.js +1 -1
  144. package/app/.next/server/app/api/settings/route.js.nft.json +1 -1
  145. package/app/.next/server/app/api/settings/system-prompt/route.js +1 -1
  146. package/app/.next/server/app/api/settings/system-prompt/route.js.nft.json +1 -1
  147. package/app/.next/server/app/api/settings/thinking-budget/route.js +1 -1
  148. package/app/.next/server/app/api/settings/thinking-budget/route.js.nft.json +1 -1
  149. package/app/.next/server/app/api/sync/cloud/route.js +1 -1
  150. package/app/.next/server/app/api/sync/cloud/route.js.nft.json +1 -1
  151. package/app/.next/server/app/api/sync/initialize/route.js +1 -1
  152. package/app/.next/server/app/api/sync/initialize/route.js.nft.json +1 -1
  153. package/app/.next/server/app/api/system/version/route.js +1 -1
  154. package/app/.next/server/app/api/system/version/route.js.nft.json +1 -1
  155. package/app/.next/server/app/api/translator/send/route.js +1 -1
  156. package/app/.next/server/app/api/translator/send/route.js.nft.json +1 -1
  157. package/app/.next/server/app/api/translator/translate/route.js +1 -1
  158. package/app/.next/server/app/api/translator/translate/route.js.nft.json +1 -1
  159. package/app/.next/server/app/api/usage/quota/route.js +1 -1
  160. package/app/.next/server/app/api/usage/quota/route.js.nft.json +1 -1
  161. package/app/.next/server/app/api/v1/management/proxies/assignments/route.js +1 -1
  162. package/app/.next/server/app/api/v1/management/proxies/assignments/route.js.nft.json +1 -1
  163. package/app/.next/server/app/api/v1/management/proxies/bulk-assign/route.js +1 -1
  164. package/app/.next/server/app/api/v1/management/proxies/bulk-assign/route.js.nft.json +1 -1
  165. package/app/.next/server/app/api/v1/management/proxies/health/route.js +1 -1
  166. package/app/.next/server/app/api/v1/management/proxies/health/route.js.nft.json +1 -1
  167. package/app/.next/server/app/api/v1/management/proxies/route.js +1 -1
  168. package/app/.next/server/app/api/v1/management/proxies/route.js.nft.json +1 -1
  169. package/app/.next/server/app/api/v1/models/route.js +1 -1
  170. package/app/.next/server/app/api/v1/models/route.js.nft.json +1 -1
  171. package/app/.next/server/app/api/v1/route.js +1 -1
  172. package/app/.next/server/app/api/v1/route.js.nft.json +1 -1
  173. package/app/.next/server/app/callback/page_client-reference-manifest.js +1 -1
  174. package/app/.next/server/app/docs/page_client-reference-manifest.js +1 -1
  175. package/app/.next/server/app/forbidden/page_client-reference-manifest.js +1 -1
  176. package/app/.next/server/app/forgot-password/page_client-reference-manifest.js +1 -1
  177. package/app/.next/server/app/landing/page_client-reference-manifest.js +1 -1
  178. package/app/.next/server/app/login/page_client-reference-manifest.js +1 -1
  179. package/app/.next/server/app/maintenance/page_client-reference-manifest.js +1 -1
  180. package/app/.next/server/app/offline/page_client-reference-manifest.js +1 -1
  181. package/app/.next/server/app/page_client-reference-manifest.js +1 -1
  182. package/app/.next/server/app/privacy/page_client-reference-manifest.js +1 -1
  183. package/app/.next/server/app/status/page_client-reference-manifest.js +1 -1
  184. package/app/.next/server/app/terms/page_client-reference-manifest.js +1 -1
  185. package/app/.next/server/chunks/[root-of-the-server]__09c944b3._.js +1 -1
  186. package/app/.next/server/chunks/[root-of-the-server]__134baf4c._.js +1 -1
  187. package/app/.next/server/chunks/[root-of-the-server]__179c5303._.js +1 -1
  188. package/app/.next/server/chunks/[root-of-the-server]__237e5042._.js +1 -1
  189. package/app/.next/server/chunks/[root-of-the-server]__46fad57a._.js +1 -1
  190. package/app/.next/server/chunks/[root-of-the-server]__784fb7c5._.js +1 -1
  191. package/app/.next/server/chunks/[root-of-the-server]__7d9b23e7._.js +1 -1
  192. package/app/.next/server/chunks/[root-of-the-server]__80e3bfc3._.js +2 -2
  193. package/app/.next/server/chunks/[root-of-the-server]__84e445b2._.js +1 -1
  194. package/app/.next/server/chunks/[root-of-the-server]__92cb0def._.js +1 -1
  195. package/app/.next/server/chunks/[root-of-the-server]__9bbd49c8._.js +1 -1
  196. package/app/.next/server/chunks/[root-of-the-server]__a630d6ef._.js +5 -5
  197. package/app/.next/server/chunks/[root-of-the-server]__add0a68c._.js +1 -1
  198. package/app/.next/server/chunks/[root-of-the-server]__c393c81f._.js +1 -1
  199. package/app/.next/server/chunks/[root-of-the-server]__c8e3c8a9._.js +50 -0
  200. package/app/.next/server/chunks/[root-of-the-server]__cd42b732._.js +1 -1
  201. package/app/.next/server/chunks/[root-of-the-server]__d4563e10._.js +1 -1
  202. package/app/.next/server/chunks/[root-of-the-server]__db2f9fe0._.js +1 -1
  203. package/app/.next/server/chunks/[root-of-the-server]__e27a89bd._.js +1 -1
  204. package/app/.next/server/chunks/[root-of-the-server]__e56edf04._.js +1 -1
  205. package/app/.next/server/chunks/[root-of-the-server]__e6e94646._.js +1 -1
  206. package/app/.next/server/chunks/[root-of-the-server]__eb98039a._.js +1 -1
  207. package/app/.next/server/chunks/[root-of-the-server]__f31b4656._.js +1 -1
  208. package/app/.next/server/chunks/_05c48915._.js +1 -1
  209. package/app/.next/server/chunks/_1244636c._.js +2 -2
  210. package/app/.next/server/chunks/_14963fed._.js +1 -1
  211. package/app/.next/server/chunks/_1717e651._.js +1 -1
  212. package/app/.next/server/chunks/_2115d8de._.js +1 -1
  213. package/app/.next/server/chunks/_3ac953eb._.js +1 -1
  214. package/app/.next/server/chunks/_4b8fd853._.js +1 -1
  215. package/app/.next/server/chunks/_5bbb2e7a._.js +1 -1
  216. package/app/.next/server/chunks/_68683848._.js +1 -1
  217. package/app/.next/server/chunks/_c05b9de3._.js +1 -1
  218. package/app/.next/server/chunks/_c795fc74._.js +1 -1
  219. package/app/.next/server/chunks/_ee7b7859._.js +1 -1
  220. package/app/.next/server/chunks/_ee9b677b._.js +1 -1
  221. package/app/.next/server/chunks/open-sse_cf4d5692._.js +1 -1
  222. package/app/.next/server/chunks/open-sse_config_constants_ts_9583de19._.js +1 -1
  223. package/app/.next/server/chunks/open-sse_services_826884e1._.js +2 -1
  224. package/app/.next/server/chunks/open-sse_translator_index_ts_f5fd0821._.js +2 -2
  225. package/app/.next/server/chunks/src_lib_localDb_ts_83220848._.js +1 -1
  226. package/app/.next/server/chunks/src_shared_validation_schemas_ts_4e63863a._.js +1 -1
  227. package/app/.next/server/chunks/ssr/[root-of-the-server]__9affb65e._.js +1 -1
  228. package/app/.next/server/chunks/ssr/[root-of-the-server]__a6942102._.js +1 -1
  229. package/app/.next/server/chunks/ssr/src_9197fb9b._.js +2 -2
  230. package/app/.next/server/chunks/ssr/src_d3225e36._.js +1 -1
  231. package/app/.next/server/chunks/ssr/src_i18n_messages_en_json_c3d5c412._.js +1 -1
  232. package/app/.next/server/chunks/ssr/src_i18n_messages_zh-CN_json_f4112d90._.js +1 -1
  233. package/app/.next/server/chunks/ssr/src_lib_initCloudSync_ts_982b9d4d._.js +1 -1
  234. package/app/.next/server/pages/500.html +2 -2
  235. package/app/.next/server/server-reference-manifest.js +1 -1
  236. package/app/.next/server/server-reference-manifest.json +1 -1
  237. package/app/.next/static/chunks/1042694db0c08f1f.css +1 -0
  238. package/app/.next/static/chunks/{0f71d7fbf89bb737.js → 34569b4e9d93c0ad.js} +2 -2
  239. package/app/.next/static/chunks/{1e206030e7793015.js → 353ef826fbae436d.js} +1 -1
  240. package/app/.next/static/chunks/91761ba00c702cff.js +1 -0
  241. package/app/CHANGELOG.md +56 -0
  242. package/app/docs/openapi.yaml +1 -1
  243. package/app/open-sse/config/constants.ts +3 -3
  244. package/app/open-sse/handlers/chatCore.ts +6 -2
  245. package/app/open-sse/services/comboAgentMiddleware.ts +9 -1
  246. package/app/open-sse/services/roleNormalizer.ts +14 -19
  247. package/app/open-sse/translator/index.ts +27 -3
  248. package/app/package-lock.json +2 -2
  249. package/app/package.json +1 -1
  250. package/app/src/app/(dashboard)/dashboard/cli-tools/CLIToolsPageClient.tsx +12 -0
  251. package/app/src/app/(dashboard)/dashboard/cli-tools/components/AntigravityToolCard.tsx +5 -5
  252. package/app/src/app/(dashboard)/dashboard/providers/[id]/page.tsx +381 -62
  253. package/app/src/app/api/provider-models/route.ts +47 -6
  254. package/app/src/i18n/messages/en.json +10 -0
  255. package/app/src/i18n/messages/zh-CN.json +10 -0
  256. package/app/src/lib/db/models.ts +130 -13
  257. package/app/src/lib/localDb.ts +5 -0
  258. package/app/src/shared/constants/cliTools.ts +2 -2
  259. package/app/src/shared/validation/schemas.ts +1 -0
  260. package/app/tests/e2e/providers-bailian-coding-plan.spec.ts +14 -0
  261. package/package.json +1 -1
  262. package/app/.next/server/chunks/[root-of-the-server]__3972de72._.js +0 -50
  263. package/app/.next/static/chunks/af071ff826ecff1b.css +0 -1
  264. package/app/.next/static/chunks/d19ab4efcaddd1db.js +0 -1
  265. /package/app/.next/static/{Ys6bRJXPNt8j-MKOjvZwp → m9BrfW8GQTdaCjvX6OgP2}/_buildManifest.js +0 -0
  266. /package/app/.next/static/{Ys6bRJXPNt8j-MKOjvZwp → m9BrfW8GQTdaCjvX6OgP2}/_clientMiddlewareManifest.json +0 -0
  267. /package/app/.next/static/{Ys6bRJXPNt8j-MKOjvZwp → m9BrfW8GQTdaCjvX6OgP2}/_ssgManifest.js +0 -0
@@ -1 +1 @@
1
- (globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,804730,t=>{t.v({name:"omniroute",version:"2.8.4",description:"Smart AI Router with auto fallback — route to FREE & cheap models, zero downtime. Works with Cursor, Cline, Claude Desktop, Codex, and any OpenAI-compatible tool.",type:"module",bin:{omniroute:"bin/omniroute.mjs","omniroute-reset-password":"bin/reset-password.mjs"},files:["bin/","app/","open-sse/mcp-server/","src/shared/contracts/","scripts/postinstall.mjs","scripts/native-binary-compat.mjs","README.md","LICENSE"],workspaces:["open-sse"],engines:{node:">=18.0.0 <24.0.0"},keywords:["ai","router","proxy","openai","claude","anthropic","gemini","fallback","cursor","cline","codex","llm","auto-fallback"],license:"MIT",author:"diegosouzapw",repository:{type:"git",url:"https://github.com/diegosouzapw/OmniRoute"},homepage:"https://omniroute.online",scripts:{dev:"node scripts/run-next.mjs dev",build:"next build","build:cli":"node scripts/prepublish.mjs",start:"node scripts/run-next.mjs start",lint:"eslint .","electron:dev":'concurrently "npm run dev" "wait-on http://localhost:20128 && cd electron && npm run dev"',"electron:build":"npm run build && cd electron && npm run build","electron:build:win":"npm run build && cd electron && npm run build:win","electron:build:mac":"npm run build && cd electron && npm run build:mac","electron:build:linux":"npm run build && cd electron && npm run build:linux",test:"node --import tsx/esm --test tests/unit/*.test.mjs","test:unit":"node --import tsx/esm --test tests/unit/*.test.mjs","test:plan3":"node --import tsx/esm --test tests/unit/plan3-p0.test.mjs","test:fixes":"node --import tsx/esm --test tests/unit/fixes-p1.test.mjs","test:security":"node --import tsx/esm --test tests/unit/security-fase01.test.mjs","check:cycles":"node scripts/check-cycles.mjs","check:route-validation:t06":"node scripts/check-route-validation.mjs","check:any-budget:t11":"node scripts/check-t11-any-budget.mjs","check:docs-sync":"node scripts/check-docs-sync.mjs","typecheck:core":"tsc --pretty false -p tsconfig.typecheck-core.json","typecheck:noimplicit:core":"tsc --pretty false -p tsconfig.typecheck-noimplicit-core.json","test:integration":"node --import tsx/esm --test tests/integration/*.test.mjs","test:e2e":"node scripts/run-playwright-tests.mjs test tests/e2e/*.spec.ts","test:protocols:e2e":"node scripts/run-protocol-clients-tests.mjs","test:vitest":"vitest run open-sse/mcp-server/__tests__/*.test.ts open-sse/services/autoCombo/__tests__/*.test.ts","test:ecosystem":"node scripts/run-ecosystem-tests.mjs","test:coverage":"npx c8 --exclude=open-sse --check-coverage --lines 50 --functions 50 --branches 50 node --import tsx/esm --test tests/unit/*.test.mjs","test:all":"npm run test:unit && npm run test:vitest && npm run test:ecosystem && npm run test:e2e",check:"npm run lint && npm run test",prepublishOnly:"npm run build:cli",postinstall:"node scripts/postinstall.mjs",prepare:"husky","system-info":"node scripts/system-info.mjs"},dependencies:{"@modelcontextprotocol/sdk":"^1.27.1","@monaco-editor/react":"^4.7.0",bcryptjs:"^3.0.3","better-sqlite3":"^12.6.2",bottleneck:"^2.19.5",dompurify:"^3.3.2",express:"^5.2.1","fetch-socks":"^1.3.2","http-proxy-middleware":"^3.0.5","https-proxy-agent":"^8.0.0",jose:"^6.1.3",lowdb:"^7.0.1","monaco-editor":"^0.55.1",next:"^16.1.6","next-intl":"^4.8.3","node-machine-id":"^1.1.12",open:"^11.0.0",ora:"^9.1.0",pino:"^10.3.1","pino-pretty":"^13.1.3",react:"19.2.4","react-dom":"19.2.4",recharts:"^3.7.0",selfsigned:"^5.5.0",tsx:"^4.21.0",undici:"^7.19.2",uuid:"^13.0.0","wreq-js":"^2.0.1",zod:"^4.3.6",zustand:"^5.0.10","@swc/helpers":"0.5.19"},devDependencies:{"@playwright/test":"^1.58.2","@tailwindcss/postcss":"^4.1.18","@types/bcryptjs":"^3.0.0","@types/better-sqlite3":"^7.6.13","@types/node":"^25.2.3","@types/react":"^19.2.14","@types/react-dom":"^19.2.3",concurrently:"^9.2.1","cross-env":"^10.1.0",eslint:"^9.39.2","eslint-config-next":"16.1.6",husky:"^9.1.7","lint-staged":"^16.2.7",prettier:"^3.8.1",tailwindcss:"^4",typescript:"^5.9.3","typescript-eslint":"^8.56.0",vitest:"^4.0.18","wait-on":"^9.0.4"},"lint-staged":{"*.{js,jsx,ts,tsx,mjs}":["prettier --write","eslint --fix --no-error-on-unmatched-pattern"],"*.{json,md,yml,yaml,css}":["prettier --write"]},pnpm:{onlyBuiltDependencies:["@parcel/watcher","@swc/core","better-sqlite3","esbuild","omniroute","sharp"]}})},175696,t=>{"use strict";var e=t.i(861745),s=t.i(843476);function n({locale:t,...n}){if(!t)throw Error(void 0);return(0,s.jsx)(e.IntlProvider,{locale:t,...n})}t.s(["default",()=>n])}]);
1
+ (globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,804730,t=>{t.v({name:"omniroute",version:"2.8.6",description:"Smart AI Router with auto fallback — route to FREE & cheap models, zero downtime. Works with Cursor, Cline, Claude Desktop, Codex, and any OpenAI-compatible tool.",type:"module",bin:{omniroute:"bin/omniroute.mjs","omniroute-reset-password":"bin/reset-password.mjs"},files:["bin/","app/","open-sse/mcp-server/","src/shared/contracts/","scripts/postinstall.mjs","scripts/native-binary-compat.mjs","README.md","LICENSE"],workspaces:["open-sse"],engines:{node:">=18.0.0 <24.0.0"},keywords:["ai","router","proxy","openai","claude","anthropic","gemini","fallback","cursor","cline","codex","llm","auto-fallback"],license:"MIT",author:"diegosouzapw",repository:{type:"git",url:"https://github.com/diegosouzapw/OmniRoute"},homepage:"https://omniroute.online",scripts:{dev:"node scripts/run-next.mjs dev",build:"next build","build:cli":"node scripts/prepublish.mjs",start:"node scripts/run-next.mjs start",lint:"eslint .","electron:dev":'concurrently "npm run dev" "wait-on http://localhost:20128 && cd electron && npm run dev"',"electron:build":"npm run build && cd electron && npm run build","electron:build:win":"npm run build && cd electron && npm run build:win","electron:build:mac":"npm run build && cd electron && npm run build:mac","electron:build:linux":"npm run build && cd electron && npm run build:linux",test:"node --import tsx/esm --test tests/unit/*.test.mjs","test:unit":"node --import tsx/esm --test tests/unit/*.test.mjs","test:plan3":"node --import tsx/esm --test tests/unit/plan3-p0.test.mjs","test:fixes":"node --import tsx/esm --test tests/unit/fixes-p1.test.mjs","test:security":"node --import tsx/esm --test tests/unit/security-fase01.test.mjs","check:cycles":"node scripts/check-cycles.mjs","check:route-validation:t06":"node scripts/check-route-validation.mjs","check:any-budget:t11":"node scripts/check-t11-any-budget.mjs","check:docs-sync":"node scripts/check-docs-sync.mjs","typecheck:core":"tsc --pretty false -p tsconfig.typecheck-core.json","typecheck:noimplicit:core":"tsc --pretty false -p tsconfig.typecheck-noimplicit-core.json","test:integration":"node --import tsx/esm --test tests/integration/*.test.mjs","test:e2e":"node scripts/run-playwright-tests.mjs test tests/e2e/*.spec.ts","test:protocols:e2e":"node scripts/run-protocol-clients-tests.mjs","test:vitest":"vitest run open-sse/mcp-server/__tests__/*.test.ts open-sse/services/autoCombo/__tests__/*.test.ts","test:ecosystem":"node scripts/run-ecosystem-tests.mjs","test:coverage":"npx c8 --exclude=open-sse --check-coverage --lines 50 --functions 50 --branches 50 node --import tsx/esm --test tests/unit/*.test.mjs","test:all":"npm run test:unit && npm run test:vitest && npm run test:ecosystem && npm run test:e2e",check:"npm run lint && npm run test",prepublishOnly:"npm run build:cli",postinstall:"node scripts/postinstall.mjs",prepare:"husky","system-info":"node scripts/system-info.mjs"},dependencies:{"@modelcontextprotocol/sdk":"^1.27.1","@monaco-editor/react":"^4.7.0",bcryptjs:"^3.0.3","better-sqlite3":"^12.6.2",bottleneck:"^2.19.5",dompurify:"^3.3.2",express:"^5.2.1","fetch-socks":"^1.3.2","http-proxy-middleware":"^3.0.5","https-proxy-agent":"^8.0.0",jose:"^6.1.3",lowdb:"^7.0.1","monaco-editor":"^0.55.1",next:"^16.1.6","next-intl":"^4.8.3","node-machine-id":"^1.1.12",open:"^11.0.0",ora:"^9.1.0",pino:"^10.3.1","pino-pretty":"^13.1.3",react:"19.2.4","react-dom":"19.2.4",recharts:"^3.7.0",selfsigned:"^5.5.0",tsx:"^4.21.0",undici:"^7.19.2",uuid:"^13.0.0","wreq-js":"^2.0.1",zod:"^4.3.6",zustand:"^5.0.10","@swc/helpers":"0.5.19"},devDependencies:{"@playwright/test":"^1.58.2","@tailwindcss/postcss":"^4.1.18","@types/bcryptjs":"^3.0.0","@types/better-sqlite3":"^7.6.13","@types/node":"^25.2.3","@types/react":"^19.2.14","@types/react-dom":"^19.2.3",concurrently:"^9.2.1","cross-env":"^10.1.0",eslint:"^9.39.2","eslint-config-next":"16.1.6",husky:"^9.1.7","lint-staged":"^16.2.7",prettier:"^3.8.1",tailwindcss:"^4",typescript:"^5.9.3","typescript-eslint":"^8.56.0",vitest:"^4.0.18","wait-on":"^9.0.4"},"lint-staged":{"*.{js,jsx,ts,tsx,mjs}":["prettier --write","eslint --fix --no-error-on-unmatched-pattern"],"*.{json,md,yml,yaml,css}":["prettier --write"]},pnpm:{onlyBuiltDependencies:["@parcel/watcher","@swc/core","better-sqlite3","esbuild","omniroute","sharp"]}})},175696,t=>{"use strict";var e=t.i(861745),s=t.i(843476);function n({locale:t,...n}){if(!t)throw Error(void 0);return(0,s.jsx)(e.IntlProvider,{locale:t,...n})}t.s(["default",()=>n])}]);
@@ -0,0 +1 @@
1
+ (globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,710864,e=>{"use strict";var t=e.i(843476),r=e.i(271645),a=e.i(627949),l=e.i(745009),s=e.i(618566),i=e.i(522016),o=e.i(657688),n=e.i(948148);e.i(565650);var d=e.i(11477),c=e.i(474078),p=e.i(628029),u=e.i(339971),m=e.i(87855),x=e.i(454149),h=e.i(116016),h=h,f=e.i(567955),f=f,g=e.i(419947),g=g,v=e.i(148943),b=e.i(961430),y=e.i(105370),j=e.i(623249);e.i(313705);var C=e.i(25230),N=e.i(556938);function k(e){let t=e&&"object"==typeof e&&!Array.isArray(e)?e:{};return{use5h:"boolean"!=typeof t.use5h||t.use5h,useWeekly:"boolean"!=typeof t.useWeekly||t.useWeekly}}function w({t:e,normalizeToolCallId:a,preserveDeveloperRole:l,showDeveloperToggle:s=!0,onNormalizeChange:i,onPreserveChange:o,disabled:n}){let[d,c]=(0,r.useState)(!1),p=(0,r.useRef)(null);return(0,r.useEffect)(()=>{if(!d)return;let e=e=>{p.current&&!p.current.contains(e.target)&&c(!1)};return document.addEventListener("mousedown",e),()=>document.removeEventListener("mousedown",e)},[d]),(0,t.jsxs)("div",{className:"relative inline-block",ref:p,children:[(0,t.jsxs)("button",{type:"button",onClick:()=>c(e=>!e),disabled:n,className:"inline-flex items-center gap-1 px-2 py-1 text-xs rounded-md border border-border bg-sidebar/50 hover:bg-sidebar text-text-muted hover:text-text-main disabled:opacity-50",title:e("compatAdjustmentsTitle"),children:[(0,t.jsx)("span",{className:"material-symbols-outlined text-sm",children:"tune"}),e("compatButtonLabel")]}),d&&(0,t.jsxs)("div",{className:"absolute left-0 top-full mt-1 z-50 min-w-[200px] p-3 rounded-lg border border-border bg-white dark:bg-zinc-900 shadow-xl ring-1 ring-black/5 dark:ring-white/10",children:[(0,t.jsx)("p",{className:"text-[10px] font-semibold uppercase tracking-wide text-text-muted mb-2",children:e("compatAdjustmentsTitle")}),(0,t.jsxs)("div",{className:"flex flex-col gap-3",children:[(0,t.jsx)(v.Toggle,{size:"sm",label:e("compatToolIdShort"),title:e("normalizeToolCallIdLabel"),checked:a,onChange:i,disabled:n}),s&&(0,t.jsx)(v.Toggle,{size:"sm",label:e("compatDoNotPreserveDeveloper"),title:e("preserveDeveloperRoleLabel"),checked:!1===l,onChange:e=>o(!e),disabled:n})]})]})]})}function T(){let e=(0,s.useParams)(),l=(0,s.useRouter)(),p=e.id,[u,v]=(0,r.useState)([]),[b,w]=(0,r.useState)(!0),[T,R]=(0,r.useState)(null),[E,D]=(0,r.useState)(!1),[U,_]=(0,r.useState)(!1),[K,F]=(0,r.useState)(!1),[z,q]=(0,r.useState)(!1),[B,J]=(0,r.useState)(null),[W,V]=(0,r.useState)(null),[H,Y]=(0,r.useState)({}),[G,Q]=(0,r.useState)(!1),{copied:X,copy:Z}=(0,N.useCopyToClipboard)(),ee=(0,n.useTranslations)("providers"),et=(0,a.useNotificationStore)(),er=(0,r.useRef)(!1),ea=(0,r.useRef)(!1),[el,es]=(0,r.useState)(null),[ei,eo]=(0,r.useState)(null),[en,ed]=(0,r.useState)(!1),[ec,ep]=(0,r.useState)(!1),[eu,em]=(0,r.useState)({current:0,total:0,phase:"idle",status:"",logs:[],error:"",importedCount:0}),[ex,eh]=(0,r.useState)({customModels:[],modelCompatOverrides:[]}),[ef,eg]=(0,r.useState)(null),ev=T?{id:T.id,name:T.name||("anthropic-compatible"===T.type?ee("anthropicCompatibleName"):ee("openaiCompatibleName")),color:"anthropic-compatible"===T.type?"#D97757":"#10A37F",textIcon:"anthropic-compatible"===T.type?"AC":"OC",apiType:T.apiType,baseUrl:T.baseUrl,type:T.type}:j.FREE_PROVIDERS[p]||j.OAUTH_PROVIDERS[p]||j.APIKEY_PROVIDERS[p],eb=!!j.FREE_PROVIDERS[p]||!!j.OAUTH_PROVIDERS[p],ey=(0,C.getModelsByProviderId)(p),ej=(0,j.getProviderAlias)(p),eC=(0,j.isOpenAICompatibleProvider)(p),eN=(0,j.isAnthropicCompatibleProvider)(p),ek=eC||eN,ew=p.endsWith("-search"),eT=ek?p:ej,eS=ek?T?.prefix||p:ej,eP=(0,r.useCallback)(async()=>{try{let e=await fetch("/api/models/alias"),t=await e.json();e.ok&&Y(t.aliases||{})}catch(e){console.log("Error fetching aliases:",e)}},[]),eR=(0,r.useCallback)(async()=>{if(!ew)try{let e=await fetch(`/api/provider-models?provider=${encodeURIComponent(p)}`,{cache:"no-store"});if(!e.ok)return;let t=await e.json();eh({customModels:t.models||[],modelCompatOverrides:t.modelCompatOverrides||[]})}catch(e){console.error("fetchProviderModelMeta",e)}},[p,ew]),eA=(0,r.useCallback)(async()=>{try{let[e,t]=await Promise.all([fetch("/api/providers",{cache:"no-store"}),fetch("/api/provider-nodes",{cache:"no-store"})]),r=await e.json(),a=await t.json();if(e.ok){let e=(r.connections||[]).filter(e=>e.provider===p);v(e)}if(t.ok){let e=(a.nodes||[]).find(e=>e.id===p)||null;if(!e&&ek)for(let t=0;t<3;t+=1){await new Promise(e=>setTimeout(e,150));let t=await fetch("/api/provider-nodes",{cache:"no-store"});if(t.ok&&(e=((await t.json()).nodes||[]).find(e=>e.id===p)||null))break}R(e)}}catch(e){console.log("Error fetching connections:",e)}finally{w(!1)}},[p,ek]),eI=async e=>{try{let t=await fetch(`/api/provider-nodes/${p}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}),r=await t.json();t.ok&&(R(r.node),await eA(),q(!1))}catch(e){console.log("Error updating provider node:",e)}};(0,r.useEffect)(()=>{eA(),eP(),fetch("/api/settings/proxy").then(e=>e.ok?e.json():null).then(e=>eo(e)).catch(()=>{})},[eA,eP]),(0,r.useEffect)(()=>{b||ew||eR()},[b,ew,eR]),(0,r.useEffect)(()=>{b||0!==u.length||!ev||ek||er.current||ea.current||(er.current=!0,eb?D(!0):_(!0))},[b]);let eE=async(e,t,r=ej)=>{let a=`${r}/${e}`;try{let e=await fetch("/api/models/alias",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:a,alias:t})});if(e.ok)await eP();else{let t=await e.json();alert(t.error||ee("failedSetAlias"))}}catch(e){console.log("Error setting alias:",e)}},eD=async e=>{try{(await fetch(`/api/models/alias?alias=${encodeURIComponent(e)}`,{method:"DELETE"})).ok&&await eP()}catch(e){console.log("Error deleting alias:",e)}},eO=async e=>{if(confirm(ee("deleteConnectionConfirm")))try{(await fetch(`/api/providers/${e}`,{method:"DELETE"})).ok&&v(u.filter(t=>t.id!==e))}catch(e){console.log("Error deleting connection:",e)}},eM=(0,r.useCallback)(()=>{eA(),D(!1)},[eA]),eU=async e=>{try{let t=await fetch("/api/providers",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({provider:p,...e})});if(t.ok)return await eA(),_(!1),null;let r=await t.json().catch(()=>({}));return r.error?.message||r.error||ee("failedSaveConnection")}catch(e){return console.log("Error saving connection:",e),ee("failedSaveConnectionRetry")}},eL=async e=>{try{let t=await fetch(`/api/providers/${B.id}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(t.ok)return await eA(),F(!1),null;let r=await t.json().catch(()=>({}));return r.error?.message||r.error||ee("failedSaveConnection")}catch(e){return console.log("Error updating connection:",e),ee("failedSaveConnectionRetry")}},e$=async(e,t)=>{try{(await fetch(`/api/providers/${e}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({isActive:t})})).ok&&v(r=>r.map(r=>r.id===e?{...r,isActive:t}:r))}catch(e){console.log("Error updating connection status:",e)}},e_=async(e,t)=>{try{(await fetch("/api/rate-limits",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({connectionId:e,enabled:t})})).ok&&v(r=>r.map(r=>r.id===e?{...r,rateLimitProtection:t}:r))}catch(e){console.error("Error toggling rate limit:",e)}},eK=async(e,t,r)=>{try{let a=u.find(t=>t.id===e);if(!a)return;let l=a.providerSpecificData&&"object"==typeof a.providerSpecificData?a.providerSpecificData:{},s=l.codexLimitPolicy&&"object"==typeof l.codexLimitPolicy?l.codexLimitPolicy:{},i={...k(s),[t]:r},o=await fetch(`/api/providers/${e}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({providerSpecificData:{...l,codexLimitPolicy:i}})});if(!o.ok){let e=await o.json().catch(()=>({}));et.error(e.error||"Failed to update Codex limit policy");return}v(t=>t.map(t=>t.id===e?{...t,providerSpecificData:{...t.providerSpecificData||{},codexLimitPolicy:i}}:t)),et.success("Codex limit policy updated")}catch(e){console.error("Error toggling Codex quota policy:",e),et.error("Failed to update Codex limit policy")}},eF=async e=>{if(e&&!W){V(e);try{let t=await fetch(`/api/providers/${e}/test`,{method:"POST"});if(!t.ok){let e=await t.json().catch(()=>({}));alert(e.error||ee("failedRetestConnection"));return}await eA()}catch(e){console.error("Error retesting connection:",e)}finally{V(null)}}},[ez,eq]=(0,r.useState)(null),eB=async e=>{if(!ez){eq(e);try{let t=await fetch(`/api/providers/${e}/refresh`,{method:"POST"}),r=await t.json().catch(()=>({}));t.ok&&r.success?(et.success(ee("tokenRefreshed")),await eA()):et.error(r.error||ee("tokenRefreshFailed"))}catch(e){console.error("Error refreshing token:",e),et.error(ee("tokenRefreshFailed"))}finally{eq(null)}}},eJ=async(e,t)=>{if(e&&t)try{let r=t.priority,a=e.priority;r===a&&(r=u.indexOf(e)>u.indexOf(t)?t.priority-.5:t.priority+.5),await Promise.all([fetch(`/api/providers/${e.id}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({priority:r})}),fetch(`/api/providers/${t.id}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({priority:a})})]),await eA()}catch(e){console.log("Error swapping priority:",e)}},eW=async()=>{if(en)return;let e=u.find(e=>!1!==e.isActive);if(e){ed(!0),ep(!0),em({current:0,total:0,phase:"fetching",status:ee("fetchingModels"),logs:[],error:"",importedCount:0});try{let t=await fetch(`/api/providers/${e.id}/models`),r=await t.json();if(!t.ok)return void em(e=>({...e,phase:"error",status:ee("failedFetchModels"),error:r.error||ee("failedImportModels")}));let a=r.models||[];if(0===a.length)return void em(e=>({...e,phase:"done",status:ee("noModelsFound"),logs:[ee("noModelsReturnedFromEndpoint")]}));em(e=>({...e,phase:"importing",total:a.length,status:ee("importingModelsProgress",{current:0,total:a.length}),logs:[ee("foundModelsStartingImport",{count:a.length})]}));let l=0;for(let e=0;e<a.length;e++){let t=a[e],r=t.id||t.name||t.model;if(!r)continue;let s=r.split("/"),i=s[s.length-1];em(t=>({...t,current:e+1,status:ee("importingModelsProgress",{current:e+1,total:a.length}),logs:[...t.logs,ee("importingModelById",{modelId:r})]})),await fetch("/api/provider-models",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({provider:p,modelId:r,modelName:t.name||r,source:"imported"})}),H[i]||await eE(r,i,eT),l+=1}await eP(),em(e=>({...e,phase:"done",current:a.length,status:l>0?ee("importSuccessCount",{count:l}):ee("noNewModelsAddedExisting"),logs:[...e.logs,l>0?ee("importDoneCount",{count:l}):ee("noNewModelsAdded")],importedCount:l})),l>0&&setTimeout(()=>{window.location.reload()},2e3)}catch(e){console.log("Error importing models:",e),em(t=>({...t,phase:"error",status:ee("importFailed"),error:e instanceof Error?e.message:ee("unexpectedErrorOccurred")}))}finally{ed(!1)}}},eV=async(e,t)=>{ep(!0),em({current:0,total:0,phase:"fetching",status:ee("fetchingModels"),logs:[],error:"",importedCount:0});try{let r=(await e()).models||[];if(0===r.length)return void em(e=>({...e,phase:"done",status:ee("noModelsFound"),logs:[ee("noModelsReturnedFromEndpoint")]}));em(e=>({...e,phase:"importing",total:r.length,status:ee("importingModelsProgress",{current:0,total:r.length}),logs:[ee("foundModelsStartingImport",{count:r.length})]}));let a=0;for(let e=0;e<r.length;e++){let l=r[e],s=l.id||l.name||l.model;s&&(em(t=>({...t,current:e+1,status:ee("importingModelsProgress",{current:e+1,total:r.length}),logs:[...t.logs,ee("importingModelById",{modelId:s})]})),await t(l)&&(a+=1))}em(e=>({...e,phase:"done",current:r.length,status:a>0?ee("importSuccessCount",{count:a}):ee("noNewModelsAdded"),logs:[...e.logs,a>0?ee("importDoneCount",{count:a}):ee("noNewModelsAdded")],importedCount:a})),a>0&&setTimeout(()=>{window.location.reload()},2e3)}catch(e){console.log("Error importing models:",e),em(t=>({...t,phase:"error",status:ee("importFailed"),error:e instanceof Error?e.message:ee("unexpectedErrorOccurred")}))}},eH=u.some(e=>!1!==e.isActive),eY=e=>{let t=ex.customModels.find(t=>t.id===e);if(t)return!!t.normalizeToolCallId;let r=ex.modelCompatOverrides.find(t=>t.id===e);return!!r?.normalizeToolCallId},eG=e=>{let t=ex.customModels.find(t=>t.id===e);if(t&&Object.prototype.hasOwnProperty.call(t,"preserveOpenAIDeveloperRole"))return!!t.preserveOpenAIDeveloperRole;let r=ex.modelCompatOverrides.find(t=>t.id===e);return!(r&&Object.prototype.hasOwnProperty.call(r,"preserveOpenAIDeveloperRole"))||!!r.preserveOpenAIDeveloperRole},eQ=async(e,t)=>{eg(e);try{let r,a=ex.customModels.find(t=>t.id===e);r=a?{provider:p,modelId:e,modelName:a.name||e,source:a.source||"manual",apiFormat:a.apiFormat||"chat-completions",supportedEndpoints:Array.isArray(a.supportedEndpoints)&&a.supportedEndpoints.length?a.supportedEndpoints:["chat"],normalizeToolCallId:void 0!==t.normalizeToolCallId?t.normalizeToolCallId:!!a.normalizeToolCallId,preserveOpenAIDeveloperRole:void 0!==t.preserveOpenAIDeveloperRole?t.preserveOpenAIDeveloperRole:!Object.prototype.hasOwnProperty.call(a,"preserveOpenAIDeveloperRole")||!!a.preserveOpenAIDeveloperRole}:{provider:p,modelId:e,...t},(await fetch("/api/provider-models",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(r)})).ok?await eR():et.error(ee("failedSaveCustomModel"))}catch{et.error(ee("failedSaveCustomModel"))}finally{eg(null)}};return b?(0,t.jsxs)("div",{className:"flex flex-col gap-8",children:[(0,t.jsx)(x.CardSkeleton,{}),(0,t.jsx)(x.CardSkeleton,{})]}):ev?(0,t.jsxs)("div",{className:"flex flex-col gap-8",children:[(0,t.jsxs)("div",{children:[(0,t.jsxs)(i.default,{href:"/dashboard/providers",className:"inline-flex items-center gap-1 text-sm text-text-muted hover:text-primary transition-colors mb-4",children:[(0,t.jsx)("span",{className:"material-symbols-outlined text-lg",children:"arrow_back"}),ee("backToProviders")]}),(0,t.jsxs)("div",{className:"flex items-center gap-4",children:[(0,t.jsx)("div",{className:"rounded-lg flex items-center justify-center",style:{backgroundColor:`${ev.color}15`},children:G?(0,t.jsx)("span",{className:"text-sm font-bold",style:{color:ev.color},children:ev.textIcon||ev.id.slice(0,2).toUpperCase()}):(0,t.jsx)(o.default,{src:eC&&ev.apiType?"responses"===ev.apiType?"/providers/oai-r.png":"/providers/oai-cc.png":eN?"/providers/anthropic-m.png":`/providers/${ev.id}.png`,alt:ev.name,width:48,height:48,className:"object-contain rounded-lg max-w-[48px] max-h-[48px]",sizes:"48px",onError:()=>Q(!0)})}),(0,t.jsxs)("div",{children:[ev.website?(0,t.jsxs)("a",{href:ev.website,target:"_blank",rel:"noopener noreferrer",className:"text-3xl font-semibold tracking-tight hover:underline inline-flex items-center gap-2",style:{color:ev.color},children:[ev.name,(0,t.jsx)("span",{className:"material-symbols-outlined text-lg opacity-60",children:"open_in_new"})]}):(0,t.jsx)("h1",{className:"text-3xl font-semibold tracking-tight",children:ev.name}),(0,t.jsx)("p",{className:"text-text-muted",children:ee("connectionCountLabel",{count:u.length})})]})]})]}),ek&&T&&(0,t.jsxs)(d.Card,{children:[(0,t.jsxs)("div",{className:"flex items-center justify-between mb-4",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("h2",{className:"text-lg font-semibold",children:eN?ee("anthropicCompatibleDetails"):ee("openaiCompatibleDetails")}),(0,t.jsxs)("p",{className:"text-sm text-text-muted",children:[eN?ee("messagesApi"):"responses"===T.apiType?ee("responsesApi"):ee("chatCompletions")," ","· ",(T.baseUrl||"").replace(/\/$/,""),"/",eN?ee("messagesPath"):"responses"===T.apiType?ee("responsesPath"):ee("chatCompletionsPath")]})]}),(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(c.Button,{size:"sm",icon:"add",onClick:()=>_(!0),disabled:u.length>0,children:ee("add")}),(0,t.jsx)(c.Button,{size:"sm",variant:"secondary",icon:"edit",onClick:()=>q(!0),children:ee("edit")}),(0,t.jsx)(c.Button,{size:"sm",variant:"secondary",icon:"delete",onClick:async()=>{if(confirm(ee("deleteCompatibleNodeConfirm",{type:eN?ee("anthropic"):ee("openai")})))try{(await fetch(`/api/provider-nodes/${p}`,{method:"DELETE"})).ok&&l.push("/dashboard/providers")}catch(e){console.log("Error deleting provider node:",e)}},children:ee("delete")})]})]}),u.length>0&&(0,t.jsx)("p",{className:"text-sm text-text-muted",children:ee("singleConnectionPerCompatible")})]}),(0,t.jsxs)(d.Card,{children:[(0,t.jsxs)("div",{className:"flex items-center justify-between mb-4",children:[(0,t.jsxs)("div",{className:"flex items-center gap-3",children:[(0,t.jsx)("h2",{className:"text-lg font-semibold",children:ee("connections")}),(0,t.jsxs)("button",{onClick:()=>es({level:"provider",id:p,label:ev?.name||p}),className:`inline-flex items-center gap-1 px-2 py-1 rounded text-xs font-medium transition-all ${ei?.providers?.[p]?"bg-amber-500/15 text-amber-500 hover:bg-amber-500/25":"bg-black/[0.03] dark:bg-white/[0.03] text-text-muted/50 hover:text-text-muted hover:bg-black/[0.06] dark:hover:bg-white/[0.06]"}`,title:ei?.providers?.[p]?ee("providerProxyTitleConfigured",{host:ei.providers[p].host||ee("configured")}):ee("providerProxyConfigureHint"),children:[(0,t.jsx)("span",{className:"material-symbols-outlined text-[14px]",children:"vpn_lock"}),ei?.providers?.[p]&&ei.providers[p].host||ee("providerProxy")]})]}),ek?0===u.length&&(0,t.jsx)(c.Button,{size:"sm",icon:"add",onClick:()=>_(!0),children:ee("add")}):(0,t.jsx)(c.Button,{size:"sm",icon:"add",onClick:()=>eb?D(!0):_(!0),children:ee("add")})]}),0===u.length?(0,t.jsxs)("div",{className:"text-center py-12",children:[(0,t.jsx)("div",{className:"inline-flex items-center justify-center w-16 h-16 rounded-full bg-primary/10 text-primary mb-4",children:(0,t.jsx)("span",{className:"material-symbols-outlined text-[32px]",children:eb?"lock":"key"})}),(0,t.jsx)("p",{className:"text-text-main font-medium mb-1",children:ee("noConnectionsYet")}),(0,t.jsx)("p",{className:"text-sm text-text-muted mb-4",children:ee("addFirstConnectionHint")}),!ek&&(0,t.jsx)(c.Button,{icon:"add",onClick:()=>eb?D(!0):_(!0),children:ee("addConnection")})]}):(0,t.jsx)("div",{className:"flex flex-col divide-y divide-black/[0.03] dark:divide-white/[0.03]",children:u.sort((e,t)=>(e.priority||0)-(t.priority||0)).map((e,r)=>(0,t.jsx)(O,{connection:e,isOAuth:eb,isFirst:0===r,isLast:r===u.length-1,onMoveUp:()=>eJ(e,u[r-1]),onMoveDown:()=>eJ(e,u[r+1]),onToggleActive:t=>e$(e.id,t),onToggleRateLimit:t=>e_(e.id,t),isCodex:"codex"===p,onToggleCodex5h:t=>eK(e.id,"use5h",t),onToggleCodexWeekly:t=>eK(e.id,"useWeekly",t),onRetest:()=>eF(e.id),isRetesting:W===e.id,onEdit:()=>{J(e),F(!0)},onDelete:()=>eO(e.id),onReauth:eb?()=>D(!0):void 0,onRefreshToken:eb?()=>eB(e.id):void 0,isRefreshing:ez===e.id,onProxy:()=>es({level:"key",id:e.id,label:e.name||e.email||e.id}),hasProxy:!!(ei?.keys?.[e.id]||ei?.providers?.[p]||ei?.global),proxySource:ei?.keys?.[e.id]?"key":ei?.providers?.[p]?"provider":ei?.global?"global":null,proxyHost:(ei?.keys?.[e.id]||ei?.providers?.[p]||ei?.global)?.host||null},e.id))})]}),!ew&&(0,t.jsxs)(d.Card,{children:[(0,t.jsx)("h2",{className:"text-lg font-semibold mb-4",children:ee("availableModels")}),(()=>{if(ek)return(0,t.jsx)(I,{providerStorageAlias:eT,providerDisplayAlias:eS,modelAliases:H,copied:X,onCopy:Z,onSetAlias:eE,onDeleteAlias:eD,connections:u,isAnthropic:eN,onImportWithProgress:eV,t:ee,effectiveModelNormalize:eY,effectiveModelPreserveDeveloper:eG,saveModelCompatFlags:eQ,compatSavingModelId:ef,onModelsChanged:eR});if(ev.passthroughModels)return(0,t.jsxs)("div",{children:[(0,t.jsxs)("div",{className:"flex items-center gap-2 mb-4",children:[(0,t.jsx)(c.Button,{size:"sm",variant:"secondary",icon:"download",onClick:eW,disabled:!eH||en,children:en?ee("importingModels"):ee("importFromModels")}),!eH&&(0,t.jsx)("span",{className:"text-xs text-text-muted",children:ee("addConnectionToImport")})]}),(0,t.jsx)(P,{providerAlias:ej,modelAliases:H,copied:X,onCopy:Z,onSetAlias:eE,onDeleteAlias:eD,t:ee,effectiveModelNormalize:eY,effectiveModelPreserveDeveloper:eG,saveModelCompatFlags:eQ,compatSavingModelId:ef})]});let e=(0,t.jsxs)("div",{className:"flex items-center gap-2 mb-4",children:[(0,t.jsx)(c.Button,{size:"sm",variant:"secondary",icon:"download",onClick:eW,disabled:!eH||en,children:en?ee("importingModels"):ee("importFromModels")}),!eH&&(0,t.jsx)("span",{className:"text-xs text-text-muted",children:ee("addConnectionToImport")})]});return 0===ey.length?(0,t.jsxs)("div",{children:[e,(0,t.jsx)("p",{className:"text-sm text-text-muted",children:ee("noModelsConfigured")})]}):(0,t.jsxs)("div",{children:[e,(0,t.jsx)("div",{className:"flex flex-wrap gap-3",children:ey.map(e=>{let r=`${eT}/${e.id}`,a=`${p}/${e.id}`,l=Object.entries(H).find(([,e])=>e===r||e===a)?.[0];return(0,t.jsx)(S,{model:e,fullModel:`${eS}/${e.id}`,alias:l,copied:X,onCopy:Z,t:ee,showDeveloperToggle:!0,normalizeToolCallId:eY(e.id),preserveDeveloperRole:eG(e.id),onNormalizeChange:t=>eQ(e.id,{normalizeToolCallId:t}),onPreserveChange:t=>eQ(e.id,{preserveOpenAIDeveloperRole:t}),compatDisabled:ef===e.id},e.id)})})]})})(),!ek&&(0,t.jsx)(A,{providerId:p,providerAlias:eS,copied:X,onCopy:Z,onModelsChanged:eR})]}),ew&&(0,t.jsxs)(d.Card,{children:[(0,t.jsx)("h2",{className:"text-lg font-semibold mb-4",children:ee("searchProvider")||"Search Provider"}),(0,t.jsx)("p",{className:"text-sm text-text-muted",children:ee("searchProviderDesc")||"This provider is used for web search via POST /v1/search. No model configuration needed — search providers are ready to use once an API key is connected."}),"perplexity-search"===p&&(0,t.jsxs)("div",{className:"mt-3 flex items-center gap-2 px-3 py-2 rounded-lg bg-blue-500/10 border border-blue-500/20",children:[(0,t.jsx)("span",{className:"material-symbols-outlined text-sm text-blue-400",children:"link"}),(0,t.jsxs)("p",{className:"text-xs text-blue-300",children:["Uses the same API key as ",(0,t.jsx)("strong",{children:"Perplexity"})," (chat provider). If you already have Perplexity configured, no additional setup is needed."]})]})]}),"kiro"===p?(0,t.jsx)(f.default,{isOpen:E,providerInfo:ev,onSuccess:eM,onClose:()=>{ea.current=!0,D(!1)}}):"cursor"===p?(0,t.jsx)(g.default,{isOpen:E,onSuccess:eM,onClose:()=>{ea.current=!0,D(!1)}}):(0,t.jsx)(h.default,{isOpen:E,provider:p,providerInfo:ev,onSuccess:eM,onClose:()=>{ea.current=!0,D(!1)}}),(0,t.jsx)(M,{isOpen:U,provider:p,providerName:ev.name,isCompatible:ek,isAnthropic:eN,onSave:eU,onClose:()=>_(!1)}),(0,t.jsx)(L,{isOpen:K,connection:B,onSave:eL,onClose:()=>F(!1)}),ek&&(0,t.jsx)($,{isOpen:z,node:T,onSave:eI,onClose:()=>q(!1),isAnthropic:eN}),el&&(0,t.jsx)(y.ProxyConfigModal,{isOpen:!!el,onClose:()=>es(null),level:el.level,levelId:el.id,levelLabel:el.label}),(0,t.jsx)(m.Modal,{isOpen:ec,onClose:()=>{("done"===eu.phase||"error"===eu.phase)&&ep(!1)},title:ee("importingModelsTitle"),size:"md",closeOnOverlay:!1,showCloseButton:"done"===eu.phase||"error"===eu.phase,children:(0,t.jsxs)("div",{className:"flex flex-col gap-4",children:[(0,t.jsxs)("div",{className:"flex items-center gap-3",children:["fetching"===eu.phase&&(0,t.jsx)("span",{className:"material-symbols-outlined text-primary animate-spin",children:"progress_activity"}),"importing"===eu.phase&&(0,t.jsx)("span",{className:"material-symbols-outlined text-primary animate-spin",children:"progress_activity"}),"done"===eu.phase&&(0,t.jsx)("span",{className:"material-symbols-outlined text-green-500",children:"check_circle"}),"error"===eu.phase&&(0,t.jsx)("span",{className:"material-symbols-outlined text-red-500",children:"error"}),(0,t.jsx)("span",{className:"text-sm font-medium text-text-main",children:eu.status})]}),("importing"===eu.phase||"done"===eu.phase)&&eu.total>0&&(0,t.jsxs)("div",{className:"w-full",children:[(0,t.jsxs)("div",{className:"flex items-center justify-between mb-1",children:[(0,t.jsxs)("span",{className:"text-xs text-text-muted",children:[eu.current," / ",eu.total]}),(0,t.jsxs)("span",{className:"text-xs text-text-muted",children:[Math.round(eu.current/eu.total*100),"%"]})]}),(0,t.jsx)("div",{className:"w-full h-2.5 bg-black/10 dark:bg-white/10 rounded-full overflow-hidden",children:(0,t.jsx)("div",{className:"h-full rounded-full transition-all duration-300 ease-out",style:{width:`${eu.current/eu.total*100}%`,background:"done"===eu.phase?"linear-gradient(90deg, #22c55e, #16a34a)":"linear-gradient(90deg, var(--color-primary), var(--color-primary-hover, var(--color-primary)))"}})})]}),"fetching"===eu.phase&&(0,t.jsx)("div",{className:"w-full h-2.5 bg-black/10 dark:bg-white/10 rounded-full overflow-hidden",children:(0,t.jsx)("div",{className:"h-full rounded-full animate-pulse",style:{width:"60%",background:"linear-gradient(90deg, var(--color-primary), var(--color-primary-hover, var(--color-primary)))"}})}),"error"===eu.phase&&eu.error&&(0,t.jsx)("div",{className:"p-3 rounded-lg bg-red-500/10 border border-red-500/20",children:(0,t.jsx)("p",{className:"text-sm text-red-400",children:eu.error})}),eu.logs.length>0&&(0,t.jsx)("div",{className:"max-h-48 overflow-y-auto rounded-lg bg-black/5 dark:bg-white/5 p-3 border border-black/5 dark:border-white/5",children:(0,t.jsx)("div",{className:"flex flex-col gap-1",children:eu.logs.map((e,r)=>(0,t.jsx)("p",{className:`text-xs font-mono ${e.startsWith("✓")?"text-green-500 font-semibold":"text-text-muted"}`,children:e},r))})}),"done"===eu.phase&&eu.importedCount>0&&(0,t.jsx)("p",{className:"text-xs text-text-muted text-center animate-pulse",children:ee("pageAutoRefresh")})]})})]}):(0,t.jsxs)("div",{className:"text-center py-20",children:[(0,t.jsx)("p",{className:"text-text-muted",children:ee("providerNotFound")}),(0,t.jsx)(i.default,{href:"/dashboard/providers",className:"text-primary mt-4 inline-block",children:ee("backToProviders")})]})}function S({model:e,fullModel:r,alias:a,copied:l,onCopy:s,t:i,showDeveloperToggle:o=!0,normalizeToolCallId:n,preserveDeveloperRole:d,onNormalizeChange:c,onPreserveChange:p,compatDisabled:u}){return(0,t.jsxs)("div",{className:"flex flex-col px-3 py-2 rounded-lg border border-border hover:bg-sidebar/50 min-w-[220px] max-w-md",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2 flex-wrap",children:[(0,t.jsx)("span",{className:"material-symbols-outlined text-base text-text-muted shrink-0",children:"smart_toy"}),(0,t.jsx)("code",{className:"text-xs text-text-muted font-mono bg-sidebar px-1.5 py-0.5 rounded",children:r}),(0,t.jsx)("button",{onClick:()=>s(r,`model-${e.id}`),className:"p-0.5 hover:bg-sidebar rounded text-text-muted hover:text-primary",title:i("copyModel"),children:(0,t.jsx)("span",{className:"material-symbols-outlined text-sm",children:l===`model-${e.id}`?"check":"content_copy"})})]}),(0,t.jsx)(w,{t:i,normalizeToolCallId:!!n,preserveDeveloperRole:d,showDeveloperToggle:o,onNormalizeChange:c,onPreserveChange:p,disabled:u})]})}function P({providerAlias:e,modelAliases:a,copied:l,onCopy:s,onSetAlias:i,onDeleteAlias:o,t:n,effectiveModelNormalize:d,effectiveModelPreserveDeveloper:p,saveModelCompatFlags:u,compatSavingModelId:m}){let[x,h]=(0,r.useState)(""),[f,g]=(0,r.useState)(!1),v=Object.entries(a).filter(([,t])=>t.startsWith(`${e}/`)).map(([t,r])=>({modelId:r.replace(`${e}/`,""),fullModel:r,alias:t})),b=async()=>{let e;if(!x.trim()||f)return;let t=x.trim(),r=(e=t.split("/"))[e.length-1];if(a[r])return void alert(n("aliasExistsAlert",{alias:r}));g(!0);try{await i(t,r),h("")}catch(e){console.log("Error adding model:",e)}finally{g(!1)}};return(0,t.jsxs)("div",{className:"flex flex-col gap-4",children:[(0,t.jsx)("p",{className:"text-sm text-text-muted",children:n("openRouterAnyModelHint")}),(0,t.jsxs)("div",{className:"flex items-end gap-2",children:[(0,t.jsxs)("div",{className:"flex-1",children:[(0,t.jsx)("label",{htmlFor:"new-model-input",className:"text-xs text-text-muted mb-1 block",children:n("modelIdFromOpenRouter")}),(0,t.jsx)("input",{id:"new-model-input",type:"text",value:x,onChange:e=>h(e.target.value),onKeyDown:e=>"Enter"===e.key&&b(),placeholder:n("openRouterModelPlaceholder"),className:"w-full px-3 py-2 text-sm border border-border rounded-lg bg-background focus:outline-none focus:border-primary"})]}),(0,t.jsx)(c.Button,{size:"sm",icon:"add",onClick:b,disabled:!x.trim()||f,children:f?n("adding"):n("add")})]}),v.length>0&&(0,t.jsx)("div",{className:"flex flex-col gap-3",children:v.map(({modelId:e,fullModel:r,alias:a})=>(0,t.jsx)(R,{modelId:e,fullModel:r,copied:l,onCopy:s,onDeleteAlias:()=>o(a),t:n,showDeveloperToggle:!0,normalizeToolCallId:d(e),preserveDeveloperRole:p(e),onNormalizeChange:t=>u(e,{normalizeToolCallId:t}),onPreserveChange:t=>u(e,{preserveOpenAIDeveloperRole:t}),compatDisabled:m===e},r))})]})}function R({modelId:e,fullModel:r,copied:a,onCopy:l,onDeleteAlias:s,t:i,showDeveloperToggle:o=!0,normalizeToolCallId:n,preserveDeveloperRole:d,onNormalizeChange:c,onPreserveChange:p,compatDisabled:u}){return(0,t.jsxs)("div",{className:"flex flex-col gap-0 p-3 rounded-lg border border-border hover:bg-sidebar/50",children:[(0,t.jsxs)("div",{className:"flex items-start gap-3",children:[(0,t.jsx)("span",{className:"material-symbols-outlined text-base text-text-muted shrink-0",children:"smart_toy"}),(0,t.jsxs)("div",{className:"flex-1 min-w-0",children:[(0,t.jsx)("p",{className:"text-sm font-medium truncate",children:e}),(0,t.jsxs)("div",{className:"flex items-center gap-1 mt-1 flex-wrap",children:[(0,t.jsx)("code",{className:"text-xs text-text-muted font-mono bg-sidebar px-1.5 py-0.5 rounded",children:r}),(0,t.jsx)("button",{onClick:()=>l(r,`model-${e}`),className:"p-0.5 hover:bg-sidebar rounded text-text-muted hover:text-primary",title:i("copyModel"),children:(0,t.jsx)("span",{className:"material-symbols-outlined text-sm",children:a===`model-${e}`?"check":"content_copy"})})]})]}),(0,t.jsx)("button",{onClick:s,className:"p-1 hover:bg-red-50 rounded text-red-500 shrink-0",title:i("removeModel"),children:(0,t.jsx)("span",{className:"material-symbols-outlined text-sm",children:"delete"})})]}),(0,t.jsx)("div",{className:"pl-9",children:(0,t.jsx)(w,{t:i,normalizeToolCallId:!!n,preserveDeveloperRole:d,showDeveloperToggle:o,onNormalizeChange:c,onPreserveChange:p,disabled:u})})]})}function A({providerId:e,providerAlias:l,copied:s,onCopy:i,onModelsChanged:o}){let d=(0,n.useTranslations)("providers"),p=(0,a.useNotificationStore)(),[u,m]=(0,r.useState)([]),[x,h]=(0,r.useState)(""),[f,g]=(0,r.useState)(""),[v,b]=(0,r.useState)("chat-completions"),[y,j]=(0,r.useState)(["chat"]),[C,N]=(0,r.useState)(!1),[k,T]=(0,r.useState)(!0),[S,P]=(0,r.useState)(null),[R,A]=(0,r.useState)("chat-completions"),[I,E]=(0,r.useState)(["chat"]),[D,O]=(0,r.useState)(!1),[M,U]=(0,r.useState)(!1),[L,$]=(0,r.useState)(null),_=(0,r.useCallback)(async()=>{try{let t=await fetch(`/api/provider-models?provider=${encodeURIComponent(e)}`);if(t.ok){let e=await t.json();m(e.models||[])}}catch(e){console.error("Failed to fetch custom models:",e)}finally{T(!1)}},[e]);(0,r.useEffect)(()=>{_()},[_]);let K=async()=>{if(x.trim()&&!C){N(!0);try{(await fetch("/api/provider-models",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({provider:e,modelId:x.trim(),modelName:f.trim()||void 0,apiFormat:v,supportedEndpoints:y})})).ok&&(h(""),g(""),b("chat-completions"),j(["chat"]),await _(),o?.())}catch(e){console.error("Failed to add custom model:",e)}finally{N(!1)}}},F=async t=>{try{await fetch(`/api/provider-models?provider=${encodeURIComponent(e)}&model=${encodeURIComponent(t)}`,{method:"DELETE"}),await _(),o?.()}catch(e){console.error("Failed to remove custom model:",e)}},z=()=>{P(null),A("chat-completions"),E(["chat"]),O(!1),U(!0),$(null)},q=async t=>{if(S&&S===t){if(!I.length)return void p.error("Select at least one supported endpoint");$(t);try{let r=u.find(e=>e.id===t);if(!(await fetch("/api/provider-models",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({provider:e,modelId:t,modelName:r?.name||t,source:r?.source||"manual",apiFormat:R,supportedEndpoints:I,normalizeToolCallId:D,preserveOpenAIDeveloperRole:M})})).ok)throw Error("Failed to save model endpoint settings");await _(),o?.(),p.success("Saved model endpoint settings"),z()}catch(e){console.error("Failed to save custom model:",e),p.error("Failed to save model endpoint settings")}finally{$(null)}}};return(0,t.jsxs)("div",{className:"mt-6 pt-6 border-t border-border",children:[(0,t.jsxs)("h3",{className:"text-sm font-semibold mb-3 flex items-center gap-2",children:[(0,t.jsx)("span",{className:"material-symbols-outlined text-base text-primary",children:"tune"}),d("customModels")]}),(0,t.jsx)("p",{className:"text-xs text-text-muted mb-3",children:d("customModelsHint")}),(0,t.jsxs)("div",{className:"flex flex-col gap-3 mb-3",children:[(0,t.jsxs)("div",{className:"flex items-end gap-2",children:[(0,t.jsxs)("div",{className:"flex-1",children:[(0,t.jsx)("label",{htmlFor:"custom-model-id",className:"text-xs text-text-muted mb-1 block",children:d("modelId")}),(0,t.jsx)("input",{id:"custom-model-id",type:"text",value:x,onChange:e=>h(e.target.value),onKeyDown:e=>"Enter"===e.key&&K(),placeholder:d("customModelPlaceholder"),className:"w-full px-3 py-2 text-sm border border-border rounded-lg bg-background focus:outline-none focus:border-primary"})]}),(0,t.jsxs)("div",{className:"w-40",children:[(0,t.jsx)("label",{htmlFor:"custom-model-name",className:"text-xs text-text-muted mb-1 block",children:d("displayName")}),(0,t.jsx)("input",{id:"custom-model-name",type:"text",value:f,onChange:e=>g(e.target.value),onKeyDown:e=>"Enter"===e.key&&K(),placeholder:d("optional"),className:"w-full px-3 py-2 text-sm border border-border rounded-lg bg-background focus:outline-none focus:border-primary"})]}),(0,t.jsx)(c.Button,{size:"sm",icon:"add",onClick:K,disabled:!x.trim()||C,children:C?d("adding"):d("add")})]}),(0,t.jsxs)("div",{className:"flex items-end gap-4 flex-wrap",children:[(0,t.jsxs)("div",{className:"w-48",children:[(0,t.jsx)("label",{htmlFor:"custom-api-format",className:"text-xs text-text-muted mb-1 block",children:"API Format"}),(0,t.jsxs)("select",{id:"custom-api-format",value:v,onChange:e=>b(e.target.value),className:"w-full px-3 py-2 text-sm border border-border rounded-lg bg-background focus:outline-none focus:border-primary",children:[(0,t.jsx)("option",{value:"chat-completions",children:"Chat Completions"}),(0,t.jsx)("option",{value:"responses",children:"Responses API"})]})]}),(0,t.jsxs)("div",{className:"flex-1",children:[(0,t.jsx)("span",{className:"text-xs text-text-muted mb-1 block",children:"Supported Endpoints"}),(0,t.jsx)("div",{className:"flex items-center gap-3",children:["chat","embeddings","images","audio"].map(e=>(0,t.jsxs)("label",{className:"flex items-center gap-1.5 text-xs text-text-main cursor-pointer",children:[(0,t.jsx)("input",{type:"checkbox",checked:y.includes(e),onChange:t=>{t.target.checked?j(t=>[...t,e]):j(t=>t.filter(t=>t!==e))},className:"rounded border-border"}),"chat"===e?"💬 Chat":"embeddings"===e?"📐 Embeddings":"images"===e?"🖼️ Images":"🔊 Audio"]},e))})]})]})]}),k?(0,t.jsx)("p",{className:"text-xs text-text-muted",children:d("loading")}):u.length>0?(0,t.jsx)("div",{className:"flex flex-col gap-2",children:u.map(e=>{let r=`${l}/${e.id}`,a=`custom-${e.id}`;return(0,t.jsxs)("div",{className:"flex items-center gap-3 p-3 rounded-lg border border-border hover:bg-sidebar/50",children:[(0,t.jsx)("span",{className:"material-symbols-outlined text-base text-primary",children:"tune"}),(0,t.jsxs)("div",{className:"flex-1 min-w-0",children:[(0,t.jsx)("p",{className:"text-sm font-medium truncate",children:e.name||e.id}),(0,t.jsxs)("div",{className:"flex items-center gap-1 mt-1 flex-wrap",children:[(0,t.jsx)("code",{className:"text-xs text-text-muted font-mono bg-sidebar px-1.5 py-0.5 rounded",children:r}),(0,t.jsx)("button",{onClick:()=>i(r,a),className:"p-0.5 hover:bg-sidebar rounded text-text-muted hover:text-primary",title:d("copyModel"),children:(0,t.jsx)("span",{className:"material-symbols-outlined text-sm",children:s===a?"check":"content_copy"})}),"responses"===e.apiFormat&&(0,t.jsx)("span",{className:"text-[10px] px-1.5 py-0.5 rounded-full bg-blue-500/15 text-blue-400 font-medium",children:"Responses"}),e.supportedEndpoints?.includes("embeddings")&&(0,t.jsx)("span",{className:"text-[10px] px-1.5 py-0.5 rounded-full bg-purple-500/15 text-purple-400 font-medium",children:"📐 Embed"}),e.supportedEndpoints?.includes("images")&&(0,t.jsx)("span",{className:"text-[10px] px-1.5 py-0.5 rounded-full bg-amber-500/15 text-amber-400 font-medium",children:"🖼️ Images"}),e.supportedEndpoints?.includes("audio")&&(0,t.jsx)("span",{className:"text-[10px] px-1.5 py-0.5 rounded-full bg-green-500/15 text-green-400 font-medium",children:"🔊 Audio"}),e.normalizeToolCallId&&(0,t.jsx)("span",{className:"text-[10px] px-1.5 py-0.5 rounded-full bg-slate-500/15 text-slate-400 font-medium",title:d("normalizeToolCallIdLabel"),children:"ID×9"}),!1===e.preserveOpenAIDeveloperRole&&(0,t.jsx)("span",{className:"text-[10px] px-1.5 py-0.5 rounded-full bg-cyan-500/15 text-cyan-400 font-medium",title:d("compatDoNotPreserveDeveloper"),children:d("compatBadgeNoPreserve")})]}),S===e.id&&(0,t.jsxs)("div",{className:"mt-3 p-3 rounded-lg border border-border bg-sidebar/40",children:[(0,t.jsxs)("div",{className:"flex items-end gap-3 flex-wrap",children:[(0,t.jsxs)("div",{className:"w-44",children:[(0,t.jsx)("label",{className:"text-xs text-text-muted mb-1 block",children:"API Format"}),(0,t.jsxs)("select",{value:R,onChange:e=>A(e.target.value),className:"w-full px-2.5 py-2 text-xs border border-border rounded-lg bg-background focus:outline-none focus:border-primary",children:[(0,t.jsx)("option",{value:"chat-completions",children:"Chat Completions"}),(0,t.jsx)("option",{value:"responses",children:"Responses API"})]})]}),(0,t.jsxs)("div",{className:"flex-1 min-w-[240px]",children:[(0,t.jsx)("span",{className:"text-xs text-text-muted mb-1 block",children:"Supported Endpoints"}),(0,t.jsx)("div",{className:"flex items-center gap-3 flex-wrap",children:["chat","embeddings","images","audio"].map(e=>(0,t.jsxs)("label",{className:"flex items-center gap-1.5 text-xs text-text-main cursor-pointer",children:[(0,t.jsx)("input",{type:"checkbox",checked:I.includes(e),onChange:t=>{t.target.checked?E(t=>t.includes(e)?t:[...t,e]):E(t=>t.filter(t=>t!==e))},className:"rounded border-border"}),"chat"===e?"💬 Chat":"embeddings"===e?"📐 Embeddings":"images"===e?"🖼️ Images":"🔊 Audio"]},e))})]})]}),(0,t.jsx)("div",{className:"mt-3 pt-3 border-t border-border/80 w-full",children:(0,t.jsx)(w,{t:d,normalizeToolCallId:D,preserveDeveloperRole:M,showDeveloperToggle:!0,onNormalizeChange:O,onPreserveChange:U,disabled:L===e.id})}),(0,t.jsxs)("div",{className:"mt-3 flex items-center gap-2",children:[(0,t.jsx)(c.Button,{size:"sm",onClick:()=>q(e.id),disabled:L===e.id,children:L===e.id?d("saving"):d("save")}),(0,t.jsx)(c.Button,{size:"sm",variant:"ghost",onClick:z,children:d("cancel")})]})]})]}),(0,t.jsxs)("div",{className:"flex items-center gap-1",children:[(0,t.jsx)("button",{onClick:()=>{P(e.id),A(e.apiFormat||"chat-completions"),E(Array.isArray(e.supportedEndpoints)&&e.supportedEndpoints.length?e.supportedEndpoints:["chat"]),O(!!e.normalizeToolCallId),U(!Object.prototype.hasOwnProperty.call(e,"preserveOpenAIDeveloperRole")||!!e.preserveOpenAIDeveloperRole)},className:"p-1 hover:bg-sidebar rounded text-text-muted hover:text-primary",title:d("edit"),children:(0,t.jsx)("span",{className:"material-symbols-outlined text-sm",children:"edit"})}),(0,t.jsx)("button",{onClick:()=>F(e.id),className:"p-1 hover:bg-red-50 rounded text-red-500",title:d("removeCustomModel"),children:(0,t.jsx)("span",{className:"material-symbols-outlined text-sm",children:"delete"})})]})]},e.id)})}):(0,t.jsx)("p",{className:"text-xs text-text-muted",children:d("noCustomModels")})]})}function I({providerStorageAlias:e,providerDisplayAlias:l,modelAliases:s,copied:i,onCopy:o,onSetAlias:n,onDeleteAlias:d,connections:p,isAnthropic:u,onImportWithProgress:m,t:x,effectiveModelNormalize:h,effectiveModelPreserveDeveloper:f,saveModelCompatFlags:g,compatSavingModelId:v,onModelsChanged:b}){let[y,j]=(0,r.useState)(""),[C,N]=(0,r.useState)(!1),[k,w]=(0,r.useState)(!1),T=(0,a.useNotificationStore)(),S=Object.entries(s).filter(([,t])=>t.startsWith(`${e}/`)).map(([t,r])=>({modelId:r.replace(`${e}/`,""),fullModel:r,alias:t})),P=e=>{let t,r=(t=e.split("/"))[t.length-1];if(!s[r])return r;let a=`${l}-${r}`;return s[a]?null:a},A=async()=>{if(!y.trim()||C)return;let t=y.trim(),r=P(t);if(!r)return void T.error(x("allSuggestedAliasesExist"));N(!0);try{let a=await fetch("/api/provider-models",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({provider:e,modelId:t,modelName:t,source:"manual"})});if(!a.ok){let e={};try{e=await a.json()}catch(e){console.error("Failed to parse error response from custom model API:",e)}throw Error(e.error?.message||x("failedSaveCustomModel"))}await n(t,r,e),j(""),T.success(x("modelAddedSuccess",{modelId:t})),b?.()}catch(e){console.error("Error adding model:",e),T.error(e instanceof Error?e.message:x("failedAddModelTryAgain"))}finally{N(!1)}},I=async()=>{if(k)return;let t=p.find(e=>!1!==e.isActive);if(t){w(!0);try{await m(async()=>{let e=await fetch(`/api/providers/${t.id}/models`),r=await e.json();if(!e.ok)throw Error(r.error||x("failedImportModels"));return r},async t=>{let r=t.id||t.name||t.model;if(!r)return!1;let a=P(r);return!!a&&((await fetch("/api/provider-models",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({provider:e,modelId:r,modelName:t.name||r,source:"imported"})})).ok?(await n(r,a,e),!0):(T.error(x("failedSaveImportedModel")),!1))})}catch(e){console.error("Error importing models:",e),T.error(x("failedImportModelsTryAgain"))}finally{w(!1)}}},E=p.some(e=>!1!==e.isActive),D=async(t,r)=>{try{if(!(await fetch(`/api/provider-models?provider=${encodeURIComponent(e)}&model=${encodeURIComponent(t)}`,{method:"DELETE"})).ok)throw Error(x("failedRemoveModelFromDatabase"));await d(r),T.success(x("modelRemovedSuccess")),b?.()}catch(e){console.error("Error deleting model:",e),T.error(e instanceof Error?e.message:x("failedDeleteModelTryAgain"))}};return(0,t.jsxs)("div",{className:"flex flex-col gap-4",children:[(0,t.jsx)("p",{className:"text-sm text-text-muted",children:x("compatibleModelsDescription",{type:u?x("anthropic"):x("openai")})}),(0,t.jsxs)("div",{className:"flex items-end gap-2 flex-wrap",children:[(0,t.jsxs)("div",{className:"flex-1 min-w-[240px]",children:[(0,t.jsx)("label",{htmlFor:"new-compatible-model-input",className:"text-xs text-text-muted mb-1 block",children:x("modelId")}),(0,t.jsx)("input",{id:"new-compatible-model-input",type:"text",value:y,onChange:e=>j(e.target.value),onKeyDown:e=>"Enter"===e.key&&A(),placeholder:u?x("anthropicCompatibleModelPlaceholder"):x("openaiCompatibleModelPlaceholder"),className:"w-full px-3 py-2 text-sm border border-border rounded-lg bg-background focus:outline-none focus:border-primary"})]}),(0,t.jsx)(c.Button,{size:"sm",icon:"add",onClick:A,disabled:!y.trim()||C,children:C?x("adding"):x("add")}),(0,t.jsx)(c.Button,{size:"sm",variant:"secondary",icon:"download",onClick:I,disabled:!E||k,children:k?x("importingModels"):x("importFromModels")})]}),!E&&(0,t.jsx)("p",{className:"text-xs text-text-muted",children:x("addConnectionToImport")}),S.length>0&&(0,t.jsx)("div",{className:"flex flex-col gap-3",children:S.map(({modelId:e,fullModel:r,alias:a})=>(0,t.jsx)(R,{modelId:e,fullModel:`${l}/${e}`,copied:i,onCopy:o,onDeleteAlias:()=>D(e,a),t:x,showDeveloperToggle:!u,normalizeToolCallId:h(e),preserveDeveloperRole:f(e),onNormalizeChange:t=>g(e,{normalizeToolCallId:t}),onPreserveChange:t=>g(e,{preserveOpenAIDeveloperRole:t}),compatDisabled:v===e},r))})]})}function E({until:e}){let[a,l]=(0,r.useState)("");return((0,r.useEffect)(()=>{let t=()=>{let t=new Date(e).getTime()-Date.now();if(t<=0)return void l("");let r=Math.floor(t/1e3);if(r<60)l(`${r}s`);else if(r<3600)l(`${Math.floor(r/60)}m ${r%60}s`);else{let e=Math.floor(r/3600),t=Math.floor(r%3600/60);l(`${e}h ${t}m`)}};t();let r=setInterval(t,1e3);return()=>clearInterval(r)},[e]),a)?(0,t.jsxs)("span",{className:"text-xs text-orange-500 font-mono",children:["⏱ ",a]}):null}S.propTypes={model:l.default.shape({id:l.default.string.isRequired}).isRequired,fullModel:l.default.string.isRequired,alias:l.default.string,copied:l.default.string,onCopy:l.default.func.isRequired,t:l.default.func,showDeveloperToggle:l.default.bool,normalizeToolCallId:l.default.bool,preserveDeveloperRole:l.default.bool,onNormalizeChange:l.default.func,onPreserveChange:l.default.func,compatDisabled:l.default.bool},P.propTypes={providerAlias:l.default.string.isRequired,modelAliases:l.default.object.isRequired,copied:l.default.string,onCopy:l.default.func.isRequired,onSetAlias:l.default.func.isRequired,onDeleteAlias:l.default.func.isRequired,t:l.default.func.isRequired,effectiveModelNormalize:l.default.func.isRequired,effectiveModelPreserveDeveloper:l.default.func.isRequired,saveModelCompatFlags:l.default.func.isRequired,compatSavingModelId:l.default.string},R.propTypes={modelId:l.default.string.isRequired,fullModel:l.default.string.isRequired,copied:l.default.string,onCopy:l.default.func.isRequired,onDeleteAlias:l.default.func.isRequired,t:l.default.func,showDeveloperToggle:l.default.bool,normalizeToolCallId:l.default.bool,preserveDeveloperRole:l.default.bool,onNormalizeChange:l.default.func,onPreserveChange:l.default.func,compatDisabled:l.default.bool},A.propTypes={providerId:l.default.string.isRequired,providerAlias:l.default.string.isRequired,copied:l.default.string,onCopy:l.default.func.isRequired,onModelsChanged:l.default.func},I.propTypes={providerStorageAlias:l.default.string.isRequired,providerDisplayAlias:l.default.string.isRequired,modelAliases:l.default.object.isRequired,copied:l.default.string,onCopy:l.default.func.isRequired,onSetAlias:l.default.func.isRequired,onDeleteAlias:l.default.func.isRequired,connections:l.default.arrayOf(l.default.shape({id:l.default.string,isActive:l.default.bool})).isRequired,isAnthropic:l.default.bool,onImportWithProgress:l.default.func.isRequired,t:l.default.func.isRequired,effectiveModelNormalize:l.default.func.isRequired,effectiveModelPreserveDeveloper:l.default.func.isRequired,saveModelCompatFlags:l.default.func.isRequired,compatSavingModelId:l.default.string,onModelsChanged:l.default.func},E.propTypes={until:l.default.string.isRequired};let D={runtime_error:{labelKey:"errorTypeRuntime",variant:"warning"},upstream_auth_error:{labelKey:"errorTypeUpstreamAuth",variant:"error"},auth_missing:{labelKey:"errorTypeMissingCredential",variant:"warning"},token_refresh_failed:{labelKey:"errorTypeRefreshFailed",variant:"warning"},token_expired:{labelKey:"errorTypeTokenExpired",variant:"warning"},upstream_rate_limited:{labelKey:"errorTypeRateLimited",variant:"warning"},upstream_unavailable:{labelKey:"errorTypeUpstreamUnavailable",variant:"error"},network_error:{labelKey:"errorTypeNetworkError",variant:"warning"},unsupported:{labelKey:"errorTypeTestUnsupported",variant:"default"},upstream_error:{labelKey:"errorTypeUpstreamError",variant:"error"}};function O({connection:e,isOAuth:a,isCodex:l,isFirst:s,isLast:i,onMoveUp:o,onMoveDown:d,onToggleActive:u,onToggleRateLimit:m,onToggleCodex5h:x,onToggleCodexWeekly:h,onRetest:f,isRetesting:g,onEdit:b,onDelete:y,onReauth:j,onProxy:C,hasProxy:N,proxySource:w,proxyHost:T,onRefreshToken:S,isRefreshing:P}){let R,A=(0,n.useTranslations)("providers"),I=a?e.name||e.email||e.displayName||A("oauthAccount"):e.name,[O,M]=(0,r.useState)(!1),[U,L]=(0,r.useState)(()=>a&&e.expiresAt?Math.floor((new Date(e.expiresAt).getTime()-Date.now())/6e4):null);(0,r.useEffect)(()=>{if(!a||!e.expiresAt)return;let t=setInterval(()=>{L(Math.floor((new Date(e.expiresAt).getTime()-Date.now())/6e4))},3e4);return()=>clearInterval(t)},[a,e.expiresAt]),(0,r.useEffect)(()=>{let t=()=>{M(e.rateLimitedUntil&&new Date(e.rateLimitedUntil).getTime()>Date.now())};t();let r=e.rateLimitedUntil?setInterval(t,1e3):null;return()=>{r&&clearInterval(r)}},[e.rateLimitedUntil]);let $="unavailable"!==e.testStatus||O?e.testStatus:"active",_=function(e,t,r,a){if(!1===e.isActive)return{statusVariant:"default",statusLabel:a("statusDisabled"),errorType:null,errorBadge:null,errorTextClass:"text-text-muted"};if("active"===t||"success"===t)return{statusVariant:"success",statusLabel:a("statusConnected"),errorType:null,errorBadge:null,errorTextClass:"text-text-muted"};let l=function(e,t){if(t)return"upstream_rate_limited";if(e.lastErrorType)return e.lastErrorType;let r=Number(e.errorCode);if(401===r||403===r)return"upstream_auth_error";if(429===r)return"upstream_rate_limited";if(r>=500)return"upstream_unavailable";let a=(e.lastError||"").toLowerCase();return a?a.includes("runtime")||a.includes("not runnable")||a.includes("not installed")||a.includes("healthcheck")?"runtime_error":a.includes("refresh failed")?"token_refresh_failed":a.includes("token expired")||a.includes("expired")?"token_expired":a.includes("invalid api key")||a.includes("token invalid")||a.includes("revoked")||a.includes("access denied")||a.includes("unauthorized")?"upstream_auth_error":a.includes("rate limit")||a.includes("quota")||a.includes("too many requests")||a.includes("429")?"upstream_rate_limited":a.includes("fetch failed")||a.includes("network")||a.includes("timeout")||a.includes("econn")||a.includes("enotfound")?"network_error":a.includes("not supported")?"unsupported":"upstream_error":null}(e,r),s=l&&D[l]||null;return"runtime_error"===l?{statusVariant:"warning",statusLabel:a("statusRuntimeIssue"),errorType:l,errorBadge:s,errorTextClass:"text-yellow-600 dark:text-yellow-400"}:"upstream_auth_error"===l||"auth_missing"===l||"token_refresh_failed"===l||"token_expired"===l?{statusVariant:"error",statusLabel:a("statusAuthFailed"),errorType:l,errorBadge:s,errorTextClass:"text-red-500"}:"upstream_rate_limited"===l?{statusVariant:"warning",statusLabel:a("statusRateLimited"),errorType:l,errorBadge:s,errorTextClass:"text-yellow-600 dark:text-yellow-400"}:"network_error"===l?{statusVariant:"warning",statusLabel:a("statusNetworkIssue"),errorType:l,errorBadge:s,errorTextClass:"text-yellow-600 dark:text-yellow-400"}:"unsupported"===l?{statusVariant:"default",statusLabel:a("statusTestUnsupported"),errorType:l,errorBadge:s,errorTextClass:"text-text-muted"}:{statusVariant:"error",statusLabel:({unavailable:a("statusUnavailable"),failed:a("statusFailed"),error:a("statusError")})[t]||t||a("statusError"),errorType:l,errorBadge:s,errorTextClass:"text-red-500"}}(e,$,O,A),K=!!e.rateLimitProtection,F=k(e.providerSpecificData&&"object"==typeof e.providerSpecificData&&e.providerSpecificData.codexLimitPolicy&&"object"==typeof e.providerSpecificData.codexLimitPolicy?e.providerSpecificData.codexLimitPolicy:{}),z=F.use5h,q=F.useWeekly;return(0,t.jsxs)("div",{className:`group flex items-center justify-between p-3 rounded-lg hover:bg-black/[0.02] dark:hover:bg-white/[0.02] transition-colors ${!1===e.isActive?"opacity-60":""}`,children:[(0,t.jsxs)("div",{className:"flex items-center gap-3 flex-1 min-w-0",children:[(0,t.jsxs)("div",{className:"flex flex-col",children:[(0,t.jsx)("button",{onClick:o,disabled:s,className:`p-0.5 rounded ${s?"text-text-muted/30 cursor-not-allowed":"hover:bg-sidebar text-text-muted hover:text-primary"}`,children:(0,t.jsx)("span",{className:"material-symbols-outlined text-sm",children:"keyboard_arrow_up"})}),(0,t.jsx)("button",{onClick:d,disabled:i,className:`p-0.5 rounded ${i?"text-text-muted/30 cursor-not-allowed":"hover:bg-sidebar text-text-muted hover:text-primary"}`,children:(0,t.jsx)("span",{className:"material-symbols-outlined text-sm",children:"keyboard_arrow_down"})})]}),(0,t.jsx)("span",{className:"material-symbols-outlined text-base text-text-muted",children:a?"lock":"key"}),(0,t.jsxs)("div",{className:"flex-1 min-w-0",children:[(0,t.jsx)("p",{className:"text-sm font-medium truncate",children:I}),(0,t.jsxs)("div",{className:"flex items-center gap-2 mt-1 flex-wrap",children:[(0,t.jsx)(p.Badge,{variant:_.statusVariant,size:"sm",dot:!0,children:_.statusLabel}),null!==U&&(U<0?(0,t.jsxs)("span",{className:"inline-flex items-center gap-0.5 px-1.5 py-0.5 rounded text-xs font-medium bg-red-500/15 text-red-500",title:`Token expired: ${e.expiresAt}`,children:[(0,t.jsx)("span",{className:"material-symbols-outlined text-[11px]",children:"error"}),"expired"]}):U<30?(0,t.jsxs)("span",{className:"inline-flex items-center gap-0.5 px-1.5 py-0.5 rounded text-xs font-medium bg-amber-500/15 text-amber-500",title:`Token expires in ${U}m`,children:[(0,t.jsx)("span",{className:"material-symbols-outlined text-[11px]",children:"warning"}),`~${U}m`]}):null),O&&!1!==e.isActive&&(0,t.jsx)(E,{until:e.rateLimitedUntil}),_.errorBadge&&!1!==e.isActive&&(0,t.jsx)(p.Badge,{variant:_.errorBadge.variant,size:"sm",children:A(_.errorBadge.labelKey)}),e.lastError&&!1!==e.isActive&&(0,t.jsx)("span",{className:`text-xs truncate max-w-[300px] ${_.errorTextClass}`,title:e.lastError,children:e.lastError}),(0,t.jsxs)("span",{className:"text-xs text-text-muted",children:["#",e.priority]}),e.globalPriority&&(0,t.jsx)("span",{className:"text-xs text-text-muted",children:A("autoPriority",{priority:e.globalPriority})}),(0,t.jsx)("span",{className:"text-text-muted/30 select-none",children:"|"}),(0,t.jsxs)("button",{onClick:()=>m(!K),className:`inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-xs font-medium transition-all cursor-pointer ${K?"bg-emerald-500/15 text-emerald-500 hover:bg-emerald-500/25":"bg-black/[0.03] dark:bg-white/[0.03] text-text-muted/50 hover:text-text-muted hover:bg-black/[0.06] dark:hover:bg-white/[0.06]"}`,title:A(K?"disableRateLimitProtection":"enableRateLimitProtection"),children:[(0,t.jsx)("span",{className:"material-symbols-outlined text-[13px]",children:"shield"}),A(K?"rateLimitProtected":"rateLimitUnprotected")]}),l&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("span",{className:"text-text-muted/30 select-none",children:"|"}),(0,t.jsxs)("button",{onClick:()=>x?.(!z),className:`inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-xs font-medium transition-all cursor-pointer ${z?"bg-blue-500/15 text-blue-500 hover:bg-blue-500/25":"bg-black/[0.03] dark:bg-white/[0.03] text-text-muted/50 hover:text-text-muted hover:bg-black/[0.06] dark:hover:bg-white/[0.06]"}`,title:"Toggle Codex 5h limit policy",children:[(0,t.jsx)("span",{className:"material-symbols-outlined text-[13px]",children:"timer"}),"5h ",z?"ON":"OFF"]}),(0,t.jsxs)("button",{onClick:()=>h?.(!q),className:`inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-xs font-medium transition-all cursor-pointer ${q?"bg-violet-500/15 text-violet-500 hover:bg-violet-500/25":"bg-black/[0.03] dark:bg-white/[0.03] text-text-muted/50 hover:text-text-muted hover:bg-black/[0.06] dark:hover:bg-white/[0.06]"}`,title:"Toggle Codex weekly limit policy",children:[(0,t.jsx)("span",{className:"material-symbols-outlined text-[13px]",children:"date_range"}),"Weekly ",q?"ON":"OFF"]})]}),N&&(R=A("global"===w?"proxySourceGlobal":"provider"===w?"proxySourceProvider":"proxySourceKey"),(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("span",{className:"text-text-muted/30 select-none",children:"|"}),(0,t.jsxs)("span",{className:`inline-flex items-center gap-0.5 px-1.5 py-0.5 rounded text-xs font-medium ${"global"===w?"bg-emerald-500/15 text-emerald-500":"provider"===w?"bg-amber-500/15 text-amber-500":"bg-blue-500/15 text-blue-500"}`,title:A("proxyConfiguredBySource",{source:R,host:T||A("configured")}),children:[(0,t.jsx)("span",{className:"material-symbols-outlined text-[13px]",children:"vpn_lock"}),T||A("proxy")]})]}))]})]})]}),(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)(c.Button,{size:"sm",variant:"ghost",icon:"refresh",loading:g,disabled:!1===e.isActive,onClick:f,className:"!h-7 !px-2 text-xs",title:A("retestAuthentication"),children:A("retest")}),S&&(0,t.jsx)(c.Button,{size:"sm",variant:"ghost",icon:"token",loading:P,disabled:!1===e.isActive||P,onClick:S,className:"!h-7 !px-2 text-xs text-amber-500 hover:text-amber-400",title:"Refresh OAuth token manually",children:"Token"}),(0,t.jsx)(v.Toggle,{size:"sm",checked:e.isActive??!0,onChange:u,title:A(e.isActive??!0?"disableConnection":"enableConnection")}),(0,t.jsxs)("div",{className:"flex gap-1 ml-1 transition-opacity",children:[j&&(0,t.jsx)("button",{onClick:j,className:"p-2 hover:bg-amber-500/10 rounded text-amber-600 hover:text-amber-500",title:A("reauthenticateConnection"),children:(0,t.jsx)("span",{className:"material-symbols-outlined text-[18px]",children:"passkey"})}),(0,t.jsx)("button",{onClick:b,className:"p-2 hover:bg-black/5 dark:hover:bg-white/5 rounded text-text-muted hover:text-primary",title:A("edit"),children:(0,t.jsx)("span",{className:"material-symbols-outlined text-[18px]",children:"edit"})}),(0,t.jsx)("button",{onClick:C,className:"p-2 hover:bg-black/5 dark:hover:bg-white/5 rounded text-text-muted hover:text-primary",title:A("proxyConfig"),children:(0,t.jsx)("span",{className:"material-symbols-outlined text-[18px]",children:"vpn_lock"})}),(0,t.jsx)("button",{onClick:y,className:"p-2 hover:bg-red-500/10 rounded text-red-500",title:A("delete"),children:(0,t.jsx)("span",{className:"material-symbols-outlined text-[18px]",children:"delete"})})]})]})]})}function M({isOpen:e,provider:a,providerName:l,isCompatible:s,isAnthropic:i,onSave:o,onClose:d}){let x=(0,n.useTranslations)("providers"),h="bailian-coding-plan"===a,f="https://coding-intl.dashscope.aliyuncs.com/apps/anthropic/v1",[g,v]=(0,r.useState)({name:"",apiKey:"",priority:1,baseUrl:h?f:""}),[b,y]=(0,r.useState)(!1),[j,C]=(0,r.useState)(null),[N,k]=(0,r.useState)(!1),[w,T]=(0,r.useState)(null),S=async()=>{y(!0),T(null);try{let e=await fetch("/api/providers/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({provider:a,apiKey:g.apiKey})}),t=await e.json();C(t.valid?"success":"failed")}catch{C("failed")}finally{y(!1)}},P=async()=>{if(a&&g.apiKey){k(!0),T(null);try{let e=null;if(h){let t=U(g.baseUrl,f);if(t.error)return void T(t.error);e=t.value}let t=!1;try{y(!0),C(null);let e=await fetch("/api/providers/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({provider:a,apiKey:g.apiKey})});t=!!(await e.json()).valid,C(t?"success":"failed")}catch{C("failed")}finally{y(!1)}if(!t)return void T(x("apiKeyValidationFailed"));let r={name:g.name,apiKey:g.apiKey,priority:g.priority,testStatus:"active",providerSpecificData:void 0};h&&(r.providerSpecificData={baseUrl:e});let l=await o(r);l&&T("string"==typeof l?l:x("failedSaveConnection"))}finally{k(!1)}}};return a?(0,t.jsx)(m.Modal,{isOpen:e,title:x("addProviderApiKeyTitle",{provider:l||a}),onClose:d,children:(0,t.jsxs)("div",{className:"flex flex-col gap-4",children:[(0,t.jsx)(u.Input,{label:x("nameLabel"),value:g.name,onChange:e=>v({...g,name:e.target.value}),placeholder:x("productionKey")}),(0,t.jsxs)("div",{className:"flex gap-2",children:[(0,t.jsx)(u.Input,{label:x("apiKeyLabel"),type:"password",value:g.apiKey,onChange:e=>v({...g,apiKey:e.target.value}),className:"flex-1"}),(0,t.jsx)("div",{className:"pt-6",children:(0,t.jsx)(c.Button,{onClick:S,disabled:!g.apiKey||b||N,variant:"secondary",children:b?x("checking"):x("check")})})]}),j&&(0,t.jsx)(p.Badge,{variant:"success"===j?"success":"error",children:"success"===j?x("valid"):x("invalid")}),w&&(0,t.jsx)("div",{className:"text-sm text-red-500 bg-red-500/10 border border-red-500/20 rounded-lg px-3 py-2",children:w}),s&&(0,t.jsx)("p",{className:"text-xs text-text-muted",children:i?x("validationChecksAnthropicCompatible",{provider:l||x("anthropicCompatibleName")}):x("validationChecksOpenAiCompatible",{provider:l||x("openaiCompatibleName")})}),(0,t.jsx)(u.Input,{label:x("priorityLabel"),type:"number",value:g.priority,onChange:e=>v({...g,priority:Number.parseInt(e.target.value)||1})}),h&&(0,t.jsx)(u.Input,{label:"Base URL",value:g.baseUrl,onChange:e=>v({...g,baseUrl:e.target.value}),placeholder:f,hint:"Optional: Custom base URL for bailian-coding-plan provider"}),(0,t.jsxs)("div",{className:"flex gap-2",children:[(0,t.jsx)(c.Button,{onClick:P,fullWidth:!0,disabled:!g.name||!g.apiKey||N,children:N?x("saving"):x("save")}),(0,t.jsx)(c.Button,{onClick:d,variant:"ghost",fullWidth:!0,children:x("cancel")})]})]})}):null}function U(e,t){let r=("string"==typeof e?e.trim():"")||t;try{let e=new URL(r);if("http:"!==e.protocol&&"https:"!==e.protocol)return{value:null,error:"Base URL must use http or https"};return{value:r,error:null}}catch{return{value:null,error:"Base URL must be a valid URL"}}}function L({isOpen:e,connection:a,onSave:l,onClose:s}){let i=(0,n.useTranslations)("providers"),[o,d]=(0,r.useState)({name:"",priority:1,apiKey:"",healthCheckInterval:60,baseUrl:""}),[x,h]=(0,r.useState)(!1),[f,g]=(0,r.useState)(null),[v,b]=(0,r.useState)(!1),[y,C]=(0,r.useState)(null),[N,k]=(0,r.useState)(!1),[w,T]=(0,r.useState)(null),[S,P]=(0,r.useState)([]),[R,A]=(0,r.useState)(""),I=a?.provider==="bailian-coding-plan",E="https://coding-intl.dashscope.aliyuncs.com/apps/anthropic/v1";(0,r.useEffect)(()=>{if(a){let e=a.providerSpecificData?.baseUrl;d({name:a.name||"",priority:a.priority||1,apiKey:"",healthCheckInterval:a.healthCheckInterval??60,baseUrl:e||(I?E:"")});let t=a.providerSpecificData?.extraApiKeys;P(Array.isArray(t)?t:[]),A(""),g(null),C(null),T(null)}},[a,I]);let O=async()=>{if(a?.provider){h(!0),g(null);try{let e=await fetch(`/api/providers/${a.id}/test`,{method:"POST"}),t=await e.json();g({valid:!!t.valid,diagnosis:t.diagnosis||null,message:t.error||null})}catch{g({valid:!1,diagnosis:{type:"network_error"},message:i("failedTestConnection")})}finally{h(!1)}}},M=async()=>{if(a?.provider&&o.apiKey){b(!0),C(null);try{let e=await fetch("/api/providers/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({provider:a.provider,apiKey:o.apiKey})}),t=await e.json();C(t.valid?"success":"failed")}catch{C("failed")}finally{b(!1)}}},L=async()=>{k(!0),T(null);try{let e={name:o.name,priority:o.priority,healthCheckInterval:o.healthCheckInterval},t=null;if(I){let e=U(o.baseUrl,E);if(e.error)return void T(e.error);t=e.value}if(!$&&o.apiKey){e.apiKey=o.apiKey;let t="success"===y;if(!t)try{b(!0),C(null);let e=await fetch("/api/providers/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({provider:a.provider,apiKey:o.apiKey})});t=!!(await e.json()).valid,C(t?"success":"failed")}catch{C("failed")}finally{b(!1)}t&&(e.testStatus="active",e.lastError=null,e.lastErrorAt=null,e.lastErrorType=null,e.lastErrorSource=null,e.errorCode=null,e.rateLimitedUntil=null)}!$&&(e.providerSpecificData={...a.providerSpecificData||{},extraApiKeys:S.filter(e=>e.trim().length>0)},I&&(e.providerSpecificData.baseUrl=t));let r=await l(e);r&&T("string"==typeof r?r:i("failedSaveConnection"))}finally{k(!1)}};if(!a)return null;let $="oauth"===a.authType,_=(0,j.isOpenAICompatibleProvider)(a.provider)||(0,j.isAnthropicCompatibleProvider)(a.provider),K=!f?.valid&&f?.diagnosis?.type&&D[f.diagnosis.type]||null;return(0,t.jsx)(m.Modal,{isOpen:e,title:i("editConnection"),onClose:s,children:(0,t.jsxs)("div",{className:"flex flex-col gap-4",children:[(0,t.jsx)(u.Input,{label:i("nameLabel"),value:o.name,onChange:e=>d({...o,name:e.target.value}),placeholder:$?i("accountName"):i("productionKey")}),$&&a.email&&(0,t.jsxs)("div",{className:"bg-sidebar/50 p-3 rounded-lg",children:[(0,t.jsx)("p",{className:"text-sm text-text-muted mb-1",children:i("email")}),(0,t.jsx)("p",{className:"font-medium",children:a.email})]}),$&&(0,t.jsx)(u.Input,{label:i("healthCheckMinutes"),type:"number",value:o.healthCheckInterval,onChange:e=>d({...o,healthCheckInterval:Math.max(0,Number.parseInt(e.target.value)||0)}),hint:i("healthCheckHint")}),(0,t.jsx)(u.Input,{label:i("priorityLabel"),type:"number",value:o.priority,onChange:e=>d({...o,priority:Number.parseInt(e.target.value)||1})}),!$&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)("div",{className:"flex gap-2",children:[(0,t.jsx)(u.Input,{label:i("apiKeyLabel"),type:"password",value:o.apiKey,onChange:e=>d({...o,apiKey:e.target.value}),placeholder:i("enterNewApiKey"),hint:i("leaveBlankKeepCurrentApiKey"),className:"flex-1"}),(0,t.jsx)("div",{className:"pt-6",children:(0,t.jsx)(c.Button,{onClick:M,disabled:!o.apiKey||v||N,variant:"secondary",children:v?i("checking"):i("check")})})]}),y&&(0,t.jsx)(p.Badge,{variant:"success"===y?"success":"error",children:"success"===y?i("valid"):i("invalid")}),w&&(0,t.jsx)("div",{className:"text-sm text-red-500 bg-red-500/10 border border-red-500/20 rounded-lg px-3 py-2",children:w})]}),I&&(0,t.jsx)(u.Input,{label:"Base URL",value:o.baseUrl,onChange:e=>d({...o,baseUrl:e.target.value}),placeholder:E,hint:"Custom base URL for bailian-coding-plan provider"}),!$&&(0,t.jsxs)("div",{className:"flex flex-col gap-2",children:[(0,t.jsxs)("label",{className:"text-sm font-medium text-text-main",children:["Extra API Keys",(0,t.jsx)("span",{className:"ml-2 text-[11px] font-normal text-text-muted",children:"(round-robin rotation — optional)"})]}),S.length>0&&(0,t.jsx)("div",{className:"flex flex-col gap-1.5",children:S.map((e,r)=>(0,t.jsxs)("div",{className:"flex items-center gap-2",children:[(0,t.jsx)("span",{className:"flex-1 font-mono text-xs bg-sidebar/50 px-3 py-2 rounded border border-border text-text-muted truncate",children:`Key #${r+2}: ${e.slice(0,6)}...${e.slice(-4)}`}),(0,t.jsx)("button",{onClick:()=>P(S.filter((e,t)=>t!==r)),className:"p-1.5 rounded hover:bg-red-500/10 text-red-400 hover:text-red-500",title:"Remove this key",children:(0,t.jsx)("span",{className:"material-symbols-outlined text-[16px]",children:"close"})})]},r))}),(0,t.jsxs)("div",{className:"flex gap-2",children:[(0,t.jsx)("input",{type:"password",value:R,onChange:e=>A(e.target.value),placeholder:"Add another API key...",className:"flex-1 text-sm bg-sidebar/50 border border-border rounded px-3 py-2 text-text-main placeholder:text-text-muted focus:ring-1 focus:ring-primary outline-none",onKeyDown:e=>{"Enter"===e.key&&R.trim()&&(P([...S,R.trim()]),A(""))}}),(0,t.jsx)("button",{onClick:()=>{R.trim()&&(P([...S,R.trim()]),A(""))},disabled:!R.trim(),className:"px-3 py-2 rounded bg-primary/10 text-primary hover:bg-primary/20 disabled:opacity-40 text-sm font-medium",children:"Add"})]}),S.length>0&&(0,t.jsxs)("p",{className:"text-[11px] text-text-muted",children:[S.length+1," keys total — rotating round-robin on each request."]})]}),!_&&(0,t.jsxs)("div",{className:"flex items-center gap-3",children:[(0,t.jsx)(c.Button,{onClick:O,variant:"secondary",disabled:x,children:x?i("testing"):i("testConnection")}),f&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(p.Badge,{variant:f.valid?"success":"error",children:f.valid?i("valid"):i("failed")}),K&&(0,t.jsx)(p.Badge,{variant:K.variant,children:i(K.labelKey)})]})]}),(0,t.jsxs)("div",{className:"flex gap-2",children:[(0,t.jsx)(c.Button,{onClick:L,fullWidth:!0,disabled:N,children:N?i("saving"):i("save")}),(0,t.jsx)(c.Button,{onClick:s,variant:"ghost",fullWidth:!0,children:i("cancel")})]})]})})}function $({isOpen:e,node:a,onSave:l,onClose:s,isAnthropic:i}){let o=(0,n.useTranslations)("providers"),[d,x]=(0,r.useState)({name:"",prefix:"",apiType:"chat",baseUrl:"https://api.openai.com/v1",chatPath:"",modelsPath:""}),[h,f]=(0,r.useState)(!1),[g,v]=(0,r.useState)(""),[y,j]=(0,r.useState)(!1),[C,N]=(0,r.useState)(null),[k,w]=(0,r.useState)(!1);(0,r.useEffect)(()=>{a&&(x({name:a.name||"",prefix:a.prefix||"",apiType:a.apiType||"chat",baseUrl:a.baseUrl||(i?"https://api.anthropic.com/v1":"https://api.openai.com/v1"),chatPath:a.chatPath||"",modelsPath:a.modelsPath||""}),w(!!(a.chatPath||a.modelsPath)))},[a,i]);let T=[{value:"chat",label:o("chatCompletions")},{value:"responses",label:o("responsesApi")}],S=async()=>{if(d.name.trim()&&d.prefix.trim()&&d.baseUrl.trim()){f(!0);try{let e={name:d.name,prefix:d.prefix,baseUrl:d.baseUrl,chatPath:d.chatPath||"",modelsPath:d.modelsPath||""};i||(e.apiType=d.apiType),await l(e)}finally{f(!1)}}},P=async()=>{j(!0);try{let e=await fetch("/api/provider-nodes/validate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({baseUrl:d.baseUrl,apiKey:g,type:i?"anthropic-compatible":"openai-compatible",modelsPath:d.modelsPath||""})}),t=await e.json();N(t.valid?"success":"failed")}catch{N("failed")}finally{j(!1)}};return a?(0,t.jsx)(m.Modal,{isOpen:e,title:o("editCompatibleTitle",{type:o(i?"anthropic":"openai")}),onClose:s,children:(0,t.jsxs)("div",{className:"flex flex-col gap-4",children:[(0,t.jsx)(u.Input,{label:o("nameLabel"),value:d.name,onChange:e=>x({...d,name:e.target.value}),placeholder:o("compatibleProdPlaceholder",{type:o(i?"anthropic":"openai")}),hint:o("nameHint")}),(0,t.jsx)(u.Input,{label:o("prefixLabel"),value:d.prefix,onChange:e=>x({...d,prefix:e.target.value}),placeholder:o(i?"anthropicPrefixPlaceholder":"openaiPrefixPlaceholder"),hint:o("prefixHint")}),!i&&(0,t.jsx)(b.Select,{label:o("apiTypeLabel"),options:T,value:d.apiType,onChange:e=>x({...d,apiType:e.target.value})}),(0,t.jsx)(u.Input,{label:o("baseUrlLabel"),value:d.baseUrl,onChange:e=>x({...d,baseUrl:e.target.value}),placeholder:o(i?"anthropicBaseUrlPlaceholder":"openaiBaseUrlPlaceholder"),hint:o("compatibleBaseUrlHint",{type:o(i?"anthropic":"openai")})}),(0,t.jsxs)("button",{type:"button",className:"text-sm text-text-muted hover:text-text-primary flex items-center gap-1",onClick:()=>w(!k),"aria-expanded":k,"aria-controls":"advanced-settings",children:[(0,t.jsx)("span",{className:`transition-transform ${k?"rotate-90":""}`,"aria-hidden":"true",children:"▶"}),o("advancedSettings")]}),k&&(0,t.jsxs)("div",{id:"advanced-settings",className:"flex flex-col gap-3 pl-2 border-l-2 border-border",children:[(0,t.jsx)(u.Input,{label:o("chatPathLabel"),value:d.chatPath,onChange:e=>x({...d,chatPath:e.target.value}),placeholder:i?"/messages":o("chatPathPlaceholder"),hint:o("chatPathHint")}),(0,t.jsx)(u.Input,{label:o("modelsPathLabel"),value:d.modelsPath,onChange:e=>x({...d,modelsPath:e.target.value}),placeholder:o("modelsPathPlaceholder"),hint:o("modelsPathHint")})]}),(0,t.jsxs)("div",{className:"flex gap-2",children:[(0,t.jsx)(u.Input,{label:o("apiKeyForCheck"),type:"password",value:g,onChange:e=>v(e.target.value),className:"flex-1"}),(0,t.jsx)("div",{className:"pt-6",children:(0,t.jsx)(c.Button,{onClick:P,disabled:!g||y||!d.baseUrl.trim(),variant:"secondary",children:o(y?"checking":"check")})})]}),C&&(0,t.jsx)(p.Badge,{variant:"success"===C?"success":"error",children:o("success"===C?"valid":"invalid")}),(0,t.jsxs)("div",{className:"flex gap-2",children:[(0,t.jsx)(c.Button,{onClick:S,fullWidth:!0,disabled:!d.name.trim()||!d.prefix.trim()||!d.baseUrl.trim()||h,children:o(h?"saving":"save")}),(0,t.jsx)(c.Button,{onClick:s,variant:"ghost",fullWidth:!0,children:o("cancel")})]})]})}):null}O.propTypes={connection:l.default.shape({id:l.default.string,name:l.default.string,email:l.default.string,displayName:l.default.string,rateLimitedUntil:l.default.string,rateLimitProtection:l.default.bool,testStatus:l.default.string,isActive:l.default.bool,priority:l.default.number,lastError:l.default.string,lastErrorType:l.default.string,lastErrorSource:l.default.string,errorCode:l.default.oneOfType([l.default.string,l.default.number]),globalPriority:l.default.number,providerSpecificData:l.default.object}).isRequired,isOAuth:l.default.bool.isRequired,isCodex:l.default.bool,isFirst:l.default.bool.isRequired,isLast:l.default.bool.isRequired,onMoveUp:l.default.func.isRequired,onMoveDown:l.default.func.isRequired,onToggleActive:l.default.func.isRequired,onToggleRateLimit:l.default.func.isRequired,onToggleCodex5h:l.default.func,onToggleCodexWeekly:l.default.func,onRetest:l.default.func.isRequired,isRetesting:l.default.bool,onEdit:l.default.func.isRequired,onDelete:l.default.func.isRequired,onReauth:l.default.func},M.propTypes={isOpen:l.default.bool.isRequired,provider:l.default.string,providerName:l.default.string,isCompatible:l.default.bool,isAnthropic:l.default.bool,onSave:l.default.func.isRequired,onClose:l.default.func.isRequired},L.propTypes={isOpen:l.default.bool.isRequired,connection:l.default.shape({id:l.default.string,name:l.default.string,email:l.default.string,priority:l.default.number,authType:l.default.string,provider:l.default.string}),onSave:l.default.func.isRequired,onClose:l.default.func.isRequired},$.propTypes={isOpen:l.default.bool.isRequired,node:l.default.shape({id:l.default.string,name:l.default.string,prefix:l.default.string,apiType:l.default.string,baseUrl:l.default.string,chatPath:l.default.string,modelsPath:l.default.string}),onSave:l.default.func.isRequired,onClose:l.default.func.isRequired,isAnthropic:l.default.bool},e.s(["default",()=>T],710864)}]);
package/app/CHANGELOG.md CHANGED
@@ -4,6 +4,62 @@
4
4
 
5
5
  ---
6
6
 
7
+ ## [2.8.6] — 2026-03-20
8
+
9
+ > Sprint: Merge PR #494 (MiniMax role fix), fix KIRO MITM dashboard, triage 8 issues.
10
+
11
+ ### Features
12
+
13
+ - **MiniMax developer→system role fix** (PR #494 by @zhangqiang8vip): Per-model `preserveDeveloperRole` toggle. Adds "Compatibility" UI in providers page. Fixes 422 "role param error" for MiniMax and similar gateways.
14
+ - **roleNormalizer**: `normalizeDeveloperRole()` now accepts `preserveDeveloperRole` parameter with tri-state behavior (undefined=keep, true=keep, false=convert).
15
+ - **DB**: New `getModelPreserveOpenAIDeveloperRole()` and `mergeModelCompatOverride()` in `models.ts`.
16
+
17
+ ### Bug Fixes
18
+
19
+ - **KIRO MITM dashboard** (#481/#487): `CLIToolsPageClient` now routes any `configType: "mitm"` tool to `AntigravityToolCard` (MITM Start/Stop controls). Previously only Antigravity was hardcoded.
20
+ - **AntigravityToolCard generic**: Uses `tool.image`, `tool.description`, `tool.id` instead of hardcoded Antigravity values. Guards against missing `defaultModels`.
21
+
22
+ ### Cleanup
23
+
24
+ - Removed `ZWS_README_V2.md` (development-only docs from PR #494).
25
+
26
+ ### Issues Triaged (8)
27
+
28
+ - **#487** — Closed (KIRO MITM fixed in this release)
29
+ - **#486** — needs-info (Windows REG.exe PATH issue)
30
+ - **#489** — needs-info (Antigravity projectId missing, OAuth reconnect needed)
31
+ - **#492** — needs-info (missing app/server.js on mise-managed Node)
32
+ - **#490** — Acknowledged (streaming + context cache blocking, fix planned)
33
+ - **#491** — Acknowledged (Codex auth state inconsistency)
34
+ - **#493** — Acknowledged (Modal provider model name prefix, workaround provided)
35
+ - **#488** — Feature request backlog (auto-update model lists)
36
+
37
+ ---
38
+
39
+ ## [2.8.5] — 2026-03-19
40
+
41
+ > Sprint: Fix zombie SSE streams, context cache first-turn, KIRO MITM, and triage 5 external issues.
42
+
43
+ ### Bug Fixes
44
+
45
+ - **Zombie SSE Streams** (#473): Reduce `STREAM_IDLE_TIMEOUT_MS` from 300s → 120s for faster combo fallback when providers hang mid-stream. Configurable via env var.
46
+ - **Context Cache Tag** (#474): Fix `injectModelTag()` to handle first-turn requests (no assistant messages) — context cache protection now works from the very first response.
47
+ - **KIRO MITM** (#481): Change KIRO `configType` from `guide` → `mitm` so the dashboard renders MITM Start/Stop controls.
48
+ - **E2E Test** (CI): Fix `providers-bailian-coding-plan.spec.ts` — dismiss pre-existing modal overlay before clicking Add API Key button.
49
+
50
+ ### Closed Issues
51
+
52
+ - #473 — Zombie SSE streams bypass combo fallback
53
+ - #474 — Context cache `<omniModel>` tag missing on first turn
54
+ - #481 — MITM for KIRO not activatable from dashboard
55
+ - #468 — Gemini CLI remote server (superseded by #462 deprecation)
56
+ - #438 — Claude unable to write files (external CLI issue)
57
+ - #439 — AppImage doesn't work (documented libfuse2 workaround)
58
+ - #402 — ARM64 DMG "damaged" (documented xattr -cr workaround)
59
+ - #460 — CLI not runnable on Windows (documented PATH fix)
60
+
61
+ ---
62
+
7
63
  ## [2.8.4] — 2026-03-19
8
64
 
9
65
  > Sprint: Gemini CLI deprecation, VM guide i18n fix, dependabot security fix, provider schema expansion.
@@ -1,7 +1,7 @@
1
1
  openapi: 3.1.0
2
2
  info:
3
3
  title: OmniRoute API
4
- version: 2.8.4
4
+ version: 2.8.6
5
5
  description: |
6
6
  OmniRoute is a local-first AI API proxy router. It provides an OpenAI-compatible
7
7
  endpoint that routes requests to multiple AI providers with load balancing,
@@ -4,9 +4,9 @@ import { loadProviderCredentials } from "./credentialLoader.ts";
4
4
  export const FETCH_TIMEOUT_MS = parseInt(process.env.FETCH_TIMEOUT_MS || "120000", 10);
5
5
 
6
6
  // Idle timeout for SSE streams (ms). Closes stream if no data for this duration.
7
- // Default: 300s to support extended-thinking models (claude-opus-4-6, o3, etc.)
8
- // that may pause for >60s during deep reasoning phases. Override with STREAM_IDLE_TIMEOUT_MS env var.
9
- export const STREAM_IDLE_TIMEOUT_MS = parseInt(process.env.STREAM_IDLE_TIMEOUT_MS || "300000", 10);
7
+ // Default: 120s balances deep-reasoning pauses with fast zombie stream detection (#473).
8
+ // Extended-thinking models rarely pause >90s between chunks. Override with STREAM_IDLE_TIMEOUT_MS env var.
9
+ export const STREAM_IDLE_TIMEOUT_MS = parseInt(process.env.STREAM_IDLE_TIMEOUT_MS || "120000", 10);
10
10
 
11
11
  // Provider configurations
12
12
  // OAuth credentials read from env vars with hardcoded fallbacks for backward compatibility.
@@ -23,7 +23,7 @@ import {
23
23
  appendRequestLog,
24
24
  saveCallLog,
25
25
  } from "@/lib/usageDb";
26
- import { getModelNormalizeToolCallId } from "@/lib/db/models";
26
+ import { getModelNormalizeToolCallId, getModelPreserveOpenAIDeveloperRole } from "@/lib/localDb";
27
27
  import { getExecutor } from "../executors/index.ts";
28
28
  import { translateNonStreamingResponse } from "./responseTranslator.ts";
29
29
  import { extractUsageFromResponse } from "./usageExtractor.ts";
@@ -318,6 +318,10 @@ export async function handleChatCore({
318
318
  }
319
319
 
320
320
  const normalizeToolCallId = getModelNormalizeToolCallId(provider || "", model || "");
321
+ const preserveDeveloperRole = getModelPreserveOpenAIDeveloperRole(
322
+ provider || "",
323
+ model || ""
324
+ );
321
325
  translatedBody = translateRequest(
322
326
  sourceFormat,
323
327
  targetFormat,
@@ -327,7 +331,7 @@ export async function handleChatCore({
327
331
  credentials,
328
332
  provider,
329
333
  reqLogger,
330
- { normalizeToolCallId }
334
+ { normalizeToolCallId, preserveDeveloperRole }
331
335
  );
332
336
  }
333
337
  } catch (error) {
@@ -52,7 +52,15 @@ export function injectModelTag(messages: Message[], providerModel: string): Mess
52
52
 
53
53
  // Find last assistant message with string content
54
54
  const lastAssistantIdx = cleaned.map((m) => m.role).lastIndexOf("assistant");
55
- if (lastAssistantIdx === -1) return cleaned;
55
+
56
+ // #474: If no assistant message exists yet (first turn), append a synthetic one
57
+ // so the tag is present when the client sends the next request with the response.
58
+ if (lastAssistantIdx === -1) {
59
+ return [
60
+ ...cleaned,
61
+ { role: "assistant", content: `\n<omniModel>${providerModel}</omniModel>` },
62
+ ];
63
+ }
56
64
 
57
65
  const msg = cleaned[lastAssistantIdx];
58
66
  if (typeof msg.content !== "string") return cleaned;
@@ -76,26 +76,27 @@ function supportsSystemRole(provider: string, model: string): boolean {
76
76
  }
77
77
 
78
78
  /**
79
- * Normalize the `developer` role to `system` for non-OpenAI providers.
80
- * OpenAI introduced `developer` as a replacement for `system` in newer models,
81
- * but most other providers still expect `system`.
79
+ * Normalize the `developer` role to `system` when the upstream does not support it.
80
+ * OpenAI Responses API sends `developer`; MiniMax and most OpenAI-compatible gateways
81
+ * only accept system/user/assistant/tool and return "role param error" otherwise.
82
82
  *
83
83
  * @param messages - Array of messages
84
84
  * @param targetFormat - The target format (e.g., "openai", "claude", "gemini")
85
- * @returns Modified messages array
85
+ * @param preserveDeveloperRole - For targetFormat openai: undefined/true = keep developer (legacy default); false = map to system (MiniMax etc.)
86
86
  */
87
87
  export function normalizeDeveloperRole(
88
88
  messages: NormalizedMessage[] | unknown,
89
- targetFormat: string
89
+ targetFormat: string,
90
+ preserveDeveloperRole?: boolean
90
91
  ): NormalizedMessage[] | unknown {
91
92
  if (!Array.isArray(messages)) return messages;
92
93
 
93
- // For OpenAI format, keep developer role as-is (it's valid)
94
- // For all other formats, convert developer → system
95
- if (targetFormat === "openai") return messages;
94
+ if (targetFormat === "openai" && preserveDeveloperRole !== false) return messages;
96
95
 
97
96
  return messages.map((msg: NormalizedMessage) => {
98
- if (msg.role === "developer") {
97
+ if (!msg || typeof msg !== "object") return msg;
98
+ const role = typeof msg.role === "string" ? msg.role : "";
99
+ if (role.toLowerCase() === "developer") {
99
100
  return { ...msg, role: "system" };
100
101
  }
101
102
  return msg;
@@ -170,24 +171,18 @@ export function normalizeSystemRole(
170
171
  * Full role normalization pipeline.
171
172
  * Call this before sending the request to the provider.
172
173
  *
173
- * @param messages - Array of messages
174
- * @param provider - Provider name/id
175
- * @param model - Model name
176
- * @param targetFormat - Target API format
177
- * @returns Normalized messages array
174
+ * @param preserveDeveloperRole - See {@link normalizeDeveloperRole}
178
175
  */
179
176
  export function normalizeRoles(
180
177
  messages: NormalizedMessage[] | unknown,
181
178
  provider: string,
182
179
  model: string,
183
- targetFormat: string
180
+ targetFormat: string,
181
+ preserveDeveloperRole?: boolean
184
182
  ): NormalizedMessage[] | unknown {
185
183
  if (!Array.isArray(messages)) return messages;
186
184
 
187
- // Step 1: Normalize developer → system (for non-OpenAI formats)
188
- let result = normalizeDeveloperRole(messages, targetFormat);
189
-
190
- // Step 2: Normalize system → user (for providers that don't support system role)
185
+ let result = normalizeDeveloperRole(messages, targetFormat, preserveDeveloperRole);
191
186
  result = normalizeSystemRole(result, provider, model);
192
187
 
193
188
  return result;
@@ -67,6 +67,7 @@ function normalizeOpenAIResponsesRequest(body) {
67
67
  }
68
68
 
69
69
  /** @param options.normalizeToolCallId - When true, use 9-char tool call ids (e.g. Mistral); when false, leave ids as-is */
70
+ /** @param options.preserveDeveloperRole - undefined/true: keep developer for OpenAI format (default); false: map to system */
70
71
  // Translate request: source -> openai -> target
71
72
  export function translateRequest(
72
73
  sourceFormat,
@@ -77,10 +78,11 @@ export function translateRequest(
77
78
  credentials = null,
78
79
  provider = null,
79
80
  reqLogger = null,
80
- options?: { normalizeToolCallId?: boolean }
81
+ options?: { normalizeToolCallId?: boolean; preserveDeveloperRole?: boolean }
81
82
  ) {
82
83
  let result = body;
83
84
  const use9CharId = options?.normalizeToolCallId === true;
85
+ const preserveDeveloperRole = options?.preserveDeveloperRole;
84
86
 
85
87
  // Phase 2: Apply thinking budget control before normalization
86
88
  result = applyThinkingBudget(result);
@@ -94,9 +96,15 @@ export function translateRequest(
94
96
  // Fix missing tool responses (insert empty tool_result if needed)
95
97
  fixMissingToolResponses(result);
96
98
 
97
- // Normalize roles: developer→system for non-OpenAI, system→user for incompatible models
99
+ // Normalize roles: developer→system unless preserved, system→user for incompatible models
98
100
  if (result.messages && Array.isArray(result.messages)) {
99
- result.messages = normalizeRoles(result.messages, provider || "", model || "", targetFormat);
101
+ result.messages = normalizeRoles(
102
+ result.messages,
103
+ provider || "",
104
+ model || "",
105
+ targetFormat,
106
+ preserveDeveloperRole
107
+ );
100
108
  }
101
109
 
102
110
  // If same format, skip translation steps
@@ -143,6 +151,22 @@ export function translateRequest(
143
151
  result = normalizeOpenAIResponsesRequest(result);
144
152
  }
145
153
 
154
+ // After OPENAI_RESPONSES → OPENAI, messages are built from input; first normalizeRoles was a no-op.
155
+ // Run role pipeline again so developer→system respects preserveDeveloperRole (no hardcoding in translator).
156
+ if (
157
+ sourceFormat === FORMATS.OPENAI_RESPONSES &&
158
+ result.messages &&
159
+ Array.isArray(result.messages)
160
+ ) {
161
+ result.messages = normalizeRoles(
162
+ result.messages,
163
+ provider || "",
164
+ model || "",
165
+ targetFormat,
166
+ preserveDeveloperRole
167
+ );
168
+ }
169
+
146
170
  // Ensure unique tool_call ids on final payload (translators may have introduced duplicates)
147
171
  ensureToolCallIds(result, { use9CharId });
148
172
  fixMissingToolResponses(result);
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omniroute",
3
- "version": "2.8.4",
3
+ "version": "2.8.6",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omniroute",
9
- "version": "2.8.4",
9
+ "version": "2.8.6",
10
10
  "hasInstallScript": true,
11
11
  "license": "MIT",
12
12
  "workspaces": [
package/app/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omniroute",
3
- "version": "2.8.4",
3
+ "version": "2.8.6",
4
4
  "description": "Smart AI Router with auto fallback — route to FREE & cheap models, zero downtime. Works with Cursor, Cline, Claude Desktop, Codex, and any OpenAI-compatible tool.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -267,6 +267,18 @@ export default function CLIToolsPageClient({ machineId }) {
267
267
  />
268
268
  );
269
269
  default:
270
+ // #487: Any tool with configType "mitm" should use the MITM card (Start/Stop controls)
271
+ if (tool.configType === "mitm") {
272
+ return (
273
+ <AntigravityToolCard
274
+ key={toolId}
275
+ {...commonProps}
276
+ activeProviders={getActiveProviders()}
277
+ hasActiveProviders={hasActiveProviders}
278
+ cloudEnabled={cloudEnabled}
279
+ />
280
+ );
281
+ }
270
282
  return (
271
283
  <DefaultToolCard
272
284
  key={toolId}
@@ -41,7 +41,7 @@ export default function AntigravityToolCard({
41
41
 
42
42
  const loadSavedMappings = async () => {
43
43
  try {
44
- const res = await fetch("/api/cli-tools/antigravity-mitm/alias?tool=antigravity");
44
+ const res = await fetch(`/api/cli-tools/antigravity-mitm/alias?tool=${tool.id}`);
45
45
  if (res.ok) {
46
46
  const data = await res.json();
47
47
  const aliases = data.aliases || {};
@@ -187,7 +187,7 @@ export default function AntigravityToolCard({
187
187
  const res = await fetch("/api/cli-tools/antigravity-mitm/alias", {
188
188
  method: "PUT",
189
189
  headers: { "Content-Type": "application/json" },
190
- body: JSON.stringify({ tool: "antigravity", mappings: modelMappings }),
190
+ body: JSON.stringify({ tool: tool.id, mappings: modelMappings }),
191
191
  });
192
192
 
193
193
  if (!res.ok) {
@@ -211,7 +211,7 @@ export default function AntigravityToolCard({
211
211
  <div className="flex items-center gap-3">
212
212
  <div className="size-8 flex items-center justify-center shrink-0">
213
213
  <Image
214
- src="/providers/antigravity.png"
214
+ src={tool.image || "/providers/antigravity.png"}
215
215
  alt={tool.name}
216
216
  width={32}
217
217
  height={32}
@@ -235,7 +235,7 @@ export default function AntigravityToolCard({
235
235
  </Badge>
236
236
  )}
237
237
  </div>
238
- <p className="text-xs text-text-muted truncate">{t("toolDescriptions.antigravity")}</p>
238
+ <p className="text-xs text-text-muted truncate">{tool.description}</p>
239
239
  </div>
240
240
  </div>
241
241
  <span
@@ -306,7 +306,7 @@ export default function AntigravityToolCard({
306
306
  )}
307
307
  </div>
308
308
 
309
- {tool.defaultModels.map((model) => (
309
+ {(tool.defaultModels || []).map((model) => (
310
310
  <div key={model.alias} className="flex items-center gap-2">
311
311
  <span className="w-32 shrink-0 text-sm font-semibold text-text-main text-right">
312
312
  {model.name}