@jheavenknows/bluerouter 1.0.121 → 1.0.123

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 (411) hide show
  1. package/app/.next-cli-build/BUILD_ID +1 -1
  2. package/app/.next-cli-build/app-path-routes-manifest.json +3 -3
  3. package/app/.next-cli-build/build-manifest.json +2 -2
  4. package/app/.next-cli-build/server/app/(dashboard)/dashboard/api-keys/page.js +1 -1
  5. package/app/.next-cli-build/server/app/(dashboard)/dashboard/api-keys/page_client-reference-manifest.js +1 -1
  6. package/app/.next-cli-build/server/app/(dashboard)/dashboard/basic-chat/page_client-reference-manifest.js +1 -1
  7. package/app/.next-cli-build/server/app/(dashboard)/dashboard/cli-tools/[toolId]/page.js.nft.json +1 -1
  8. package/app/.next-cli-build/server/app/(dashboard)/dashboard/cli-tools/[toolId]/page_client-reference-manifest.js +1 -1
  9. package/app/.next-cli-build/server/app/(dashboard)/dashboard/cli-tools/page.js.nft.json +1 -1
  10. package/app/.next-cli-build/server/app/(dashboard)/dashboard/cli-tools/page_client-reference-manifest.js +1 -1
  11. package/app/.next-cli-build/server/app/(dashboard)/dashboard/combos/page.js +1 -1
  12. package/app/.next-cli-build/server/app/(dashboard)/dashboard/combos/page_client-reference-manifest.js +1 -1
  13. package/app/.next-cli-build/server/app/(dashboard)/dashboard/console-log/page_client-reference-manifest.js +1 -1
  14. package/app/.next-cli-build/server/app/(dashboard)/dashboard/endpoint/page.js.nft.json +1 -1
  15. package/app/.next-cli-build/server/app/(dashboard)/dashboard/endpoint/page_client-reference-manifest.js +1 -1
  16. package/app/.next-cli-build/server/app/(dashboard)/dashboard/media-providers/[kind]/[id]/page_client-reference-manifest.js +1 -1
  17. package/app/.next-cli-build/server/app/(dashboard)/dashboard/media-providers/[kind]/page_client-reference-manifest.js +1 -1
  18. package/app/.next-cli-build/server/app/(dashboard)/dashboard/media-providers/combo/[id]/page_client-reference-manifest.js +1 -1
  19. package/app/.next-cli-build/server/app/(dashboard)/dashboard/media-providers/web/page_client-reference-manifest.js +1 -1
  20. package/app/.next-cli-build/server/app/(dashboard)/dashboard/mitm/page_client-reference-manifest.js +1 -1
  21. package/app/.next-cli-build/server/app/(dashboard)/dashboard/page.js.nft.json +1 -1
  22. package/app/.next-cli-build/server/app/(dashboard)/dashboard/page_client-reference-manifest.js +1 -1
  23. package/app/.next-cli-build/server/app/(dashboard)/dashboard/profile/page_client-reference-manifest.js +1 -1
  24. package/app/.next-cli-build/server/app/(dashboard)/dashboard/prompt-logs/page_client-reference-manifest.js +1 -1
  25. package/app/.next-cli-build/server/app/(dashboard)/dashboard/provider-health/page_client-reference-manifest.js +1 -1
  26. package/app/.next-cli-build/server/app/(dashboard)/dashboard/providers/[id]/page_client-reference-manifest.js +1 -1
  27. package/app/.next-cli-build/server/app/(dashboard)/dashboard/providers/new/page_client-reference-manifest.js +1 -1
  28. package/app/.next-cli-build/server/app/(dashboard)/dashboard/providers/page_client-reference-manifest.js +1 -1
  29. package/app/.next-cli-build/server/app/(dashboard)/dashboard/proxy-pools/page_client-reference-manifest.js +1 -1
  30. package/app/.next-cli-build/server/app/(dashboard)/dashboard/quota/page_client-reference-manifest.js +1 -1
  31. package/app/.next-cli-build/server/app/(dashboard)/dashboard/skills/page_client-reference-manifest.js +1 -1
  32. package/app/.next-cli-build/server/app/(dashboard)/dashboard/translator/page_client-reference-manifest.js +1 -1
  33. package/app/.next-cli-build/server/app/(dashboard)/dashboard/usage/page_client-reference-manifest.js +1 -1
  34. package/app/.next-cli-build/server/app/_global-error/page_client-reference-manifest.js +1 -1
  35. package/app/.next-cli-build/server/app/_global-error.html +1 -1
  36. package/app/.next-cli-build/server/app/_global-error.rsc +1 -1
  37. package/app/.next-cli-build/server/app/_global-error.segments/_full.segment.rsc +1 -1
  38. package/app/.next-cli-build/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  39. package/app/.next-cli-build/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  40. package/app/.next-cli-build/server/app/_global-error.segments/_head.segment.rsc +1 -1
  41. package/app/.next-cli-build/server/app/_global-error.segments/_index.segment.rsc +1 -1
  42. package/app/.next-cli-build/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  43. package/app/.next-cli-build/server/app/_not-found/page_client-reference-manifest.js +1 -1
  44. package/app/.next-cli-build/server/app/_not-found.html +1 -1
  45. package/app/.next-cli-build/server/app/_not-found.rsc +3 -3
  46. package/app/.next-cli-build/server/app/_not-found.segments/_full.segment.rsc +3 -3
  47. package/app/.next-cli-build/server/app/_not-found.segments/_head.segment.rsc +1 -1
  48. package/app/.next-cli-build/server/app/_not-found.segments/_index.segment.rsc +3 -3
  49. package/app/.next-cli-build/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  50. package/app/.next-cli-build/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  51. package/app/.next-cli-build/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  52. package/app/.next-cli-build/server/app/api/auth/ldap/test/route.js.nft.json +1 -1
  53. package/app/.next-cli-build/server/app/api/auth/ldap/users/route.js.nft.json +1 -1
  54. package/app/.next-cli-build/server/app/api/auth/login/route.js.nft.json +1 -1
  55. package/app/.next-cli-build/server/app/api/auth/logout/route.js.nft.json +1 -1
  56. package/app/.next-cli-build/server/app/api/auth/oidc/callback/route.js.nft.json +1 -1
  57. package/app/.next-cli-build/server/app/api/auth/oidc/start/route.js.nft.json +1 -1
  58. package/app/.next-cli-build/server/app/api/auth/oidc/test/route.js.nft.json +1 -1
  59. package/app/.next-cli-build/server/app/api/auth/status/route.js.nft.json +1 -1
  60. package/app/.next-cli-build/server/app/api/cli-tools/all-statuses/route.js.nft.json +1 -1
  61. package/app/.next-cli-build/server/app/api/cli-tools/antigravity-mitm/alias/route.js.nft.json +1 -1
  62. package/app/.next-cli-build/server/app/api/cli-tools/antigravity-mitm/autostart/route.js.nft.json +1 -1
  63. package/app/.next-cli-build/server/app/api/cli-tools/antigravity-mitm/prompt-log-db/route.js.nft.json +1 -1
  64. package/app/.next-cli-build/server/app/api/cli-tools/antigravity-mitm/root-ca/route.js.nft.json +1 -1
  65. package/app/.next-cli-build/server/app/api/cli-tools/antigravity-mitm/route.js.nft.json +1 -1
  66. package/app/.next-cli-build/server/app/api/cli-tools/copilot-settings/route.js.nft.json +1 -1
  67. package/app/.next-cli-build/server/app/api/cli-tools/cowork-settings/route.js.nft.json +1 -1
  68. package/app/.next-cli-build/server/app/api/combos/[id]/route.js.nft.json +1 -1
  69. package/app/.next-cli-build/server/app/api/combos/route.js.nft.json +1 -1
  70. package/app/.next-cli-build/server/app/api/dashboard/prompt-logs/[id]/route.js.nft.json +1 -1
  71. package/app/.next-cli-build/server/app/api/dashboard/prompt-logs/options/route.js.nft.json +1 -1
  72. package/app/.next-cli-build/server/app/api/dashboard/prompt-logs/route.js.nft.json +1 -1
  73. package/app/.next-cli-build/server/app/api/keys/[id]/route.js.nft.json +1 -1
  74. package/app/.next-cli-build/server/app/api/keys/route.js +1 -1
  75. package/app/.next-cli-build/server/app/api/keys/route.js.nft.json +1 -1
  76. package/app/.next-cli-build/server/app/api/mcp/[plugin]/message/route.js.nft.json +1 -1
  77. package/app/.next-cli-build/server/app/api/mcp/[plugin]/sse/route.js.nft.json +1 -1
  78. package/app/.next-cli-build/server/app/api/media-providers/tts/deepgram/voices/route.js.nft.json +1 -1
  79. package/app/.next-cli-build/server/app/api/media-providers/tts/elevenlabs/voices/route.js.nft.json +1 -1
  80. package/app/.next-cli-build/server/app/api/media-providers/tts/inworld/voices/route.js.nft.json +1 -1
  81. package/app/.next-cli-build/server/app/api/media-providers/tts/minimax/voices/route.js.nft.json +1 -1
  82. package/app/.next-cli-build/server/app/api/models/alias/route.js.nft.json +1 -1
  83. package/app/.next-cli-build/server/app/api/models/availability/route.js.nft.json +1 -1
  84. package/app/.next-cli-build/server/app/api/models/custom/route.js.nft.json +1 -1
  85. package/app/.next-cli-build/server/app/api/models/disabled/route.js.nft.json +1 -1
  86. package/app/.next-cli-build/server/app/api/models/route.js +1 -1
  87. package/app/.next-cli-build/server/app/api/models/route.js.nft.json +1 -1
  88. package/app/.next-cli-build/server/app/api/models/test/route.js +1 -1
  89. package/app/.next-cli-build/server/app/api/models/test/route.js.nft.json +1 -1
  90. package/app/.next-cli-build/server/app/api/oauth/[provider]/[action]/route.js.nft.json +1 -1
  91. package/app/.next-cli-build/server/app/api/oauth/codex/import-token/route.js.nft.json +1 -1
  92. package/app/.next-cli-build/server/app/api/oauth/cursor/auto-import/route.js.nft.json +1 -1
  93. package/app/.next-cli-build/server/app/api/oauth/cursor/import/route.js.nft.json +1 -1
  94. package/app/.next-cli-build/server/app/api/oauth/gitlab/pat/route.js.nft.json +1 -1
  95. package/app/.next-cli-build/server/app/api/oauth/iflow/cookie/route.js.nft.json +1 -1
  96. package/app/.next-cli-build/server/app/api/oauth/kiro/import/route.js.nft.json +1 -1
  97. package/app/.next-cli-build/server/app/api/oauth/kiro/social-exchange/route.js.nft.json +1 -1
  98. package/app/.next-cli-build/server/app/api/pricing/route.js.nft.json +1 -1
  99. package/app/.next-cli-build/server/app/api/provider-health/route.js.nft.json +1 -1
  100. package/app/.next-cli-build/server/app/api/provider-nodes/[id]/route.js.nft.json +1 -1
  101. package/app/.next-cli-build/server/app/api/provider-nodes/route.js.nft.json +1 -1
  102. package/app/.next-cli-build/server/app/api/providers/[id]/models/route.js.nft.json +1 -1
  103. package/app/.next-cli-build/server/app/api/providers/[id]/route.js.nft.json +1 -1
  104. package/app/.next-cli-build/server/app/api/providers/[id]/test/route.js.nft.json +1 -1
  105. package/app/.next-cli-build/server/app/api/providers/[id]/test-models/route.js +1 -1
  106. package/app/.next-cli-build/server/app/api/providers/[id]/test-models/route.js.nft.json +1 -1
  107. package/app/.next-cli-build/server/app/api/providers/client/route.js.nft.json +1 -1
  108. package/app/.next-cli-build/server/app/api/providers/route.js +1 -1
  109. package/app/.next-cli-build/server/app/api/providers/route.js.nft.json +1 -1
  110. package/app/.next-cli-build/server/app/api/providers/test-batch/route.js.nft.json +1 -1
  111. package/app/.next-cli-build/server/app/api/providers/validate/route.js.nft.json +1 -1
  112. package/app/.next-cli-build/server/app/api/proxy-pools/[id]/route.js.nft.json +1 -1
  113. package/app/.next-cli-build/server/app/api/proxy-pools/[id]/test/route.js.nft.json +1 -1
  114. package/app/.next-cli-build/server/app/api/proxy-pools/cloudflare-deploy/route.js.nft.json +1 -1
  115. package/app/.next-cli-build/server/app/api/proxy-pools/deno-deploy/route.js.nft.json +1 -1
  116. package/app/.next-cli-build/server/app/api/proxy-pools/route.js.nft.json +1 -1
  117. package/app/.next-cli-build/server/app/api/proxy-pools/vercel-deploy/route.js.nft.json +1 -1
  118. package/app/.next-cli-build/server/app/api/settings/database/route.js.nft.json +1 -1
  119. package/app/.next-cli-build/server/app/api/settings/require-login/route.js.nft.json +1 -1
  120. package/app/.next-cli-build/server/app/api/settings/route.js.nft.json +1 -1
  121. package/app/.next-cli-build/server/app/api/translator/console-logs/route.js +1 -1
  122. package/app/.next-cli-build/server/app/api/translator/console-logs/stream/route.js +1 -1
  123. package/app/.next-cli-build/server/app/api/translator/send/route.js.nft.json +1 -1
  124. package/app/.next-cli-build/server/app/api/translator/translate/route.js.nft.json +1 -1
  125. package/app/.next-cli-build/server/app/api/tunnel/disable/route.js.nft.json +1 -1
  126. package/app/.next-cli-build/server/app/api/tunnel/enable/route.js.nft.json +1 -1
  127. package/app/.next-cli-build/server/app/api/tunnel/status/route.js.nft.json +1 -1
  128. package/app/.next-cli-build/server/app/api/tunnel/tailscale-check/route.js.nft.json +1 -1
  129. package/app/.next-cli-build/server/app/api/tunnel/tailscale-disable/route.js.nft.json +1 -1
  130. package/app/.next-cli-build/server/app/api/tunnel/tailscale-enable/route.js.nft.json +1 -1
  131. package/app/.next-cli-build/server/app/api/tunnel/tailscale-install/route.js.nft.json +1 -1
  132. package/app/.next-cli-build/server/app/api/tunnel/tailscale-login/route.js.nft.json +1 -1
  133. package/app/.next-cli-build/server/app/api/tunnel/tailscale-start-daemon/route.js.nft.json +1 -1
  134. package/app/.next-cli-build/server/app/api/usage/[connectionId]/route.js.nft.json +1 -1
  135. package/app/.next-cli-build/server/app/api/usage/chart/route.js.nft.json +1 -1
  136. package/app/.next-cli-build/server/app/api/usage/history/route.js.nft.json +1 -1
  137. package/app/.next-cli-build/server/app/api/usage/providers/route.js.nft.json +1 -1
  138. package/app/.next-cli-build/server/app/api/usage/request-details/route.js.nft.json +1 -1
  139. package/app/.next-cli-build/server/app/api/usage/request-logs/route.js.nft.json +1 -1
  140. package/app/.next-cli-build/server/app/api/usage/stats/route.js.nft.json +1 -1
  141. package/app/.next-cli-build/server/app/api/usage/stream/route.js.nft.json +1 -1
  142. package/app/.next-cli-build/server/app/api/v1/api/chat/route.js.nft.json +1 -1
  143. package/app/.next-cli-build/server/app/api/v1/audio/speech/route.js.nft.json +1 -1
  144. package/app/.next-cli-build/server/app/api/v1/audio/transcriptions/route.js.nft.json +1 -1
  145. package/app/.next-cli-build/server/app/api/v1/chat/completions/route.js.nft.json +1 -1
  146. package/app/.next-cli-build/server/app/api/v1/embeddings/route.js.nft.json +1 -1
  147. package/app/.next-cli-build/server/app/api/v1/images/generations/route.js.nft.json +1 -1
  148. package/app/.next-cli-build/server/app/api/v1/messages/route.js.nft.json +1 -1
  149. package/app/.next-cli-build/server/app/api/v1/models/[kind]/route.js.nft.json +1 -1
  150. package/app/.next-cli-build/server/app/api/v1/models/route.js.nft.json +1 -1
  151. package/app/.next-cli-build/server/app/api/v1/responses/compact/route.js.nft.json +1 -1
  152. package/app/.next-cli-build/server/app/api/v1/responses/route.js.nft.json +1 -1
  153. package/app/.next-cli-build/server/app/api/v1/route.js.nft.json +1 -1
  154. package/app/.next-cli-build/server/app/api/v1/search/route.js.nft.json +1 -1
  155. package/app/.next-cli-build/server/app/api/v1/web/fetch/route.js.nft.json +1 -1
  156. package/app/.next-cli-build/server/app/api/v1beta/models/[...path]/route.js.nft.json +1 -1
  157. package/app/.next-cli-build/server/app/api/v1beta/models/route.js.nft.json +1 -1
  158. package/app/.next-cli-build/server/app/api/version/route.js +1 -1
  159. package/app/.next-cli-build/server/app/api/version/shutdown/route.js +1 -1
  160. package/app/.next-cli-build/server/app/api/version/shutdown/route.js.nft.json +1 -1
  161. package/app/.next-cli-build/server/app/api/version/update/route.js +1 -1
  162. package/app/.next-cli-build/server/app/api/version/update/route.js.nft.json +1 -1
  163. package/app/.next-cli-build/server/app/callback/page_client-reference-manifest.js +1 -1
  164. package/app/.next-cli-build/server/app/callback.html +1 -1
  165. package/app/.next-cli-build/server/app/callback.rsc +3 -3
  166. package/app/.next-cli-build/server/app/callback.segments/_full.segment.rsc +3 -3
  167. package/app/.next-cli-build/server/app/callback.segments/_head.segment.rsc +1 -1
  168. package/app/.next-cli-build/server/app/callback.segments/_index.segment.rsc +3 -3
  169. package/app/.next-cli-build/server/app/callback.segments/_tree.segment.rsc +1 -1
  170. package/app/.next-cli-build/server/app/callback.segments/callback/__PAGE__.segment.rsc +1 -1
  171. package/app/.next-cli-build/server/app/callback.segments/callback.segment.rsc +1 -1
  172. package/app/.next-cli-build/server/app/dashboard/api-keys.html +1 -1
  173. package/app/.next-cli-build/server/app/dashboard/api-keys.rsc +5 -5
  174. package/app/.next-cli-build/server/app/dashboard/api-keys.segments/!KGRhc2hib2FyZCk/dashboard/api-keys/__PAGE__.segment.rsc +2 -2
  175. package/app/.next-cli-build/server/app/dashboard/api-keys.segments/!KGRhc2hib2FyZCk/dashboard/api-keys.segment.rsc +1 -1
  176. package/app/.next-cli-build/server/app/dashboard/api-keys.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  177. package/app/.next-cli-build/server/app/dashboard/api-keys.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  178. package/app/.next-cli-build/server/app/dashboard/api-keys.segments/_full.segment.rsc +5 -5
  179. package/app/.next-cli-build/server/app/dashboard/api-keys.segments/_head.segment.rsc +1 -1
  180. package/app/.next-cli-build/server/app/dashboard/api-keys.segments/_index.segment.rsc +3 -3
  181. package/app/.next-cli-build/server/app/dashboard/api-keys.segments/_tree.segment.rsc +1 -1
  182. package/app/.next-cli-build/server/app/dashboard/basic-chat.html +1 -1
  183. package/app/.next-cli-build/server/app/dashboard/basic-chat.rsc +5 -5
  184. package/app/.next-cli-build/server/app/dashboard/basic-chat.segments/!KGRhc2hib2FyZCk/dashboard/basic-chat/__PAGE__.segment.rsc +2 -2
  185. package/app/.next-cli-build/server/app/dashboard/basic-chat.segments/!KGRhc2hib2FyZCk/dashboard/basic-chat.segment.rsc +1 -1
  186. package/app/.next-cli-build/server/app/dashboard/basic-chat.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  187. package/app/.next-cli-build/server/app/dashboard/basic-chat.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  188. package/app/.next-cli-build/server/app/dashboard/basic-chat.segments/_full.segment.rsc +5 -5
  189. package/app/.next-cli-build/server/app/dashboard/basic-chat.segments/_head.segment.rsc +1 -1
  190. package/app/.next-cli-build/server/app/dashboard/basic-chat.segments/_index.segment.rsc +3 -3
  191. package/app/.next-cli-build/server/app/dashboard/basic-chat.segments/_tree.segment.rsc +1 -1
  192. package/app/.next-cli-build/server/app/dashboard/cli-tools.html +1 -1
  193. package/app/.next-cli-build/server/app/dashboard/cli-tools.rsc +5 -5
  194. package/app/.next-cli-build/server/app/dashboard/cli-tools.segments/!KGRhc2hib2FyZCk/dashboard/cli-tools/__PAGE__.segment.rsc +2 -2
  195. package/app/.next-cli-build/server/app/dashboard/cli-tools.segments/!KGRhc2hib2FyZCk/dashboard/cli-tools.segment.rsc +1 -1
  196. package/app/.next-cli-build/server/app/dashboard/cli-tools.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  197. package/app/.next-cli-build/server/app/dashboard/cli-tools.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  198. package/app/.next-cli-build/server/app/dashboard/cli-tools.segments/_full.segment.rsc +5 -5
  199. package/app/.next-cli-build/server/app/dashboard/cli-tools.segments/_head.segment.rsc +1 -1
  200. package/app/.next-cli-build/server/app/dashboard/cli-tools.segments/_index.segment.rsc +3 -3
  201. package/app/.next-cli-build/server/app/dashboard/cli-tools.segments/_tree.segment.rsc +1 -1
  202. package/app/.next-cli-build/server/app/dashboard/combos.html +1 -1
  203. package/app/.next-cli-build/server/app/dashboard/combos.rsc +5 -5
  204. package/app/.next-cli-build/server/app/dashboard/combos.segments/!KGRhc2hib2FyZCk/dashboard/combos/__PAGE__.segment.rsc +2 -2
  205. package/app/.next-cli-build/server/app/dashboard/combos.segments/!KGRhc2hib2FyZCk/dashboard/combos.segment.rsc +1 -1
  206. package/app/.next-cli-build/server/app/dashboard/combos.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  207. package/app/.next-cli-build/server/app/dashboard/combos.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  208. package/app/.next-cli-build/server/app/dashboard/combos.segments/_full.segment.rsc +5 -5
  209. package/app/.next-cli-build/server/app/dashboard/combos.segments/_head.segment.rsc +1 -1
  210. package/app/.next-cli-build/server/app/dashboard/combos.segments/_index.segment.rsc +3 -3
  211. package/app/.next-cli-build/server/app/dashboard/combos.segments/_tree.segment.rsc +1 -1
  212. package/app/.next-cli-build/server/app/dashboard/endpoint.html +1 -1
  213. package/app/.next-cli-build/server/app/dashboard/endpoint.rsc +6 -6
  214. package/app/.next-cli-build/server/app/dashboard/endpoint.segments/!KGRhc2hib2FyZCk/dashboard/endpoint/__PAGE__.segment.rsc +3 -3
  215. package/app/.next-cli-build/server/app/dashboard/endpoint.segments/!KGRhc2hib2FyZCk/dashboard/endpoint.segment.rsc +1 -1
  216. package/app/.next-cli-build/server/app/dashboard/endpoint.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  217. package/app/.next-cli-build/server/app/dashboard/endpoint.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  218. package/app/.next-cli-build/server/app/dashboard/endpoint.segments/_full.segment.rsc +6 -6
  219. package/app/.next-cli-build/server/app/dashboard/endpoint.segments/_head.segment.rsc +1 -1
  220. package/app/.next-cli-build/server/app/dashboard/endpoint.segments/_index.segment.rsc +3 -3
  221. package/app/.next-cli-build/server/app/dashboard/endpoint.segments/_tree.segment.rsc +1 -1
  222. package/app/.next-cli-build/server/app/dashboard/media-providers/web.html +1 -1
  223. package/app/.next-cli-build/server/app/dashboard/media-providers/web.rsc +5 -5
  224. package/app/.next-cli-build/server/app/dashboard/media-providers/web.segments/!KGRhc2hib2FyZCk/dashboard/media-providers/web/__PAGE__.segment.rsc +2 -2
  225. package/app/.next-cli-build/server/app/dashboard/media-providers/web.segments/!KGRhc2hib2FyZCk/dashboard/media-providers/web.segment.rsc +1 -1
  226. package/app/.next-cli-build/server/app/dashboard/media-providers/web.segments/!KGRhc2hib2FyZCk/dashboard/media-providers.segment.rsc +1 -1
  227. package/app/.next-cli-build/server/app/dashboard/media-providers/web.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  228. package/app/.next-cli-build/server/app/dashboard/media-providers/web.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  229. package/app/.next-cli-build/server/app/dashboard/media-providers/web.segments/_full.segment.rsc +5 -5
  230. package/app/.next-cli-build/server/app/dashboard/media-providers/web.segments/_head.segment.rsc +1 -1
  231. package/app/.next-cli-build/server/app/dashboard/media-providers/web.segments/_index.segment.rsc +3 -3
  232. package/app/.next-cli-build/server/app/dashboard/media-providers/web.segments/_tree.segment.rsc +1 -1
  233. package/app/.next-cli-build/server/app/dashboard/mitm.html +1 -1
  234. package/app/.next-cli-build/server/app/dashboard/mitm.rsc +5 -5
  235. package/app/.next-cli-build/server/app/dashboard/mitm.segments/!KGRhc2hib2FyZCk/dashboard/mitm/__PAGE__.segment.rsc +2 -2
  236. package/app/.next-cli-build/server/app/dashboard/mitm.segments/!KGRhc2hib2FyZCk/dashboard/mitm.segment.rsc +1 -1
  237. package/app/.next-cli-build/server/app/dashboard/mitm.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  238. package/app/.next-cli-build/server/app/dashboard/mitm.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  239. package/app/.next-cli-build/server/app/dashboard/mitm.segments/_full.segment.rsc +5 -5
  240. package/app/.next-cli-build/server/app/dashboard/mitm.segments/_head.segment.rsc +1 -1
  241. package/app/.next-cli-build/server/app/dashboard/mitm.segments/_index.segment.rsc +3 -3
  242. package/app/.next-cli-build/server/app/dashboard/mitm.segments/_tree.segment.rsc +1 -1
  243. package/app/.next-cli-build/server/app/dashboard/profile.html +1 -1
  244. package/app/.next-cli-build/server/app/dashboard/profile.rsc +4 -4
  245. package/app/.next-cli-build/server/app/dashboard/profile.segments/!KGRhc2hib2FyZCk/dashboard/profile/__PAGE__.segment.rsc +1 -1
  246. package/app/.next-cli-build/server/app/dashboard/profile.segments/!KGRhc2hib2FyZCk/dashboard/profile.segment.rsc +1 -1
  247. package/app/.next-cli-build/server/app/dashboard/profile.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  248. package/app/.next-cli-build/server/app/dashboard/profile.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  249. package/app/.next-cli-build/server/app/dashboard/profile.segments/_full.segment.rsc +4 -4
  250. package/app/.next-cli-build/server/app/dashboard/profile.segments/_head.segment.rsc +1 -1
  251. package/app/.next-cli-build/server/app/dashboard/profile.segments/_index.segment.rsc +3 -3
  252. package/app/.next-cli-build/server/app/dashboard/profile.segments/_tree.segment.rsc +1 -1
  253. package/app/.next-cli-build/server/app/dashboard/prompt-logs.html +1 -1
  254. package/app/.next-cli-build/server/app/dashboard/prompt-logs.rsc +4 -4
  255. package/app/.next-cli-build/server/app/dashboard/prompt-logs.segments/!KGRhc2hib2FyZCk/dashboard/prompt-logs/__PAGE__.segment.rsc +1 -1
  256. package/app/.next-cli-build/server/app/dashboard/prompt-logs.segments/!KGRhc2hib2FyZCk/dashboard/prompt-logs.segment.rsc +1 -1
  257. package/app/.next-cli-build/server/app/dashboard/prompt-logs.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  258. package/app/.next-cli-build/server/app/dashboard/prompt-logs.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  259. package/app/.next-cli-build/server/app/dashboard/prompt-logs.segments/_full.segment.rsc +4 -4
  260. package/app/.next-cli-build/server/app/dashboard/prompt-logs.segments/_head.segment.rsc +1 -1
  261. package/app/.next-cli-build/server/app/dashboard/prompt-logs.segments/_index.segment.rsc +3 -3
  262. package/app/.next-cli-build/server/app/dashboard/prompt-logs.segments/_tree.segment.rsc +1 -1
  263. package/app/.next-cli-build/server/app/dashboard/provider-health.html +1 -1
  264. package/app/.next-cli-build/server/app/dashboard/provider-health.rsc +4 -4
  265. package/app/.next-cli-build/server/app/dashboard/provider-health.segments/!KGRhc2hib2FyZCk/dashboard/provider-health/__PAGE__.segment.rsc +1 -1
  266. package/app/.next-cli-build/server/app/dashboard/provider-health.segments/!KGRhc2hib2FyZCk/dashboard/provider-health.segment.rsc +1 -1
  267. package/app/.next-cli-build/server/app/dashboard/provider-health.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  268. package/app/.next-cli-build/server/app/dashboard/provider-health.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  269. package/app/.next-cli-build/server/app/dashboard/provider-health.segments/_full.segment.rsc +4 -4
  270. package/app/.next-cli-build/server/app/dashboard/provider-health.segments/_head.segment.rsc +1 -1
  271. package/app/.next-cli-build/server/app/dashboard/provider-health.segments/_index.segment.rsc +3 -3
  272. package/app/.next-cli-build/server/app/dashboard/provider-health.segments/_tree.segment.rsc +1 -1
  273. package/app/.next-cli-build/server/app/dashboard/providers/new.html +1 -1
  274. package/app/.next-cli-build/server/app/dashboard/providers/new.rsc +5 -5
  275. package/app/.next-cli-build/server/app/dashboard/providers/new.segments/!KGRhc2hib2FyZCk/dashboard/providers/new/__PAGE__.segment.rsc +2 -2
  276. package/app/.next-cli-build/server/app/dashboard/providers/new.segments/!KGRhc2hib2FyZCk/dashboard/providers/new.segment.rsc +1 -1
  277. package/app/.next-cli-build/server/app/dashboard/providers/new.segments/!KGRhc2hib2FyZCk/dashboard/providers.segment.rsc +1 -1
  278. package/app/.next-cli-build/server/app/dashboard/providers/new.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  279. package/app/.next-cli-build/server/app/dashboard/providers/new.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  280. package/app/.next-cli-build/server/app/dashboard/providers/new.segments/_full.segment.rsc +5 -5
  281. package/app/.next-cli-build/server/app/dashboard/providers/new.segments/_head.segment.rsc +1 -1
  282. package/app/.next-cli-build/server/app/dashboard/providers/new.segments/_index.segment.rsc +3 -3
  283. package/app/.next-cli-build/server/app/dashboard/providers/new.segments/_tree.segment.rsc +1 -1
  284. package/app/.next-cli-build/server/app/dashboard/providers.html +1 -1
  285. package/app/.next-cli-build/server/app/dashboard/providers.rsc +5 -5
  286. package/app/.next-cli-build/server/app/dashboard/providers.segments/!KGRhc2hib2FyZCk/dashboard/providers/__PAGE__.segment.rsc +2 -2
  287. package/app/.next-cli-build/server/app/dashboard/providers.segments/!KGRhc2hib2FyZCk/dashboard/providers.segment.rsc +1 -1
  288. package/app/.next-cli-build/server/app/dashboard/providers.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  289. package/app/.next-cli-build/server/app/dashboard/providers.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  290. package/app/.next-cli-build/server/app/dashboard/providers.segments/_full.segment.rsc +5 -5
  291. package/app/.next-cli-build/server/app/dashboard/providers.segments/_head.segment.rsc +1 -1
  292. package/app/.next-cli-build/server/app/dashboard/providers.segments/_index.segment.rsc +3 -3
  293. package/app/.next-cli-build/server/app/dashboard/providers.segments/_tree.segment.rsc +1 -1
  294. package/app/.next-cli-build/server/app/dashboard/proxy-pools.html +1 -1
  295. package/app/.next-cli-build/server/app/dashboard/proxy-pools.rsc +5 -5
  296. package/app/.next-cli-build/server/app/dashboard/proxy-pools.segments/!KGRhc2hib2FyZCk/dashboard/proxy-pools/__PAGE__.segment.rsc +2 -2
  297. package/app/.next-cli-build/server/app/dashboard/proxy-pools.segments/!KGRhc2hib2FyZCk/dashboard/proxy-pools.segment.rsc +1 -1
  298. package/app/.next-cli-build/server/app/dashboard/proxy-pools.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  299. package/app/.next-cli-build/server/app/dashboard/proxy-pools.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  300. package/app/.next-cli-build/server/app/dashboard/proxy-pools.segments/_full.segment.rsc +5 -5
  301. package/app/.next-cli-build/server/app/dashboard/proxy-pools.segments/_head.segment.rsc +1 -1
  302. package/app/.next-cli-build/server/app/dashboard/proxy-pools.segments/_index.segment.rsc +3 -3
  303. package/app/.next-cli-build/server/app/dashboard/proxy-pools.segments/_tree.segment.rsc +1 -1
  304. package/app/.next-cli-build/server/app/dashboard/quota.html +1 -1
  305. package/app/.next-cli-build/server/app/dashboard/quota.rsc +6 -6
  306. package/app/.next-cli-build/server/app/dashboard/quota.segments/!KGRhc2hib2FyZCk/dashboard/quota/__PAGE__.segment.rsc +3 -3
  307. package/app/.next-cli-build/server/app/dashboard/quota.segments/!KGRhc2hib2FyZCk/dashboard/quota.segment.rsc +1 -1
  308. package/app/.next-cli-build/server/app/dashboard/quota.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  309. package/app/.next-cli-build/server/app/dashboard/quota.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  310. package/app/.next-cli-build/server/app/dashboard/quota.segments/_full.segment.rsc +6 -6
  311. package/app/.next-cli-build/server/app/dashboard/quota.segments/_head.segment.rsc +1 -1
  312. package/app/.next-cli-build/server/app/dashboard/quota.segments/_index.segment.rsc +3 -3
  313. package/app/.next-cli-build/server/app/dashboard/quota.segments/_tree.segment.rsc +1 -1
  314. package/app/.next-cli-build/server/app/dashboard/settings/pricing/page_client-reference-manifest.js +1 -1
  315. package/app/.next-cli-build/server/app/dashboard/settings/pricing.html +1 -1
  316. package/app/.next-cli-build/server/app/dashboard/settings/pricing.rsc +3 -3
  317. package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/_full.segment.rsc +3 -3
  318. package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/_head.segment.rsc +1 -1
  319. package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/_index.segment.rsc +3 -3
  320. package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/_tree.segment.rsc +1 -1
  321. package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/dashboard/settings/pricing/__PAGE__.segment.rsc +1 -1
  322. package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/dashboard/settings/pricing.segment.rsc +1 -1
  323. package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/dashboard/settings.segment.rsc +1 -1
  324. package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/dashboard.segment.rsc +1 -1
  325. package/app/.next-cli-build/server/app/dashboard/skills.html +1 -1
  326. package/app/.next-cli-build/server/app/dashboard/skills.rsc +5 -5
  327. package/app/.next-cli-build/server/app/dashboard/skills.segments/!KGRhc2hib2FyZCk/dashboard/skills/__PAGE__.segment.rsc +2 -2
  328. package/app/.next-cli-build/server/app/dashboard/skills.segments/!KGRhc2hib2FyZCk/dashboard/skills.segment.rsc +1 -1
  329. package/app/.next-cli-build/server/app/dashboard/skills.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  330. package/app/.next-cli-build/server/app/dashboard/skills.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  331. package/app/.next-cli-build/server/app/dashboard/skills.segments/_full.segment.rsc +5 -5
  332. package/app/.next-cli-build/server/app/dashboard/skills.segments/_head.segment.rsc +1 -1
  333. package/app/.next-cli-build/server/app/dashboard/skills.segments/_index.segment.rsc +3 -3
  334. package/app/.next-cli-build/server/app/dashboard/skills.segments/_tree.segment.rsc +1 -1
  335. package/app/.next-cli-build/server/app/dashboard/translator.html +1 -1
  336. package/app/.next-cli-build/server/app/dashboard/translator.rsc +5 -5
  337. package/app/.next-cli-build/server/app/dashboard/translator.segments/!KGRhc2hib2FyZCk/dashboard/translator/__PAGE__.segment.rsc +2 -2
  338. package/app/.next-cli-build/server/app/dashboard/translator.segments/!KGRhc2hib2FyZCk/dashboard/translator.segment.rsc +1 -1
  339. package/app/.next-cli-build/server/app/dashboard/translator.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  340. package/app/.next-cli-build/server/app/dashboard/translator.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  341. package/app/.next-cli-build/server/app/dashboard/translator.segments/_full.segment.rsc +5 -5
  342. package/app/.next-cli-build/server/app/dashboard/translator.segments/_head.segment.rsc +1 -1
  343. package/app/.next-cli-build/server/app/dashboard/translator.segments/_index.segment.rsc +3 -3
  344. package/app/.next-cli-build/server/app/dashboard/translator.segments/_tree.segment.rsc +1 -1
  345. package/app/.next-cli-build/server/app/dashboard/usage.html +1 -1
  346. package/app/.next-cli-build/server/app/dashboard/usage.rsc +5 -5
  347. package/app/.next-cli-build/server/app/dashboard/usage.segments/!KGRhc2hib2FyZCk/dashboard/usage/__PAGE__.segment.rsc +2 -2
  348. package/app/.next-cli-build/server/app/dashboard/usage.segments/!KGRhc2hib2FyZCk/dashboard/usage.segment.rsc +1 -1
  349. package/app/.next-cli-build/server/app/dashboard/usage.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  350. package/app/.next-cli-build/server/app/dashboard/usage.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  351. package/app/.next-cli-build/server/app/dashboard/usage.segments/_full.segment.rsc +5 -5
  352. package/app/.next-cli-build/server/app/dashboard/usage.segments/_head.segment.rsc +1 -1
  353. package/app/.next-cli-build/server/app/dashboard/usage.segments/_index.segment.rsc +3 -3
  354. package/app/.next-cli-build/server/app/dashboard/usage.segments/_tree.segment.rsc +1 -1
  355. package/app/.next-cli-build/server/app/dashboard.html +1 -1
  356. package/app/.next-cli-build/server/app/dashboard.rsc +5 -5
  357. package/app/.next-cli-build/server/app/dashboard.segments/!KGRhc2hib2FyZCk/dashboard/__PAGE__.segment.rsc +2 -2
  358. package/app/.next-cli-build/server/app/dashboard.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  359. package/app/.next-cli-build/server/app/dashboard.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  360. package/app/.next-cli-build/server/app/dashboard.segments/_full.segment.rsc +5 -5
  361. package/app/.next-cli-build/server/app/dashboard.segments/_head.segment.rsc +1 -1
  362. package/app/.next-cli-build/server/app/dashboard.segments/_index.segment.rsc +3 -3
  363. package/app/.next-cli-build/server/app/dashboard.segments/_tree.segment.rsc +1 -1
  364. package/app/.next-cli-build/server/app/index.html +1 -1
  365. package/app/.next-cli-build/server/app/index.rsc +3 -3
  366. package/app/.next-cli-build/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  367. package/app/.next-cli-build/server/app/index.segments/_full.segment.rsc +3 -3
  368. package/app/.next-cli-build/server/app/index.segments/_head.segment.rsc +1 -1
  369. package/app/.next-cli-build/server/app/index.segments/_index.segment.rsc +3 -3
  370. package/app/.next-cli-build/server/app/index.segments/_tree.segment.rsc +1 -1
  371. package/app/.next-cli-build/server/app/landing/page_client-reference-manifest.js +1 -1
  372. package/app/.next-cli-build/server/app/landing.html +1 -1
  373. package/app/.next-cli-build/server/app/landing.rsc +3 -3
  374. package/app/.next-cli-build/server/app/landing.segments/_full.segment.rsc +3 -3
  375. package/app/.next-cli-build/server/app/landing.segments/_head.segment.rsc +1 -1
  376. package/app/.next-cli-build/server/app/landing.segments/_index.segment.rsc +3 -3
  377. package/app/.next-cli-build/server/app/landing.segments/_tree.segment.rsc +1 -1
  378. package/app/.next-cli-build/server/app/landing.segments/landing/__PAGE__.segment.rsc +1 -1
  379. package/app/.next-cli-build/server/app/landing.segments/landing.segment.rsc +1 -1
  380. package/app/.next-cli-build/server/app/login/page_client-reference-manifest.js +1 -1
  381. package/app/.next-cli-build/server/app/login.html +1 -1
  382. package/app/.next-cli-build/server/app/login.rsc +3 -3
  383. package/app/.next-cli-build/server/app/login.segments/_full.segment.rsc +3 -3
  384. package/app/.next-cli-build/server/app/login.segments/_head.segment.rsc +1 -1
  385. package/app/.next-cli-build/server/app/login.segments/_index.segment.rsc +3 -3
  386. package/app/.next-cli-build/server/app/login.segments/_tree.segment.rsc +1 -1
  387. package/app/.next-cli-build/server/app/login.segments/login/__PAGE__.segment.rsc +1 -1
  388. package/app/.next-cli-build/server/app/login.segments/login.segment.rsc +1 -1
  389. package/app/.next-cli-build/server/app/page_client-reference-manifest.js +1 -1
  390. package/app/.next-cli-build/server/app-paths-manifest.json +3 -3
  391. package/app/.next-cli-build/server/chunks/2496.js +1 -1
  392. package/app/.next-cli-build/server/chunks/3110.js +1 -1
  393. package/app/.next-cli-build/server/chunks/4746.js +1 -1
  394. package/app/.next-cli-build/server/chunks/7130.js +1 -1
  395. package/app/.next-cli-build/server/chunks/7153.js +1 -1
  396. package/app/.next-cli-build/server/middleware-build-manifest.js +1 -1
  397. package/app/.next-cli-build/server/pages/404.html +1 -1
  398. package/app/.next-cli-build/server/pages/500.html +1 -1
  399. package/app/.next-cli-build/static/chunks/{1321-7ea088790c89505d.js → 1321-464567b4573f6085.js} +1 -1
  400. package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/api-keys/page-b617fab6cd6ae253.js +1 -0
  401. package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/combos/page-9d08502839471987.js +1 -0
  402. package/app/cli/.build-home/AppData/Roaming/bluerouter/db/data.sqlite-shm +0 -0
  403. package/app/cli/.build-home/AppData/Roaming/bluerouter/db/data.sqlite-wal +0 -0
  404. package/app/package.json +1 -1
  405. package/package.json +1 -1
  406. package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/api-keys/page-bec8eaff7440b8c7.js +0 -1
  407. package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/combos/page-82be3137baaf2025.js +0 -1
  408. /package/app/.next-cli-build/static/{PoyNsanu7MLWpwd_H-pe4 → OL2EoAoYy9N8kjE3nAQAA}/_buildManifest.js +0 -0
  409. /package/app/.next-cli-build/static/{PoyNsanu7MLWpwd_H-pe4 → OL2EoAoYy9N8kjE3nAQAA}/_ssgManifest.js +0 -0
  410. /package/app/cli/.build-home/AppData/Roaming/bluerouter/db/backups/{upgrade-1.0.115-to-1.0.117-1.0.117-20260609-215212 → upgrade-1.0.121-to-1.0.122-1.0.122-20260609-230221}/data.sqlite +0 -0
  411. /package/app/cli/.build-home/AppData/Roaming/bluerouter/db/backups/{upgrade-1.0.117-to-1.0.118-1.0.118-20260609-222041 → upgrade-1.0.122-to-1.0.123-1.0.123-20260609-231344}/data.sqlite +0 -0
@@ -0,0 +1 @@
1
+ (self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[7593],{56484:(e,t,s)=>{"use strict";s.d(t,{default:()=>m});var a=s(95155),l=s(12115),n=s(35497),r=s(11059);function i(e){return String(e||"").trim().toLowerCase()}function d(e){return Array.isArray(e)?[...new Set(e.map(e=>String(e||"").trim()).filter(Boolean))]:[]}function c({models:e,emptyLabel:t="Chưa g\xe1n model"}){let s=d(e);return 0===s.length?(0,a.jsx)("span",{className:"rounded-full bg-amber-500/10 px-2 py-0.5 text-xs text-amber-700 dark:text-amber-300",children:t}):(0,a.jsxs)("div",{className:"flex flex-wrap gap-1.5",children:[s.slice(0,8).map(e=>(0,a.jsx)("span",{className:"rounded-full border border-border bg-surface px-2 py-1 font-mono text-[11px] text-text-main",children:e},e)),s.length>8?(0,a.jsxs)("span",{className:"rounded-full bg-primary/10 px-2 py-1 text-[11px] text-primary",children:["+",s.length-8," model kh\xe1c"]}):null]})}function o({open:e,keyName:t,models:s,setModels:r,activeProviders:i,modelAliases:c,onClose:m,onSave:x,saving:h}){let[u,p]=(0,l.useState)(!1),y=d(s),g=e=>{let t=e?.value||e?.name||e;r(e=>d(e).filter(e=>e!==t))};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(n.aF,{isOpen:e,onClose:m,title:`Sửa quyền truy cập model${t?`: ${t}`:""}`,children:(0,a.jsxs)("div",{className:"space-y-4",children:[(0,a.jsx)("div",{className:"rounded-xl border border-blue-200 bg-blue-50 px-3 py-2 text-sm text-blue-700 dark:border-blue-500/30 dark:bg-blue-500/10 dark:text-blue-300",children:"Chọn một hoặc nhiều model m\xe0 API key n\xe0y được ph\xe9p sử dụng. Trang n\xe0y kh\xf4ng cho lưu danh s\xe1ch model rỗng."}),(0,a.jsxs)("div",{className:"space-y-2",children:[(0,a.jsxs)("div",{className:"flex items-center justify-between gap-2",children:[(0,a.jsx)("label",{className:"text-sm font-medium text-text-main",children:"Model được ph\xe9p d\xf9ng"}),(0,a.jsxs)(n.$n,{variant:"secondary",onClick:()=>p(!0),children:[(0,a.jsx)("span",{className:"material-symbols-outlined mr-1 text-[18px]",children:"add"}),"Th\xeam model"]})]}),(0,a.jsx)("div",{className:"min-h-[76px] rounded-xl border border-border bg-background p-3",children:0===y.length?(0,a.jsx)("div",{className:"text-sm text-text-muted",children:"Chưa chọn model n\xe0o. H\xe3y th\xeam \xedt nhất một model trước khi lưu."}):(0,a.jsx)("div",{className:"flex flex-wrap gap-2",children:y.map(e=>(0,a.jsxs)("button",{type:"button",onClick:()=>g(e),className:"rounded-full border border-primary/30 bg-primary/10 px-2 py-1 font-mono text-xs text-primary transition hover:bg-red-500/10 hover:text-red-600",title:"Bấm để gỡ model n\xe0y",children:[e," ",(0,a.jsx)("span",{className:"ml-1",children:"\xd7"})]},e))})})]}),(0,a.jsxs)("div",{className:"flex justify-end gap-2",children:[(0,a.jsx)(n.$n,{variant:"secondary",onClick:m,children:"Hủy"}),(0,a.jsx)(n.$n,{onClick:x,disabled:h||0===y.length,children:h?"Đang lưu...":"Lưu danh s\xe1ch model"})]})]})}),(0,a.jsx)(n.rq,{isOpen:u,onClose:()=>p(!1),onSelect:e=>{let t=e?.value||e?.name||e;t&&r(e=>d([...e,t]))},onDeselect:g,activeProviders:i,modelAliases:c,title:"Chọn model được ph\xe9p d\xf9ng",addedModelValues:y,closeOnSelect:!1})]})}function m(){let[e,t]=(0,l.useState)(null),[s,m]=(0,l.useState)([]),[x,h]=(0,l.useState)(!0),[u,p]=(0,l.useState)(""),[y,g]=(0,l.useState)(!1),[b,f]=(0,l.useState)(""),[j,k]=(0,l.useState)(""),[v,N]=(0,l.useState)(""),[w,C]=(0,l.useState)(""),[A,S]=(0,l.useState)(new Set),[P,I]=(0,l.useState)(null),[$,T]=(0,l.useState)([]),[K,M]=(0,l.useState)(""),[O,D]=(0,l.useState)(!1),[L,_]=(0,l.useState)(!1),[E,q]=(0,l.useState)(""),[B,H]=(0,l.useState)(""),[U,F]=(0,l.useState)(!1),[G,V]=(0,l.useState)(!1),[Z,J]=(0,l.useState)([]),[X,Q]=(0,l.useState)({}),[R,z]=(0,l.useState)(null),[W,Y]=(0,l.useState)([]),[ee,et]=(0,l.useState)(!1),[es,ea]=(0,l.useState)(5),[el,en]=(0,l.useState)(0),{copied:er,copy:ei}=(0,r.C)(),ed=e?.isAdmin===!0,ec=e?.userId||"",eo=Number.isFinite(Number(es))?Number(es):5,em=Number.isFinite(Number(el))?Number(el):s.length,ex=!ed&&em>=eo,eh=(0,l.useMemo)(()=>{let e=new Map;for(let t of $){let s=i(t.username||t.email||t.userPrincipalName||t.sAMAccountName);s&&e.set(s,{id:s,label:`${t.displayName||t.username||s}${t.email?` \xb7 ${t.email}`:""}`})}for(let t of s){let s=i(t.ownerUser||t.createdByUser);s&&!e.has(s)&&e.set(s,{id:s,label:s})}return ec&&!e.has(ec)&&e.set(ec,{id:ec,label:`${ec} (t\xf4i)`}),[...e.values()].sort((e,t)=>e.label.localeCompare(t.label))},[$,s,ec]),eu=B!==E,ep=async()=>{let e=await fetch("/api/auth/status",{cache:"no-store"}),s=await e.json().catch(()=>({}));return t(s),!j&&s?.userId&&k(s.userId),s},ey=async()=>{h(!0),p("");try{let e=await fetch("/api/keys",{cache:"no-store"}),t=await e.json().catch(()=>({}));if(!e.ok)throw Error(t.error||"Kh\xf4ng tải được danh s\xe1ch API keys");let s=t.keys||[],a=String(t.apiKeyDefaultModel||"").trim();m(s),q(a),H(a),ea(t.apiKeyLimit??5),en(t.apiKeyCount??s.length)}catch(e){p(e.message||"Kh\xf4ng tải được danh s\xe1ch API keys")}finally{h(!1)}},eg=async()=>{try{let[e,t]=await Promise.allSettled([fetch("/api/providers",{cache:"no-store"}),fetch("/api/models/alias",{cache:"no-store"})]);if("fulfilled"===e.status&&e.value.ok){let t=await e.value.json().catch(()=>({}));J(t.connections||[])}if("fulfilled"===t.status&&t.value.ok){let e=await t.value.json().catch(()=>({}));Q(e.aliases||{})}}catch{}},eb=async(e=K)=>{if(ed){D(!0);try{let t=await fetch(`/api/auth/ldap/users?q=${encodeURIComponent(e||"")}`,{cache:"no-store"}),s=await t.json().catch(()=>({}));t.ok&&T(s.users||[])}catch{}finally{D(!1)}}};(0,l.useEffect)(()=>{let e=!1;return(async()=>{let t=await ep();e||await Promise.all([ey(),eg()]),e||t?.isAdmin!==!0||await eb("")})(),()=>{e=!0}},[]);let ef=async()=>{if(ed&&B.trim()){F(!0),p("");try{let e=await fetch("/api/keys",{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKeyDefaultModel:B.trim()})}),t=await e.json().catch(()=>({}));if(!e.ok)throw Error(t.error||"Kh\xf4ng lưu được default model");let s=String(t.apiKeyDefaultModel||"").trim();q(s),H(s)}catch(e){p(e.message||"Kh\xf4ng lưu được default model")}finally{F(!1)}}},ej=async()=>{if(b.trim()){if(!E.trim())return void p("Admin cần cấu h\xecnh default model trước khi tạo API key.");if(ex)return void p(`Mỗi user chỉ được tạo tối đa ${eo} API keys. Vui l\xf2ng x\xf3a bớt key cũ trước khi tạo key mới.`);_(!0),p("");try{let e=ed?i(v||j||ec):void 0,t=await fetch("/api/keys",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:b.trim(),ownerUser:e})}),s=await t.json().catch(()=>({}));if(!t.ok)throw Error(s.error||"Kh\xf4ng tạo được API key");C(s.key),f(""),N(""),g(!1),await ey()}catch(e){p(e.message||"Kh\xf4ng tạo được API key")}finally{_(!1)}}},ek=async(e,t)=>{let s=await fetch(`/api/keys/${e}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}),a=await s.json().catch(()=>({}));return s.ok?(await ey(),!0):(p(a.error||"Kh\xf4ng cập nhật được API key"),!1)},ev=async()=>{if(!R)return;let e=d(W);if(0===e.length)return void p("H\xe3y chọn \xedt nhất một model trước khi lưu quyền truy cập API key.");et(!0),p("");try{await ek(R.id,{allowedModels:e})&&(z(null),Y([]))}finally{et(!1)}},eN=L||!b.trim()||!E.trim()||ex;return(0,a.jsxs)("div",{className:"space-y-6",children:[(0,a.jsxs)("div",{className:"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",children:[(0,a.jsxs)("div",{children:[(0,a.jsxs)("h1",{className:"flex items-center gap-2 text-3xl font-bold text-text-main",children:[(0,a.jsx)("span",{className:"material-symbols-outlined text-primary",children:"vpn_key"}),"Quản l\xfd API Keys"]}),(0,a.jsx)("p",{className:"mt-1 text-sm text-text-muted",children:ed?"Admin c\xf3 thể tạo API key, g\xe1n owner v\xe0 chỉnh quyền truy cập model. Admin kh\xf4ng bị giới hạn số lượng key.":`Bạn c\xf3 thể quản l\xfd API key của t\xe0i khoản m\xecnh. Mỗi user thường được tạo tối đa ${eo} API keys.`})]}),(0,a.jsxs)(n.$n,{onClick:()=>g(!0),disabled:!E.trim()||ex,children:[(0,a.jsx)("span",{className:"material-symbols-outlined mr-1 text-[18px]",children:"add"}),"Tạo API Key"]})]}),u?(0,a.jsx)("div",{className:"rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-500/30 dark:bg-red-500/10 dark:text-red-300",children:u}):null,ed?null:(0,a.jsx)(n.Zp,{children:(0,a.jsxs)("div",{className:"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",children:[(0,a.jsxs)("div",{children:[(0,a.jsxs)("div",{className:"flex items-center gap-2 text-lg font-semibold text-text-main",children:[(0,a.jsx)("span",{className:"material-symbols-outlined text-primary",children:"counter_5"}),"Giới hạn API key theo user"]}),(0,a.jsxs)("p",{className:"mt-1 text-sm text-text-muted",children:["T\xe0i khoản của bạn đang c\xf3 ",em,"/",eo," API keys. Key bị disabled vẫn được t\xednh v\xe0o giới hạn."]})]}),(0,a.jsx)("span",{className:`rounded-full px-3 py-1 text-xs ${ex?"bg-red-500/10 text-red-600":"bg-green-500/10 text-green-600"}`,children:ex?"Đ\xe3 đạt giới hạn":"C\xf2n c\xf3 thể tạo th\xeam"})]})}),(0,a.jsx)(n.Zp,{children:(0,a.jsxs)("div",{className:"flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between",children:[(0,a.jsxs)("div",{className:"min-w-0",children:[(0,a.jsxs)("div",{className:"flex items-center gap-2 text-lg font-semibold text-text-main",children:[(0,a.jsx)("span",{className:"material-symbols-outlined text-primary",children:"rule"}),"Default model cho API key mới"]}),(0,a.jsx)("p",{className:"mt-1 text-sm text-text-muted",children:"API key mới sẽ mặc định chỉ được d\xf9ng model n\xe0y. Cần cấu h\xecnh default model trước khi tạo API key."}),(0,a.jsx)("div",{className:"mt-3",children:E?(0,a.jsx)("span",{className:"rounded-full border border-primary/30 bg-primary/10 px-3 py-1 font-mono text-xs text-primary",children:E}):(0,a.jsx)("span",{className:"rounded-full bg-red-500/10 px-3 py-1 text-xs text-red-600",children:"Chưa cấu h\xecnh default model"})})]}),ed?(0,a.jsxs)("div",{className:"flex flex-col gap-2 sm:min-w-[360px]",children:[(0,a.jsxs)("div",{className:"flex gap-2",children:[(0,a.jsx)("button",{type:"button",onClick:()=>V(!0),className:"min-w-0 flex-1 rounded-xl border border-border bg-background px-3 py-2 text-left font-mono text-xs text-text-main transition hover:border-primary/50",children:B||"Chọn default model..."}),(0,a.jsx)(n.$n,{onClick:ef,disabled:U||!B.trim()||!eu,children:U?"Đang lưu...":"Lưu"})]}),(0,a.jsx)("p",{className:"text-xs text-text-muted",children:"Danh s\xe1ch model gồm provider models, custom models, aliases v\xe0 combos."})]}):null]})}),w?(0,a.jsx)(n.Zp,{className:"border-green-200 bg-green-50/60 dark:border-green-500/30 dark:bg-green-500/10",children:(0,a.jsxs)("div",{className:"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("div",{className:"font-semibold text-green-700 dark:text-green-300",children:"Đ\xe3 tạo API key"}),(0,a.jsx)("div",{className:"mt-1 break-all font-mono text-xs text-text-main",children:w}),(0,a.jsxs)("div",{className:"mt-1 text-xs text-text-muted",children:["H\xe3y copy ngay. Key mới mặc định chỉ được d\xf9ng ",E||"default model"," trừ khi admin chỉnh quyền model."]})]}),(0,a.jsxs)("div",{className:"flex gap-2",children:[(0,a.jsx)(n.$n,{variant:"secondary",onClick:()=>ei(w,"created"),children:"created"===er?"Đ\xe3 copy":"Copy"}),(0,a.jsx)(n.$n,{variant:"secondary",onClick:()=>C(""),children:"Đ\xf3ng"})]})]})}):null,(0,a.jsx)(n.Zp,{children:x?(0,a.jsxs)("div",{className:"py-10 text-center text-sm text-text-muted",children:[(0,a.jsx)("span",{className:"material-symbols-outlined mr-2 animate-spin align-middle",children:"progress_activity"}),"Đang tải API keys..."]}):0===s.length?(0,a.jsxs)("div",{className:"py-12 text-center text-sm text-text-muted",children:[(0,a.jsx)("span",{className:"material-symbols-outlined mb-2 block text-4xl",children:"vpn_key_off"}),"Chưa c\xf3 API key n\xe0o."]}):(0,a.jsx)("div",{className:"space-y-3",children:s.map(e=>{var t;let s=A.has(e.id),l=e.ownerUser||e.createdByUser||"chưa g\xe1n",r=d(e.allowedModels);return(0,a.jsx)("div",{className:"rounded-2xl border border-border bg-background p-4",children:(0,a.jsxs)("div",{className:"flex flex-col gap-3 lg:flex-row lg:items-start lg:justify-between",children:[(0,a.jsxs)("div",{className:"min-w-0 flex-1 space-y-2",children:[(0,a.jsxs)("div",{className:"flex flex-wrap items-center gap-2",children:[(0,a.jsx)("h3",{className:"font-semibold text-text-main",children:e.name||"API Key"}),(0,a.jsx)("span",{className:`rounded-full px-2 py-0.5 text-xs ${e.isActive?"bg-green-500/10 text-green-600":"bg-red-500/10 text-red-600"}`,children:e.isActive?"Đang bật":"Đ\xe3 tắt"}),(0,a.jsxs)("span",{className:"rounded-full bg-primary/10 px-2 py-0.5 text-xs text-primary",children:["Owner: ",l]}),r.length>0?(0,a.jsxs)("span",{className:"rounded-full bg-blue-500/10 px-2 py-0.5 text-xs text-blue-600",children:[r.length," model"]}):(0,a.jsx)("span",{className:"rounded-full bg-amber-500/10 px-2 py-0.5 text-xs text-amber-700",children:"Legacy full access"})]}),(0,a.jsx)("div",{className:"break-all font-mono text-xs text-text-muted",children:s?e.key:(t=e.key)?t.length>12?`${t.slice(0,8)}...${t.slice(-4)}`:t:""}),(0,a.jsxs)("div",{className:"text-xs text-text-muted",children:["Tạo l\xfac: ",e.createdAt?new Date(e.createdAt).toLocaleString():"kh\xf4ng r\xf5",e.createdByUser?` \xb7 Tạo bởi: ${e.createdByUser}`:""]}),(0,a.jsx)("div",{className:"pt-1",children:(0,a.jsx)(c,{models:r,emptyLabel:"Legacy full model access"})})]}),(0,a.jsxs)("div",{className:"flex flex-wrap items-center gap-2",children:[(0,a.jsx)(n.lM,{checked:!1!==e.isActive,onChange:t=>ek(e.id,{isActive:t})}),(0,a.jsx)(n.$n,{variant:"secondary",onClick:()=>{var t;return t=e.id,void S(e=>{let s=new Set(e);return s.has(t)?s.delete(t):s.add(t),s})},children:s?"Ẩn":"Hiện"}),(0,a.jsx)(n.$n,{variant:"secondary",onClick:()=>ei(e.key,e.id),children:er===e.id?"Đ\xe3 copy":"Copy"}),(0,a.jsx)(n.$n,{variant:"secondary",onClick:()=>{var t;let s;return t=e.id,s=prompt("T\xean API key mới",e.name||""),void(s?.trim()&&ek(t,{name:s.trim()}))},children:"Đổi t\xean"}),ed?(0,a.jsx)(n.$n,{variant:"secondary",onClick:()=>{ed&&(z(e),Y(d(e.allowedModels)))},children:"Models"}):null,ed?(0,a.jsx)(n.$n,{variant:"secondary",onClick:()=>((e,t)=>{if(!ed)return;let s=prompt("G\xe1n owner username/email",t||ec||"");null!==s&&ek(e,{ownerUser:i(s)})})(e.id,l),children:"G\xe1n owner"}):null,(0,a.jsx)(n.$n,{variant:"danger",onClick:()=>{var t;return t=e.id,void I({title:"X\xf3a API Key",message:"Bạn chắc chắn muốn x\xf3a API key n\xe0y? Thao t\xe1c n\xe0y kh\xf4ng thể ho\xe0n t\xe1c.",onConfirm:async()=>{I(null),(await fetch(`/api/keys/${t}`,{method:"DELETE"})).ok&&await ey()}})},children:"X\xf3a"})]})]})},e.id)})})}),(0,a.jsx)(n.aF,{isOpen:y,onClose:()=>g(!1),title:"Tạo API Key",children:(0,a.jsxs)("div",{className:"space-y-4",children:[E.trim()?ex?(0,a.jsxs)("div",{className:"rounded-xl border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700 dark:border-red-500/30 dark:bg-red-500/10 dark:text-red-300",children:["Bạn đ\xe3 đạt giới hạn ",eo," API keys. H\xe3y x\xf3a bớt key cũ trước khi tạo key mới."]}):(0,a.jsxs)("div",{className:"rounded-xl border border-blue-200 bg-blue-50 px-3 py-2 text-sm text-blue-700 dark:border-blue-500/30 dark:bg-blue-500/10 dark:text-blue-300",children:["Key n\xe0y sẽ được tạo với quyền d\xf9ng default model: ",(0,a.jsx)("span",{className:"font-mono",children:E}),"."]}):(0,a.jsx)("div",{className:"rounded-xl border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700 dark:border-red-500/30 dark:bg-red-500/10 dark:text-red-300",children:"Admin cần cấu h\xecnh default model trước khi tạo API key."}),(0,a.jsx)(n.pd,{label:"T\xean key",value:b,onChange:e=>f(e.target.value),placeholder:"VD: VSCode, LibreChat, team-user"}),ed?(0,a.jsxs)("div",{className:"space-y-2",children:[(0,a.jsx)("label",{className:"text-sm font-medium text-text-main",children:"Owner user"}),(0,a.jsxs)("div",{className:"flex gap-2",children:[(0,a.jsx)(n.pd,{value:K,onChange:e=>M(e.target.value),placeholder:"T\xecm user LDAP"}),(0,a.jsx)(n.$n,{variant:"secondary",onClick:()=>eb(K),disabled:O,children:O?"Đang tải":"Tải"})]}),(0,a.jsxs)("select",{value:j,onChange:e=>k(e.target.value),className:"w-full rounded-xl border border-border bg-background px-3 py-2 text-sm text-text-main outline-none focus:border-primary",children:[(0,a.jsxs)("option",{value:ec,children:["G\xe1n cho t\xf4i (",ec||"local",")"]}),eh.map(e=>(0,a.jsx)("option",{value:e.id,children:e.label},e.id))]}),(0,a.jsx)(n.pd,{label:"Ghi đ\xe8 owner thủ c\xf4ng",value:v,onChange:e=>N(e.target.value),placeholder:"username hoặc email, kh\xf4ng bắt buộc",hint:"D\xf9ng khi LDAP kh\xf4ng khả dụng hoặc user kh\xf4ng c\xf3 trong danh s\xe1ch."})]}):(0,a.jsxs)("div",{className:"rounded-xl bg-surface-2 px-3 py-2 text-sm text-text-muted",children:["Owner: ",ec||"user hiện tại"]}),(0,a.jsxs)("div",{className:"flex justify-end gap-2",children:[(0,a.jsx)(n.$n,{variant:"secondary",onClick:()=>g(!1),children:"Hủy"}),(0,a.jsx)(n.$n,{onClick:ej,disabled:eN,children:L?"Đang tạo...":"Tạo"})]})]})}),(0,a.jsx)(o,{open:!!R,keyName:R?.name,models:W,setModels:Y,activeProviders:Z,modelAliases:X,onClose:()=>{z(null),Y([])},onSave:ev,saving:ee}),(0,a.jsx)(n.rq,{isOpen:G,onClose:()=>V(!1),onSelect:e=>{let t=String(e?.value||e?.name||e||"").trim();t&&(H(t),V(!1))},activeProviders:Z,modelAliases:X,title:"Chọn default model cho API key",selectedModel:B,addedModelValues:B?[B]:[]}),P?(0,a.jsx)(n.uo,{isOpen:!0,onClose:()=>I(null),onConfirm:P.onConfirm,title:P.title,message:P.message,confirmText:"X\xf3a",cancelText:"Hủy",variant:"danger"}):null]})}},84223:(e,t,s)=>{Promise.resolve().then(s.bind(s,56484))}},e=>{e.O(0,[2574,3862,1051,1321,5497,8441,3794,7358],()=>e(e.s=84223)),_N_E=e.O()}]);
@@ -0,0 +1 @@
1
+ (self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[3649],{20061:(e,t,a)=>{Promise.resolve().then(a.bind(a,47295))},47295:(e,t,a)=>{"use strict";a.r(t),a.d(t,{default:()=>I});var l=a(95155),n=a(12115),r=a(44923),s=a(87256),o=a(39768),i=a(82639),d=a(35497),c=a(11059),m=a(28777),u=a(52679);let x=/^[a-zA-Z0-9_.\-]+$/,b=[{value:"global",label:"To\xe0n cục / h\xe0nh vi hiện tại",description:"Giữ h\xe0nh vi hiện tại: mọi request d\xf9ng chung v\xf2ng xoay combo to\xe0n cục."},{value:"client_ip",label:"IP client",description:"Giữ c\xf9ng model khởi đầu cho c\xf9ng IP client cho đến khi hết TTL."},{value:"client_session",label:"IP client + phi\xean chat",description:"Ph\xf9 hợp nhất cho c\xf4ng cụ chat. Giữ c\xf9ng model cho c\xf9ng m\xe1y v\xe0 c\xf9ng phi\xean chat."}],h=[{value:"fallback",label:"Ưu ti\xean fallback",description:"H\xe0nh vi hiện tại: thử model trong combo theo đ\xfang thứ tự hiển thị, lỗi th\xec fallback."},{value:"round-robin",label:"Round Robin",description:"Lu\xe2n phi\xean model đang bật được thử đầu ti\xean. C\xe1c t\xf9y chọn giữ d\xednh b\xean dưới vẫn được \xe1p dụng."},{value:"least-latency",label:"Độ trễ thấp nhất",description:"Ưu ti\xean target c\xf3 điểm health/độ trễ tốt nhất. Hữu \xedch cho LAN Ollama."},{value:"p2c",label:"P2C",description:"Power of Two Choices: lấy mẫu hai target khỏe rồi chọn target \xedt bận hơn."}];function p(e={}){return Array.isArray(e.disabledModels)?[...new Set(e.disabledModels.map(e=>String(e||"").trim()).filter(Boolean))]:[]}function g(e={}){let t=e.fallbackStrategy||"fallback";return{...e,fallbackStrategy:t,stickyScope:e.stickyScope||"global",stickyTtlMinutes:Number(e.stickyTtlMinutes||120),stickyFallbackToClientIp:!1!==e.stickyFallbackToClientIp,lanHealthEnabled:!1!==e.lanHealthEnabled,healthCheckPath:e.healthCheckPath||"/api/tags",healthCheckIntervalMs:Number(e.healthCheckIntervalMs||15e3),requiredTags:Array.isArray(e.requiredTags)?e.requiredTags:[],modelTagRules:e.modelTagRules||e.modelTags||e.tagRules||{},disabledModels:p(e)}}function f(e=[]){return Array.isArray(e)?e.join(", "):String(e||"")}function y(e=""){return String(e||"").split(/[\n,;]+/).map(e=>e.trim()).filter(Boolean)}function v(e={}){return e&&"object"==typeof e?Object.entries(e).map(([e,t])=>`${e}: ${Array.isArray(t)?t.join(", "):String(t||"")}`).join("\n"):""}function j(e){return String(e||"").trim()}function k(e){return"string"==typeof e?e:e&&"object"==typeof e&&(e.value||e.model||e.id)||""}function N(e={}){let t=j(e.provider);return j(e.providerSpecificData?.prefix)||j(e.prefix)||j((0,u.wG)(t))||t}function w(e={}){return j(e.email)||j(e.accountName)||j(e.displayName)||j(e.name)||j(e.providerSpecificData?.nodeName)||j(e.id)}function C(e){return e&&"object"==typeof e?j(e.connectionId||e.connection_id):""}function S(e){let t=k(e),a=C(e);return t?a?`${t}@@${a}`:t:""}function T(e){return"string"==typeof e?e:e&&"object"==typeof e?j(e.label)||j(e.displayName)||j(e.name)||k(e):""}function $(e){if(!e||"object"!=typeof e)return k(e);let t=k(e);return t?Object.fromEntries(Object.entries({model:t,connectionId:C(e)||void 0,label:T(e)||void 0,provider:j(e.provider)||void 0,accountName:j(e.accountName||e.account_name)||void 0,weight:Number(e.weight||0)>0?Number(e.weight):void 0}).filter(([,e])=>void 0!==e&&""!==e)):""}function M(e,t=!0){let a=$(e),l=S(a);return{value:l,enabled:t,target:a,label:T(a)||l,subtitle:function(e){if(!e||"object"!=typeof e)return"";let t=C(e),a=k(e);return t?`T\xe0i khoản cố định • ${t}${a?` • ${a}`:""}`:""}(a)}}function E(e={}){return j(e.defaultModel)||j(e.providerSpecificData?.defaultModel)||j(e.model)||""}function _(e,t,a,l=""){let n=j(a);if(!n)return;let r=j(l)||n;t.has(n)||(t.add(n),e.push({value:n,label:r}))}function P(e={},t={}){let a=[],l=new Set,n=j(e.provider),r=[...new Set([N(e),j((0,u.wG)(n)),n].filter(Boolean))];for(let e of(0,m.KC)(n)||[])e?.type&&"llm"!==e.type||_(a,l,e.id,e.name||e.id);for(let[e,n]of Object.entries(t||{})){let t=j(n),s=r.find(e=>e&&t.startsWith(`${e}/`));if(!s)continue;let o=t.slice(s.length+1);_(a,l,o,e||o)}return _(a,l,E(e),"Model mặc định"),a}function O(e={},t=[]){let a={...e},l=[...new Set((t||[]).map(e=>String(e||"").trim()).filter(Boolean))];return l.length>0?a.disabledModels=l:delete a.disabledModels,Object.keys(a).length?a:null}function I(){let[e,t]=(0,n.useState)([]),[a,r]=(0,n.useState)(!0),[s,o]=(0,n.useState)(!1),[i,m]=(0,n.useState)(null),[u,x]=(0,n.useState)([]),[b,h]=(0,n.useState)({}),[f,y]=(0,n.useState)(null),{copied:v,copy:j}=(0,c.C)();(0,n.useEffect)(()=>{k()},[]);let k=async()=>{try{let[e,a,l]=await Promise.all([fetch("/api/combos"),fetch("/api/providers"),fetch("/api/settings")]),n=await e.json(),r=await a.json(),s=l.ok?await l.json():{};e.ok&&t((n.combos||[]).filter(e=>!e.kind)),a.ok&&x(r.connections||[]),h(s.comboStrategies||{})}catch(e){console.log("Error fetching data:",e)}finally{r(!1)}},N=async e=>{await fetch("/api/settings",{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({comboStrategies:e})}),h(e)},w=async(e,t)=>{let a={...b},l=O(a[e]||{},t);l?a[e]=l:delete a[e],await N(a)},C=async e=>{let{disabledModels:t=[],...a}=e;try{let e=await fetch("/api/combos",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(a)});if(e.ok)t.length>0&&await w(a.name,t),await k(),o(!1);else{let t=await e.json();alert(t.error||"Tạo combo thất bại")}}catch(e){console.log("Error creating combo:",e)}},S=async(e,t)=>{let{disabledModels:a=[],...l}=t,n=i?.name;try{let t=await fetch(`/api/combos/${e}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(l)});if(t.ok){let e={...b};n&&n!==l.name&&e[n]&&(e[l.name]=e[n],delete e[n]);let t=O(e[l.name]||{},a);t?e[l.name]=t:delete e[l.name],await N(e),await k(),m(null)}else{let e=await t.json();alert(e.error||"Cập nhật combo thất bại")}}catch(e){console.log("Error updating combo:",e)}},T=async a=>{let l=e.find(e=>e.id===a);y({title:"X\xf3a combo",message:"Bạn c\xf3 chắc muốn x\xf3a combo n\xe0y?",onConfirm:async()=>{y(null);try{if((await fetch(`/api/combos/${a}`,{method:"DELETE"})).ok){if(l?.name&&b[l.name]){let e={...b};delete e[l.name],await N(e)}t(e.filter(e=>e.id!==a))}}catch(e){console.log("Error deleting combo:",e)}}})},$=async(e,t)=>{try{let a={...b},l=function(e={}){let t={...e};return t.disabledModels=p(e),"fallback"===(t.fallbackStrategy||"fallback")&&delete t.fallbackStrategy,t.disabledModels.length||delete t.disabledModels,t.requiredTags?.length||delete t.requiredTags,t.modelTagRules&&0!==Object.keys(t.modelTagRules).length||delete t.modelTagRules,"/api/tags"===t.healthCheckPath&&delete t.healthCheckPath,15e3===Number(t.healthCheckIntervalMs)&&delete t.healthCheckIntervalMs,!0===t.lanHealthEnabled&&delete t.lanHealthEnabled,Object.keys(t).length?t:null}({...g(a[e]),...t});l?a[e]=l:delete a[e],await N(a)}catch(e){console.log("Error updating combo routing settings:",e)}};return a?(0,l.jsx)("div",{className:"space-y-6",children:[1,2,3].map(e=>(0,l.jsx)("div",{className:"h-40 animate-pulse rounded-2xl bg-surface"},e))}):(0,l.jsxs)("div",{className:"space-y-8",children:[(0,l.jsxs)("div",{className:"flex items-start justify-between gap-4",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("h1",{className:"text-2xl font-semibold text-text-main",children:"Combo model"}),(0,l.jsx)("p",{className:"mt-1 text-sm text-text-muted",children:"Tạo combo model với fallback, round-robin, độ trễ thấp nhất, P2C v\xe0 routing theo trạng th\xe1i health của LAN Ollama."})]}),(0,l.jsxs)("button",{type:"button",onClick:()=>o(!0),className:"inline-flex items-center justify-center gap-2 rounded-xl bg-primary px-4 py-2 text-sm font-medium text-white hover:opacity-90",children:[(0,l.jsx)("span",{className:"material-symbols-outlined text-[18px]",children:"add"}),"Create Combo"]})]}),0===e.length?(0,l.jsxs)("div",{className:"rounded-2xl border border-dashed border-border p-10 text-center",children:[(0,l.jsx)("span",{className:"material-symbols-outlined text-4xl text-text-muted",children:"layers"}),(0,l.jsx)("h2",{className:"mt-3 text-lg font-semibold text-text-main",children:"Chưa c\xf3 combo n\xe0o"}),(0,l.jsx)("p",{className:"mt-1 text-sm text-text-muted",children:"Tạo combo model c\xf3 hỗ trợ fallback."}),(0,l.jsx)("button",{type:"button",onClick:()=>o(!0),className:"mt-4 rounded-xl bg-primary px-4 py-2 text-sm font-medium text-white hover:opacity-90",children:"Create Combo"})]}):(0,l.jsx)("div",{className:"grid gap-5 md:grid-cols-2 xl:grid-cols-3",children:e.map(e=>(0,l.jsx)(R,{combo:e,copied:v,onCopy:j,onEdit:()=>m(e),onDelete:()=>T(e.id),comboStrategy:b[e.name]||{},onUpdateComboStrategy:t=>$(e.name,t)},e.id))}),(0,l.jsx)(q,{isOpen:s,onClose:()=>o(!1),onSave:C,activeProviders:u}),(0,l.jsx)(q,{isOpen:!!i,combo:i,comboStrategy:i&&b[i.name]||{},onClose:()=>m(null),onSave:e=>S(i.id,e),activeProviders:u}),(0,l.jsx)(d.uo,{isOpen:!!f,onClose:()=>y(null),onConfirm:f?.onConfirm,title:f?.title||"X\xe1c nhận",message:f?.message,variant:"danger"})]})}function R({combo:e,copied:t,onCopy:a,onEdit:r,onDelete:s,comboStrategy:o={},onUpdateComboStrategy:i}){let d=g(o||{}),c=d.fallbackStrategy||"fallback",m=h.find(e=>e.value===c)||h[0],u=d.stickyScope||"global",x=b.find(e=>e.value===u)||b[0],j=p(o),[N,w]=(0,n.useState)(f(d.requiredTags)),[C,$]=(0,n.useState)(v(d.modelTagRules));return(0,n.useEffect)(()=>{w(f(d.requiredTags)),$(v(d.modelTagRules))},[e.name,JSON.stringify(d.requiredTags||[]),JSON.stringify(d.modelTagRules||{})]),(0,l.jsxs)("div",{className:"rounded-2xl border border-border bg-background p-5 shadow-sm",children:[(0,l.jsxs)("div",{className:"mb-4 flex items-start justify-between gap-3",children:[(0,l.jsxs)("div",{className:"min-w-0",children:[(0,l.jsxs)("div",{className:"flex items-center gap-2",children:[(0,l.jsx)("span",{className:"material-symbols-outlined text-primary",children:"layers"}),(0,l.jsx)("h3",{className:"truncate text-lg font-semibold text-text-main",children:e.name})]}),(0,l.jsxs)("p",{className:"mt-1 text-sm text-text-muted",children:[e.models.length," model đang bật",j.length>0?` • ${j.length} đ\xe3 tắt`:""]})]}),(0,l.jsxs)("div",{className:"flex items-center gap-1",children:[(0,l.jsx)(A,{icon:t===`combo-${e.id}`?"check":"content_copy",title:"Copy combo name",onClick:()=>a(e.name,`combo-${e.id}`)}),(0,l.jsx)(A,{icon:"edit",title:"Edit",onClick:r}),(0,l.jsx)(A,{icon:"delete",title:"Delete",onClick:s,danger:!0})]})]}),(0,l.jsxs)("div",{className:"mb-4 flex flex-wrap gap-2",children:[0===e.models.length?(0,l.jsx)("span",{className:"rounded-lg border border-border px-3 py-1 text-xs text-text-muted",children:"Chưa c\xf3 model đang bật"}):e.models.slice(0,5).map(e=>(0,l.jsx)("span",{className:"rounded-lg border border-border bg-surface px-3 py-1 font-mono text-xs text-text-main",title:k(e),children:T(e)},S(e))),e.models.length>5&&(0,l.jsxs)("span",{className:"rounded-lg border border-border px-3 py-1 text-xs text-text-muted",children:["+",e.models.length-5," model nữa"]}),j.length>0&&(0,l.jsxs)("span",{className:"rounded-lg border border-red-200 bg-red-50 px-3 py-1 text-xs font-medium text-red-600 dark:border-red-500/30 dark:bg-red-500/10 dark:text-red-300",children:[j.length," đ\xe3 tắt"]})]}),(0,l.jsxs)("div",{className:"rounded-xl border border-border bg-surface/50 p-4",children:[(0,l.jsxs)("div",{className:"mb-3 flex items-start justify-between gap-3",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("h4",{className:"font-semibold text-text-main",children:"Chiến lược routing"}),(0,l.jsx)("p",{className:"mt-1 text-sm text-text-muted",children:m.description})]}),(0,l.jsx)("span",{className:"rounded-lg border border-primary/20 bg-primary/10 px-2 py-1 text-[11px] font-semibold text-primary",children:m.label})]}),(0,l.jsxs)("label",{className:"mb-3 block",children:[(0,l.jsx)("span",{className:"mb-1 block text-[11px] font-semibold uppercase tracking-wide text-text-muted",children:"Strategy"}),(0,l.jsx)("select",{value:c,onChange:e=>i({fallbackStrategy:e.target.value}),className:"w-full rounded-lg border border-border bg-background px-2 py-2 text-sm text-text-main outline-none focus:border-primary",children:h.map(e=>(0,l.jsx)("option",{value:e.value,children:e.label},e.value))})]}),("least-latency"===c||"p2c"===c)&&(0,l.jsxs)("div",{className:"mb-3 rounded-xl border border-emerald-500/20 bg-emerald-500/5 p-3",children:[(0,l.jsxs)("div",{className:"mb-2 flex items-center gap-2 text-sm font-semibold text-emerald-600 dark:text-emerald-300",children:[(0,l.jsx)("span",{className:"material-symbols-outlined text-[18px]",children:"monitor_heart"}),"Routing theo trạng th\xe1i health LAN"]}),(0,l.jsxs)("label",{className:"mb-3 flex items-start gap-2 text-xs text-text-muted",children:[(0,l.jsx)("input",{type:"checkbox",checked:!1!==d.lanHealthEnabled,onChange:e=>i({lanHealthEnabled:e.target.checked}),className:"mt-0.5 h-3.5 w-3.5 accent-primary"}),"D\xf9ng trạng th\xe1i health đang hoạt động v\xe0 dữ liệu circuit breaker khi xếp hạng target trong combo."]}),(0,l.jsxs)("div",{className:"grid gap-2 sm:grid-cols-2",children:[(0,l.jsxs)("label",{className:"block",children:[(0,l.jsx)("span",{className:"mb-1 block text-[11px] font-semibold uppercase tracking-wide text-text-muted",children:"Đường dẫn health"}),(0,l.jsx)("input",{value:d.healthCheckPath||"/api/tags",onChange:e=>i({healthCheckPath:e.target.value||"/api/tags"}),placeholder:"/api/tags",className:"w-full rounded-lg border border-border bg-background px-2 py-1.5 font-mono text-xs text-text-main outline-none focus:border-primary"})]}),(0,l.jsxs)("label",{className:"block",children:[(0,l.jsx)("span",{className:"mb-1 block text-[11px] font-semibold uppercase tracking-wide text-text-muted",children:"Chu kỳ kiểm tra (ms)"}),(0,l.jsx)("input",{type:"number",min:"1000",value:d.healthCheckIntervalMs||15e3,onChange:e=>i({healthCheckIntervalMs:Number(e.target.value)||15e3}),className:"w-full rounded-lg border border-border bg-background px-2 py-1.5 text-xs text-text-main outline-none focus:border-primary"})]})]}),(0,l.jsxs)("label",{className:"mt-3 block",children:[(0,l.jsx)("span",{className:"mb-1 block text-[11px] font-semibold uppercase tracking-wide text-text-muted",children:"Tag endpoint bắt buộc"}),(0,l.jsx)("input",{value:N,onChange:e=>w(e.target.value),onBlur:()=>i({requiredTags:y(N)}),onKeyDown:e=>"Enter"===e.key&&e.currentTarget.blur(),placeholder:"gpu, vram:8gb",className:"w-full rounded-lg border border-border bg-background px-2 py-1.5 font-mono text-xs text-text-main outline-none focus:border-primary"}),(0,l.jsx)("span",{className:"mt-1 block text-[11px] text-text-muted",children:"T\xf9y chọn. Mọi endpoint được route đều phải c\xf3 c\xe1c tag n\xe0y."})]}),(0,l.jsxs)("label",{className:"mt-3 block",children:[(0,l.jsx)("span",{className:"mb-1 block text-[11px] font-semibold uppercase tracking-wide text-text-muted",children:"Quy tắc tag theo model"}),(0,l.jsx)("textarea",{value:C,onChange:e=>$(e.target.value),onBlur:()=>i({modelTagRules:function(e=""){let t={};for(let a of String(e||"").split(/\n+/)){let e=a.trim();if(!e)continue;let l=e.indexOf(":");if(-1===l)continue;let n=e.slice(0,l).trim(),r=y(e.slice(l+1));n&&r.length&&(t[n]=r)}return t}(C)}),placeholder:"qwen3:32b: gpu, vram:16gb\nqwen3:8b: model_support:qwen3:8b",rows:3,className:"w-full rounded-lg border border-border bg-background px-2 py-1.5 font-mono text-xs text-text-main outline-none focus:border-primary"}),(0,l.jsx)("span",{className:"mt-1 block text-[11px] text-text-muted",children:"Mỗi d\xf2ng một quy tắc: model: tag1, tag2. C\xf3 thể \xe9p model lớn chạy tr\xean endpoint GPU/VRAM ph\xf9 hợp."})]})]}),"round-robin"===c&&(0,l.jsxs)("div",{className:"mt-4 rounded-xl border border-primary/15 bg-background p-3",children:[(0,l.jsxs)("div",{className:"mb-3 flex items-center gap-2 text-sm font-semibold text-primary",children:[(0,l.jsx)("span",{className:"material-symbols-outlined text-[18px]",children:"sticky_note_2"}),"Round Robin giữ d\xednh"]}),(0,l.jsx)("p",{className:"mb-3 text-xs text-text-muted",children:"T\xf9y chọn giữ d\xednh chỉ \xe1p dụng cho combo n\xe0y. Để To\xe0n cục nếu muốn giữ h\xe0nh vi hiện tại."}),(0,l.jsxs)("label",{className:"mb-3 block",children:[(0,l.jsx)("span",{className:"mb-1 block text-[11px] font-semibold uppercase tracking-wide text-text-muted",children:"Chế độ giữ d\xednh"}),(0,l.jsx)("select",{value:u,onChange:e=>i({stickyScope:e.target.value}),className:"w-full rounded-lg border border-border bg-background px-2 py-1.5 text-xs text-text-main outline-none focus:border-primary",children:b.map(e=>(0,l.jsx)("option",{value:e.value,children:e.label},e.value))}),(0,l.jsx)("span",{className:"mt-1 block text-[11px] text-text-muted",children:x.description})]}),(0,l.jsxs)("label",{className:"mb-3 block",children:[(0,l.jsx)("span",{className:"mb-1 block text-[11px] font-semibold uppercase tracking-wide text-text-muted",children:"TTL giữ d\xednh (ph\xfat)"}),(0,l.jsx)("input",{type:"number",min:"1",value:d.stickyTtlMinutes||120,onChange:e=>i({stickyTtlMinutes:Number(e.target.value)||120}),className:"w-full rounded-lg border border-border bg-background px-2 py-1.5 text-xs text-text-main outline-none focus:border-primary"}),(0,l.jsx)("span",{className:"mt-1 block text-[11px] text-text-muted",children:"G\xe1n giữ d\xednh được lưu trong RAM v\xe0 hết hạn sau thời gian n\xe0y. Khuyến nghị 120 ph\xfat."})]}),(0,l.jsxs)("label",{className:"flex items-start gap-2 text-xs text-text-muted",children:[(0,l.jsx)("input",{type:"checkbox",checked:!1!==d.stickyFallbackToClientIp,onChange:e=>i({stickyFallbackToClientIp:e.target.checked}),className:"mt-0.5 h-3.5 w-3.5 accent-primary"}),"Nếu thiếu chat session id, fallback về IP client thay v\xec xoay to\xe0n cục."]})]})]})]})}function A({icon:e,title:t,onClick:a,danger:n=!1}){return(0,l.jsx)("button",{type:"button",onClick:e=>{e.stopPropagation(),a?.()},className:`rounded-lg p-2 transition ${n?"text-red-500 hover:bg-red-500/10":"text-text-muted hover:bg-background hover:text-primary"}`,title:t,"aria-label":t,children:(0,l.jsx)("span",{className:"material-symbols-outlined text-[20px]",children:e})})}function D({id:e,index:t,model:a,subtitle:r="",enabled:i,isFirst:d,isLast:c,onEdit:m,onMoveUp:u,onMoveDown:x,onRemove:b,onToggleEnabled:h}){let{attributes:p,listeners:g,setNodeRef:f,transform:y,isDragging:v}=(0,s.gl)({id:e}),j={transform:o.Ks.Transform.toString(y),opacity:v?.4:1,zIndex:v?999:void 0},[k,N]=(0,n.useState)(!1),[w,C]=(0,n.useState)(a);(0,n.useEffect)(()=>{C(a)},[a]);let S=()=>{let e=w.trim();e&&e!==a?m(e):C(a),N(!1)};return(0,l.jsxs)("div",{ref:f,style:j,className:`flex items-center gap-3 rounded-xl border p-3 transition ${i?"border-border bg-background":"border-red-200 bg-red-50/70 opacity-75 dark:border-red-500/30 dark:bg-red-500/10"}`,children:[(0,l.jsx)("button",{type:"button",className:"cursor-grab text-text-muted active:cursor-grabbing",...p,...g,title:"K\xe9o để sắp xếp lại",children:(0,l.jsx)("span",{className:"material-symbols-outlined text-[20px]",children:"drag_indicator"})}),(0,l.jsx)("span",{className:`flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-xs font-semibold ${i?"bg-primary/10 text-primary":"bg-red-100 text-red-600 dark:bg-red-500/20 dark:text-red-300"}`,children:t+1}),k?(0,l.jsx)("input",{autoFocus:!0,value:w,onChange:e=>C(e.target.value),onBlur:S,onKeyDown:e=>{"Enter"===e.key&&S(),"Escape"===e.key&&(C(a),N(!1))},className:"min-w-0 flex-1 rounded border border-primary/40 bg-background px-2 py-1 font-mono text-xs text-text-main outline-none"}):(0,l.jsxs)("button",{type:"button",onClick:()=>N(!0),className:"min-w-0 flex-1 text-left",title:"Bấm để sửa model",children:[(0,l.jsx)("span",{className:`block truncate font-mono text-xs ${i?"text-text-main":"text-text-muted line-through"}`,children:a}),r?(0,l.jsx)("span",{className:"mt-0.5 block truncate text-[11px] text-text-muted",children:r}):null]}),(0,l.jsx)("button",{type:"button",onClick:h,className:`rounded-lg p-1.5 transition ${i?"text-green-600 hover:bg-green-500/10":"text-red-500 hover:bg-red-500/10"}`,title:i?"Disable this model in combo":"Enable this model in combo",children:(0,l.jsx)("span",{className:"material-symbols-outlined text-[20px]",children:i?"toggle_on":"toggle_off"})}),(0,l.jsx)("button",{type:"button",onClick:u,disabled:d,className:"rounded-lg p-1.5 text-text-muted transition hover:bg-surface disabled:opacity-30",title:"Di chuyển l\xean",children:(0,l.jsx)("span",{className:"material-symbols-outlined text-[20px]",children:"arrow_upward"})}),(0,l.jsx)("button",{type:"button",onClick:x,disabled:c,className:"rounded-lg p-1.5 text-text-muted transition hover:bg-surface disabled:opacity-30",title:"Di chuyển xuống",children:(0,l.jsx)("span",{className:"material-symbols-outlined text-[20px]",children:"arrow_downward"})}),(0,l.jsx)("button",{type:"button",onClick:b,className:"rounded-lg p-1.5 text-red-500 transition hover:bg-red-500/10",title:"X\xf3a",children:(0,l.jsx)("span",{className:"material-symbols-outlined text-[20px]",children:"close"})})]})}function q({isOpen:e,combo:t,comboStrategy:a={},onClose:o,onSave:c,activeProviders:m,kindFilter:b=null}){let[h,g]=(0,n.useState)(t?.name||""),[f,y]=(0,n.useState)([]),[v,k]=(0,n.useState)(!1),[C,T]=(0,n.useState)(!1),[_,O]=(0,n.useState)(""),[I,R]=(0,n.useState)({}),[A,B]=(0,n.useState)(""),[H,F]=(0,n.useState)(""),G=a||{},L=(0,r.FR)((0,r.MS)(r.AN,{activationConstraint:{distance:5}}),(0,r.MS)(r.uN,{coordinateGetter:s.JR}));(0,n.useEffect)(()=>{e&&(g(t?.name||""),y(function(e,t={}){let a=(e?.models||[]).map(e=>M(e,!0)).filter(e=>e.value),l=new Set(a.map(e=>e.value));return[...a,...p(t).filter(e=>!l.has(e)).map(e=>M(e,!1)).filter(e=>e.value)]}(t,G)),O(""),B(""),F(""))},[e,t?.id,t?.name]),(0,n.useEffect)(()=>{e&&z()},[e]);let z=async()=>{try{let e=await fetch("/api/models/alias");if(!e.ok)return;let t=await e.json();R(t.aliases||{})}catch(e){console.error("Error fetching modal data:",e)}},K=f.map((e,t)=>({uid:`item-${t}-${e.value}`,row:e})),J=f.map(e=>e.value),U=f.filter(e=>e.enabled).length,X=f.filter(e=>!e.enabled).length,V=(m||[]).filter(e=>e?.isActive!==!1),W=V.find(e=>e.id===A)||null,Q=W?P(W,I):[],Z=Q[0]?.value||(W?E(W):""),Y=e=>e.trim()?x.test(e)?(O(""),!0):(O("Chỉ cho ph\xe9p chữ, số, dấu -, _ v\xe0 ."),!1):(O("Bắt buộc nhập t\xean"),!1),ee=async()=>{if(!Y(h))return;let{models:e,disabledModels:t}=function(e=[]){let t=[],a=new Set;for(let l of e){let e=j(l?.value||S(l?.target));!e||a.has(e)||(a.add(e),t.push({...l,value:e,enabled:l?.enabled!==!1,target:$(l?.target||l?.value)}))}return{models:t.filter(e=>e.enabled).map(e=>e.target).filter(Boolean),disabledModels:t.filter(e=>!e.enabled).map(e=>e.value)}}(f);T(!0),await c({name:h.trim(),models:e,disabledModels:t}),T(!1)};if(!e)return null;let et=!!t;return(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)("div",{className:"fixed inset-0 z-[100] flex items-center justify-center bg-black/45 p-4 backdrop-blur-[1px]",children:(0,l.jsxs)("div",{className:"flex max-h-[88vh] w-full max-w-3xl flex-col overflow-hidden rounded-2xl border border-border bg-white shadow-2xl dark:bg-zinc-950",children:[(0,l.jsxs)("div",{className:"flex items-start justify-between border-b border-border px-5 py-4",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("h2",{className:"text-xl font-semibold text-text-main",children:et?"Chỉnh sửa combo":"Tạo combo"}),(0,l.jsx)("p",{className:"mt-1 text-sm text-text-muted",children:"Chọn model, tắt tạm thời khi cần v\xe0 sắp xếp thứ tự ưu ti\xean fallback đang bật."})]}),(0,l.jsx)("button",{type:"button",onClick:o,className:"rounded-lg p-1 text-text-muted transition hover:bg-surface hover:text-text-main",children:(0,l.jsx)("span",{className:"material-symbols-outlined",children:"close"})})]}),(0,l.jsxs)("div",{className:"min-h-0 flex-1 space-y-5 overflow-y-auto bg-white px-5 py-4 dark:bg-zinc-950",children:[(0,l.jsxs)("label",{className:"block",children:[(0,l.jsx)("span",{className:"mb-2 block text-sm font-semibold text-text-main",children:"T\xean"}),(0,l.jsx)("input",{value:h,onChange:e=>{let t=e.target.value;g(t),t?Y(t):O("")},placeholder:"premium-coding",className:`w-full rounded-xl border bg-white px-4 py-2.5 text-text-main outline-none transition focus:border-primary dark:bg-zinc-900 ${_?"border-red-400":"border-border"}`}),(0,l.jsx)("span",{className:`mt-1 block text-xs ${_?"text-red-500":"text-text-muted"}`,children:_||"Chỉ cho ph\xe9p chữ, số, dấu -, _ v\xe0 ."})]}),(0,l.jsxs)("div",{children:[(0,l.jsxs)("div",{className:"mb-3 flex items-center justify-between gap-3",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("h3",{className:"text-sm font-semibold text-text-main",children:"Danh s\xe1ch model"}),(0,l.jsxs)("p",{className:"mt-1 text-xs text-text-muted",children:[U," đang bật",X>0?`, ${X} đ\xe3 tắt`:"",". Model đ\xe3 tắt vẫn nằm trong combo nhưng kh\xf4ng được route."]})]}),(0,l.jsxs)("button",{type:"button",onClick:()=>k(!0),className:"inline-flex items-center gap-1 rounded-lg border border-border px-3 py-1.5 text-xs font-medium text-primary hover:border-primary/50",children:[(0,l.jsx)("span",{className:"material-symbols-outlined text-[18px]",children:"add"}),"Th\xeam model"]})]}),(0,l.jsxs)("div",{className:"mb-4 rounded-xl border border-primary/15 bg-primary/5 p-3",children:[(0,l.jsxs)("div",{className:"mb-2 flex items-center gap-2 text-xs font-semibold uppercase tracking-wide text-primary",children:[(0,l.jsx)("span",{className:"material-symbols-outlined text-[18px]",children:"account_tree"}),"Th\xeam target theo provider account"]}),(0,l.jsx)("p",{className:"mb-3 text-xs text-text-muted",children:"Cố định bước combo n\xe0y v\xe0o một provider account cụ thể. Model lấy từ provider; account chỉ d\xf9ng để cố định routing/load balancing."}),(0,l.jsxs)("div",{className:"grid gap-2 md:grid-cols-[minmax(0,1.5fr)_minmax(0,1fr)_auto]",children:[(0,l.jsxs)("select",{value:A,onChange:e=>{let t=e.target.value,a=V.find(e=>e.id===t),l=a?P(a,I):[];B(t),F(l[0]?.value||E(a||{})||"")},className:"min-w-0 rounded-lg border border-border bg-background px-3 py-2 text-xs text-text-main outline-none focus:border-primary",children:[(0,l.jsx)("option",{value:"",children:"Chọn provider account"}),V.map(e=>{let t=N(e),a=w(e),n=a&&a!==t?`${t} / ${a}`:t;return(0,l.jsx)("option",{value:e.id,children:n},e.id)})]}),(0,l.jsxs)("select",{value:H,onChange:e=>F(e.target.value),disabled:!A||0===Q.length,className:"min-w-0 rounded-lg border border-border bg-background px-3 py-2 font-mono text-xs text-text-main outline-none focus:border-primary disabled:cursor-not-allowed disabled:opacity-60",children:[(0,l.jsx)("option",{value:"",children:A?Q.length>0?"Chọn model":"Kh\xf4ng t\xecm thấy model của provider":"Chọn account trước"}),Q.map(e=>(0,l.jsx)("option",{value:e.value,children:e.label},`${e.value}-${e.label}`))]}),(0,l.jsxs)("button",{type:"button",onClick:()=>{if(!W)return;let e=function(e,t){let a=N(e),l=function(e={}){let t=j(e.provider),a=N(e);return(0,u.mq)(t)||(0,u.gb)(t)?a||t:t||a}(e),n=w(e),r=j(t||E(e));if(!a||!e?.id||!r)return null;let s=r.includes("/"),o=s?r:`${a}/${r}`,i=s&&r.startsWith(`${a}/`)?r.slice(a.length+1):r,d=n&&n!==l?`${l}/${n}/${i}`:`${l}/${i}`;return{model:o,connectionId:e.id,provider:l||a,accountName:n||void 0,label:d}}(W,H||Z);if(!e)return void alert("H\xe3y chọn provider account v\xe0 model cho target cố định n\xe0y.");let t=M(e,!0);y(e=>{let a=e.findIndex(e=>e.value===t.value);if(-1!==a){let l=[...e];return l[a]={...l[a],...t,enabled:!0},l}return[...e,t]}),F("")},disabled:!A||!H,className:"inline-flex items-center justify-center gap-1 rounded-lg bg-primary px-3 py-2 text-xs font-semibold text-white disabled:opacity-50",children:[(0,l.jsx)("span",{className:"material-symbols-outlined text-[18px]",children:"add_link"}),"Th\xeam account"]})]})]}),0===f.length?(0,l.jsxs)("div",{className:"rounded-xl border border-dashed border-border p-8 text-center text-text-muted",children:[(0,l.jsx)("span",{className:"material-symbols-outlined",children:"layers"}),(0,l.jsx)("p",{className:"mt-2 text-sm",children:"Chưa th\xeam model n\xe0o"})]}):(0,l.jsx)(r.Mp,{sensors:L,collisionDetection:r.fp,modifiers:[i.FN,i.gj],onDragEnd:e=>{let{active:t,over:a}=e;if(a&&t.id!==a.id){let e=K.findIndex(e=>e.uid===t.id),l=K.findIndex(e=>e.uid===a.id);-1!==e&&-1!==l&&y(t=>(0,s.be)(t,e,l))}},children:(0,l.jsx)(s.gB,{items:K.map(e=>e.uid),strategy:s._G,children:(0,l.jsx)("div",{className:"space-y-3",children:K.map(({uid:e,row:t},a)=>(0,l.jsx)(D,{id:e,index:a,model:t.label||t.value,subtitle:t.subtitle,enabled:t.enabled,isFirst:0===a,isLast:a===f.length-1,onEdit:e=>{let t=e.trim();if(!t)return;let l=[...f],n=l[a];if(n?.target&&"object"==typeof n.target){let e=$({...n.target,model:t});l[a]=M(e,!1!==n.enabled)}else l[a]=M(t,n?.enabled!==!1);y(l)},onMoveUp:()=>{if(0===a)return;let e=[...f];[e[a-1],e[a]]=[e[a],e[a-1]],y(e)},onMoveDown:()=>{if(a===f.length-1)return;let e=[...f];[e[a],e[a+1]]=[e[a+1],e[a]],y(e)},onToggleEnabled:()=>{let e=[...f];e[a]={...e[a],enabled:!e[a].enabled},y(e)},onRemove:()=>y(f.filter((e,t)=>t!==a))},e))})})})]})]}),(0,l.jsxs)("div",{className:"flex justify-end gap-3 border-t border-border bg-white px-5 py-4 dark:bg-zinc-950",children:[(0,l.jsx)("button",{type:"button",onClick:o,className:"rounded-xl border border-border px-4 py-2 text-sm font-medium text-text-main hover:bg-surface",children:"Hủy"}),(0,l.jsx)("button",{type:"button",onClick:ee,disabled:C,className:"rounded-xl bg-primary px-4 py-2 text-sm font-medium text-white hover:opacity-90 disabled:opacity-50",children:C?"Đang lưu...":et?"Lưu":"Tạo"})]})]})}),(0,l.jsx)(d.rq,{isOpen:v,onClose:()=>k(!1),onSelect:e=>{let t=e.value;y(e=>{let a=e.findIndex(e=>e.value===t);if(-1!==a){let t=[...e];return t[a]={...t[a],enabled:!0},t}return[...e,M(t,!0)]})},onDeselect:e=>{y(t=>t.filter(t=>t.value!==e.value))},activeProviders:m,modelAliases:I,title:"Th\xeam model v\xe0o combo",kindFilter:b,addedModelValues:J,closeOnSelect:!1})]})}}},e=>{e.O(0,[2574,3862,1051,7055,1321,5497,8441,3794,7358],()=>e(e.s=20061)),_N_E=e.O()}]);
package/app/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bluerouter-app",
3
- "version": "1.0.121",
3
+ "version": "1.0.123",
4
4
  "description": "Router web dashboard",
5
5
  "private": false,
6
6
  "scripts": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jheavenknows/bluerouter",
3
- "version": "1.0.121",
3
+ "version": "1.0.123",
4
4
  "description": "Bluerouter CLI - Start and manage Bluerouter server",
5
5
  "bin": {
6
6
  "bluerouter": "./cli.js"
@@ -1 +0,0 @@
1
- (self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[7593],{56484:(e,t,a)=>{"use strict";a.d(t,{default:()=>m});var s=a(95155),l=a(12115),r=a(35497),n=a(11059);function i(e){return String(e||"").trim().toLowerCase()}function d(e){return Array.isArray(e)?[...new Set(e.map(e=>String(e||"").trim()).filter(Boolean))]:[]}function o({models:e,emptyLabel:t="No model assigned"}){let a=d(e);return 0===a.length?(0,s.jsx)("span",{className:"rounded-full bg-amber-500/10 px-2 py-0.5 text-xs text-amber-700 dark:text-amber-300",children:t}):(0,s.jsxs)("div",{className:"flex flex-wrap gap-1.5",children:[a.slice(0,8).map(e=>(0,s.jsx)("span",{className:"rounded-full border border-border bg-surface px-2 py-1 font-mono text-[11px] text-text-main",children:e},e)),a.length>8?(0,s.jsxs)("span",{className:"rounded-full bg-primary/10 px-2 py-1 text-[11px] text-primary",children:["+",a.length-8," more"]}):null]})}function c({open:e,keyName:t,models:a,setModels:n,activeProviders:i,modelAliases:o,onClose:m,onSave:x,saving:u}){let[h,p]=(0,l.useState)(!1),y=d(a),f=e=>{let t=e?.value||e?.name||e;n(e=>d(e).filter(e=>e!==t))};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(r.aF,{isOpen:e,onClose:m,title:`Edit model access${t?`: ${t}`:""}`,children:(0,s.jsxs)("div",{className:"space-y-4",children:[(0,s.jsx)("div",{className:"rounded-xl border border-blue-200 bg-blue-50 px-3 py-2 text-sm text-blue-700 dark:border-blue-500/30 dark:bg-blue-500/10 dark:text-blue-300",children:"Select one or more models this API key may use. Empty model access is not allowed from this page."}),(0,s.jsxs)("div",{className:"space-y-2",children:[(0,s.jsxs)("div",{className:"flex items-center justify-between gap-2",children:[(0,s.jsx)("label",{className:"text-sm font-medium text-text-main",children:"Allowed models"}),(0,s.jsxs)(r.$n,{variant:"secondary",onClick:()=>p(!0),children:[(0,s.jsx)("span",{className:"material-symbols-outlined mr-1 text-[18px]",children:"add"}),"Add model"]})]}),(0,s.jsx)("div",{className:"min-h-[76px] rounded-xl border border-border bg-background p-3",children:0===y.length?(0,s.jsx)("div",{className:"text-sm text-text-muted",children:"No models selected. Add at least one model before saving."}):(0,s.jsx)("div",{className:"flex flex-wrap gap-2",children:y.map(e=>(0,s.jsxs)("button",{type:"button",onClick:()=>f(e),className:"rounded-full border border-primary/30 bg-primary/10 px-2 py-1 font-mono text-xs text-primary transition hover:bg-red-500/10 hover:text-red-600",title:"Click to remove",children:[e," ",(0,s.jsx)("span",{className:"ml-1",children:"\xd7"})]},e))})})]}),(0,s.jsxs)("div",{className:"flex justify-end gap-2",children:[(0,s.jsx)(r.$n,{variant:"secondary",onClick:m,children:"Cancel"}),(0,s.jsx)(r.$n,{onClick:x,disabled:u||0===y.length,children:u?"Saving...":"Save models"})]})]})}),(0,s.jsx)(r.rq,{isOpen:h,onClose:()=>p(!1),onSelect:e=>{let t=e?.value||e?.name||e;t&&n(e=>d([...e,t]))},onDeselect:f,activeProviders:i,modelAliases:o,title:"Select allowed models",addedModelValues:y,closeOnSelect:!1})]})}function m(){let[e,t]=(0,l.useState)(null),[a,m]=(0,l.useState)([]),[x,u]=(0,l.useState)(!0),[h,p]=(0,l.useState)(""),[y,f]=(0,l.useState)(!1),[b,g]=(0,l.useState)(""),[j,v]=(0,l.useState)(""),[k,N]=(0,l.useState)(""),[w,C]=(0,l.useState)(""),[S,A]=(0,l.useState)(new Set),[P,$]=(0,l.useState)(null),[I,M]=(0,l.useState)([]),[D,O]=(0,l.useState)(""),[E,L]=(0,l.useState)(!1),[T,F]=(0,l.useState)(!1),[U,_]=(0,l.useState)(""),[K,B]=(0,l.useState)(""),[q,J]=(0,l.useState)(!1),[V,Z]=(0,l.useState)(!1),[H,R]=(0,l.useState)([]),[Y,z]=(0,l.useState)({}),[G,Q]=(0,l.useState)(null),[W,X]=(0,l.useState)([]),[ee,et]=(0,l.useState)(!1),{copied:ea,copy:es}=(0,n.C)(),el=e?.isAdmin!==!1,er=e?.userId||"",en=(0,l.useMemo)(()=>{let e=new Map;for(let t of I){let a=i(t.username||t.email||t.userPrincipalName||t.sAMAccountName);a&&e.set(a,{id:a,label:`${t.displayName||t.username||a}${t.email?` \xb7 ${t.email}`:""}`})}for(let t of a){let a=i(t.ownerUser||t.createdByUser);a&&!e.has(a)&&e.set(a,{id:a,label:a})}return er&&!e.has(er)&&e.set(er,{id:er,label:`${er} (me)`}),[...e.values()].sort((e,t)=>e.label.localeCompare(t.label))},[I,a,er]),ei=K!==U,ed=async()=>{let e=await fetch("/api/auth/status",{cache:"no-store"}),a=await e.json().catch(()=>({}));return t(a),!j&&a?.userId&&v(a.userId),a},eo=async()=>{u(!0),p("");try{let e=await fetch("/api/keys",{cache:"no-store"}),t=await e.json().catch(()=>({}));if(!e.ok)throw Error(t.error||"Failed to load API keys");m(t.keys||[]);let a=String(t.apiKeyDefaultModel||"").trim();_(a),B(a)}catch(e){p(e.message||"Failed to load API keys")}finally{u(!1)}},ec=async()=>{try{let[e,t]=await Promise.allSettled([fetch("/api/providers",{cache:"no-store"}),fetch("/api/models/alias",{cache:"no-store"})]);if("fulfilled"===e.status&&e.value.ok){let t=await e.value.json().catch(()=>({}));R(t.connections||[])}if("fulfilled"===t.status&&t.value.ok){let e=await t.value.json().catch(()=>({}));z(e.aliases||{})}}catch{}},em=async(e=D)=>{if(el){L(!0);try{let t=await fetch(`/api/auth/ldap/users?q=${encodeURIComponent(e||"")}`,{cache:"no-store"}),a=await t.json().catch(()=>({}));t.ok&&M(a.users||[])}catch{}finally{L(!1)}}};(0,l.useEffect)(()=>{let e=!1;return(async()=>{let t=await ed();e||await Promise.all([eo(),ec()]),e||t?.isAdmin===!1||await em("")})(),()=>{e=!0}},[]);let ex=async()=>{if(el&&K.trim()){J(!0),p("");try{let e=await fetch("/api/keys",{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKeyDefaultModel:K.trim()})}),t=await e.json().catch(()=>({}));if(!e.ok)throw Error(t.error||"Failed to save default model");let a=String(t.apiKeyDefaultModel||"").trim();_(a),B(a)}catch(e){p(e.message||"Failed to save default model")}finally{J(!1)}}},eu=async()=>{if(b.trim()){if(!U.trim())return void p("Default API key model must be configured before creating API keys.");F(!0),p("");try{let e=el?i(k||j||er):void 0,t=await fetch("/api/keys",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:b.trim(),ownerUser:e})}),a=await t.json().catch(()=>({}));if(!t.ok)throw Error(a.error||"Failed to create key");C(a.key),g(""),N(""),f(!1),await eo()}catch(e){p(e.message||"Failed to create key")}finally{F(!1)}}},eh=async(e,t)=>{let a=await fetch(`/api/keys/${e}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}),s=await a.json().catch(()=>({}));return a.ok?(await eo(),!0):(p(s.error||"Failed to update key"),!1)},ep=async()=>{if(!G)return;let e=d(W);if(0===e.length)return void p("Select at least one model before saving API key access.");et(!0),p("");try{await eh(G.id,{allowedModels:e})&&(Q(null),X([]))}finally{et(!1)}},ey=T||!b.trim()||!U.trim();return(0,s.jsxs)("div",{className:"space-y-6",children:[(0,s.jsxs)("div",{className:"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",children:[(0,s.jsxs)("div",{children:[(0,s.jsxs)("h1",{className:"flex items-center gap-2 text-3xl font-bold text-text-main",children:[(0,s.jsx)("span",{className:"material-symbols-outlined text-primary",children:"vpn_key"}),"API Key Management"]}),(0,s.jsx)("p",{className:"mt-1 text-sm text-text-muted",children:el?"Admins can create API keys, assign owners, and control model access.":"You can manage API keys created by you or assigned to your account."})]}),(0,s.jsxs)(r.$n,{onClick:()=>f(!0),disabled:!U.trim(),children:[(0,s.jsx)("span",{className:"material-symbols-outlined mr-1 text-[18px]",children:"add"}),"Create API Key"]})]}),h?(0,s.jsx)("div",{className:"rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-500/30 dark:bg-red-500/10 dark:text-red-300",children:h}):null,(0,s.jsx)(r.Zp,{children:(0,s.jsxs)("div",{className:"flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between",children:[(0,s.jsxs)("div",{className:"min-w-0",children:[(0,s.jsxs)("div",{className:"flex items-center gap-2 text-lg font-semibold text-text-main",children:[(0,s.jsx)("span",{className:"material-symbols-outlined text-primary",children:"rule"}),"Default model for new API keys"]}),(0,s.jsx)("p",{className:"mt-1 text-sm text-text-muted",children:"New API keys are restricted to this model by default. The default model must be set before anyone can create API keys."}),(0,s.jsx)("div",{className:"mt-3",children:U?(0,s.jsx)("span",{className:"rounded-full border border-primary/30 bg-primary/10 px-3 py-1 font-mono text-xs text-primary",children:U}):(0,s.jsx)("span",{className:"rounded-full bg-red-500/10 px-3 py-1 text-xs text-red-600",children:"No default model configured"})})]}),el?(0,s.jsxs)("div",{className:"flex flex-col gap-2 sm:min-w-[360px]",children:[(0,s.jsxs)("div",{className:"flex gap-2",children:[(0,s.jsx)("button",{type:"button",onClick:()=>Z(!0),className:"min-w-0 flex-1 rounded-xl border border-border bg-background px-3 py-2 text-left font-mono text-xs text-text-main transition hover:border-primary/50",children:K||"Select default model..."}),(0,s.jsx)(r.$n,{onClick:ex,disabled:q||!K.trim()||!ei,children:q?"Saving...":"Save"})]}),(0,s.jsx)("p",{className:"text-xs text-text-muted",children:"Model picker includes provider models, custom models, aliases, and combos."})]}):null]})}),w?(0,s.jsx)(r.Zp,{className:"border-green-200 bg-green-50/60 dark:border-green-500/30 dark:bg-green-500/10",children:(0,s.jsxs)("div",{className:"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",children:[(0,s.jsxs)("div",{children:[(0,s.jsx)("div",{className:"font-semibold text-green-700 dark:text-green-300",children:"API key created"}),(0,s.jsx)("div",{className:"mt-1 break-all font-mono text-xs text-text-main",children:w}),(0,s.jsxs)("div",{className:"mt-1 text-xs text-text-muted",children:["Copy it now. New keys are restricted to ",U||"the default model"," unless an admin edits model access."]})]}),(0,s.jsxs)("div",{className:"flex gap-2",children:[(0,s.jsx)(r.$n,{variant:"secondary",onClick:()=>es(w,"created"),children:"created"===ea?"Copied":"Copy"}),(0,s.jsx)(r.$n,{variant:"secondary",onClick:()=>C(""),children:"Dismiss"})]})]})}):null,(0,s.jsx)(r.Zp,{children:x?(0,s.jsxs)("div",{className:"py-10 text-center text-sm text-text-muted",children:[(0,s.jsx)("span",{className:"material-symbols-outlined mr-2 animate-spin align-middle",children:"progress_activity"}),"Loading API keys..."]}):0===a.length?(0,s.jsxs)("div",{className:"py-12 text-center text-sm text-text-muted",children:[(0,s.jsx)("span",{className:"material-symbols-outlined mb-2 block text-4xl",children:"vpn_key_off"}),"No API keys yet."]}):(0,s.jsx)("div",{className:"space-y-3",children:a.map(e=>{var t;let a=S.has(e.id),l=e.ownerUser||e.createdByUser||"unassigned",n=d(e.allowedModels);return(0,s.jsx)("div",{className:"rounded-2xl border border-border bg-background p-4",children:(0,s.jsxs)("div",{className:"flex flex-col gap-3 lg:flex-row lg:items-start lg:justify-between",children:[(0,s.jsxs)("div",{className:"min-w-0 flex-1 space-y-2",children:[(0,s.jsxs)("div",{className:"flex flex-wrap items-center gap-2",children:[(0,s.jsx)("h3",{className:"font-semibold text-text-main",children:e.name||"API Key"}),(0,s.jsx)("span",{className:`rounded-full px-2 py-0.5 text-xs ${e.isActive?"bg-green-500/10 text-green-600":"bg-red-500/10 text-red-600"}`,children:e.isActive?"Active":"Disabled"}),(0,s.jsxs)("span",{className:"rounded-full bg-primary/10 px-2 py-0.5 text-xs text-primary",children:["Owner: ",l]}),n.length>0?(0,s.jsxs)("span",{className:"rounded-full bg-blue-500/10 px-2 py-0.5 text-xs text-blue-600",children:[n.length," model(s)"]}):(0,s.jsx)("span",{className:"rounded-full bg-amber-500/10 px-2 py-0.5 text-xs text-amber-700",children:"Legacy full access"})]}),(0,s.jsx)("div",{className:"break-all font-mono text-xs text-text-muted",children:a?e.key:(t=e.key)?t.length>12?`${t.slice(0,8)}...${t.slice(-4)}`:t:""}),(0,s.jsxs)("div",{className:"text-xs text-text-muted",children:["Created: ",e.createdAt?new Date(e.createdAt).toLocaleString():"unknown",e.createdByUser?` \xb7 Created by: ${e.createdByUser}`:""]}),(0,s.jsx)("div",{className:"pt-1",children:(0,s.jsx)(o,{models:n,emptyLabel:"Legacy full model access"})})]}),(0,s.jsxs)("div",{className:"flex flex-wrap items-center gap-2",children:[(0,s.jsx)(r.lM,{checked:!1!==e.isActive,onChange:t=>eh(e.id,{isActive:t})}),(0,s.jsx)(r.$n,{variant:"secondary",onClick:()=>{var t;return t=e.id,void A(e=>{let a=new Set(e);return a.has(t)?a.delete(t):a.add(t),a})},children:a?"Hide":"Show"}),(0,s.jsx)(r.$n,{variant:"secondary",onClick:()=>es(e.key,e.id),children:ea===e.id?"Copied":"Copy"}),(0,s.jsx)(r.$n,{variant:"secondary",onClick:()=>{var t;let a;return t=e.id,a=prompt("New API key name",e.name||""),void(a?.trim()&&eh(t,{name:a.trim()}))},children:"Rename"}),el?(0,s.jsx)(r.$n,{variant:"secondary",onClick:()=>{el&&(Q(e),X(d(e.allowedModels)))},children:"Models"}):null,el?(0,s.jsx)(r.$n,{variant:"secondary",onClick:()=>((e,t)=>{if(!el)return;let a=prompt("Assign owner username/email",t||er||"");null!==a&&eh(e,{ownerUser:i(a)})})(e.id,l),children:"Assign"}):null,(0,s.jsx)(r.$n,{variant:"danger",onClick:()=>{var t;return t=e.id,void $({title:"Delete API Key",message:"Delete this API key? This cannot be undone.",onConfirm:async()=>{$(null),(await fetch(`/api/keys/${t}`,{method:"DELETE"})).ok&&m(e=>e.filter(e=>e.id!==t))}})},children:"Delete"})]})]})},e.id)})})}),(0,s.jsx)(r.aF,{isOpen:y,onClose:()=>f(!1),title:"Create API Key",children:(0,s.jsxs)("div",{className:"space-y-4",children:[U.trim()?(0,s.jsxs)("div",{className:"rounded-xl border border-blue-200 bg-blue-50 px-3 py-2 text-sm text-blue-700 dark:border-blue-500/30 dark:bg-blue-500/10 dark:text-blue-300",children:["This key will be created with access to the default model only: ",(0,s.jsx)("span",{className:"font-mono",children:U}),"."]}):(0,s.jsx)("div",{className:"rounded-xl border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700 dark:border-red-500/30 dark:bg-red-500/10 dark:text-red-300",children:"Admin must set a default model before API keys can be created."}),(0,s.jsx)(r.pd,{label:"Name",value:b,onChange:e=>g(e.target.value),placeholder:"e.g. VSCode, LibreChat, team-user"}),el?(0,s.jsxs)("div",{className:"space-y-2",children:[(0,s.jsx)("label",{className:"text-sm font-medium text-text-main",children:"Owner user"}),(0,s.jsxs)("div",{className:"flex gap-2",children:[(0,s.jsx)(r.pd,{value:D,onChange:e=>O(e.target.value),placeholder:"Search LDAP users"}),(0,s.jsx)(r.$n,{variant:"secondary",onClick:()=>em(D),disabled:E,children:E?"Loading":"Load"})]}),(0,s.jsxs)("select",{value:j,onChange:e=>v(e.target.value),className:"w-full rounded-xl border border-border bg-background px-3 py-2 text-sm text-text-main outline-none focus:border-primary",children:[(0,s.jsxs)("option",{value:er,children:["Assign to me (",er||"local",")"]}),en.map(e=>(0,s.jsx)("option",{value:e.id,children:e.label},e.id))]}),(0,s.jsx)(r.pd,{label:"Manual owner override",value:k,onChange:e=>N(e.target.value),placeholder:"username or email, optional",hint:"Use this if LDAP search is unavailable or the user is not listed."})]}):(0,s.jsxs)("div",{className:"rounded-xl bg-surface-2 px-3 py-2 text-sm text-text-muted",children:["Owner: ",er||"current user"]}),(0,s.jsxs)("div",{className:"flex justify-end gap-2",children:[(0,s.jsx)(r.$n,{variant:"secondary",onClick:()=>f(!1),children:"Cancel"}),(0,s.jsx)(r.$n,{onClick:eu,disabled:ey,children:T?"Creating...":"Create"})]})]})}),(0,s.jsx)(c,{open:!!G,keyName:G?.name,models:W,setModels:X,activeProviders:H,modelAliases:Y,onClose:()=>{Q(null),X([])},onSave:ep,saving:ee}),(0,s.jsx)(r.rq,{isOpen:V,onClose:()=>Z(!1),onSelect:e=>{let t=String(e?.value||e?.name||e||"").trim();t&&(B(t),Z(!1))},activeProviders:H,modelAliases:Y,title:"Select default API key model",selectedModel:K,addedModelValues:K?[K]:[]}),P?(0,s.jsx)(r.uo,{isOpen:!0,onClose:()=>$(null),onConfirm:P.onConfirm,title:P.title,message:P.message,confirmText:"Delete",cancelText:"Cancel",variant:"danger"}):null]})}},84223:(e,t,a)=>{Promise.resolve().then(a.bind(a,56484))}},e=>{e.O(0,[2574,3862,1051,1321,5497,8441,3794,7358],()=>e(e.s=84223)),_N_E=e.O()}]);
@@ -1 +0,0 @@
1
- (self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[3649],{20061:(e,t,a)=>{Promise.resolve().then(a.bind(a,47295))},47295:(e,t,a)=>{"use strict";a.r(t),a.d(t,{default:()=>A});var l=a(95155),r=a(12115),n=a(44923),s=a(87256),o=a(39768),i=a(82639),d=a(35497),c=a(11059),m=a(28777),u=a(52679);let x=/^[a-zA-Z0-9_.\-]+$/,b=[{value:"global",label:"Global / current behavior",description:"Keep the existing behavior: every request advances the combo rotation globally."},{value:"client_ip",label:"Client IP",description:"Keep the same starting model for the same client IP until TTL expires."},{value:"client_session",label:"Client IP + chat session",description:"Best for chat tools. Keeps the same model for the same machine and chat session."}],p=[{value:"fallback",label:"Fallback priority",description:"Current behavior: try combo models in the order shown, then fallback on failure."},{value:"round-robin",label:"Round Robin",description:"Rotate which active model starts first. Sticky options below still apply."},{value:"least-latency",label:"Least latency",description:"Prefer the target with the best health/latency score. Useful for LAN Ollama."},{value:"p2c",label:"P2C",description:"Power of Two Choices: sample two healthy targets and pick the less busy one."}];function h(e={}){return Array.isArray(e.disabledModels)?[...new Set(e.disabledModels.map(e=>String(e||"").trim()).filter(Boolean))]:[]}function g(e={}){let t=e.fallbackStrategy||"fallback";return{...e,fallbackStrategy:t,stickyScope:e.stickyScope||"global",stickyTtlMinutes:Number(e.stickyTtlMinutes||120),stickyFallbackToClientIp:!1!==e.stickyFallbackToClientIp,lanHealthEnabled:!1!==e.lanHealthEnabled,healthCheckPath:e.healthCheckPath||"/api/tags",healthCheckIntervalMs:Number(e.healthCheckIntervalMs||15e3),requiredTags:Array.isArray(e.requiredTags)?e.requiredTags:[],modelTagRules:e.modelTagRules||e.modelTags||e.tagRules||{},disabledModels:h(e)}}function f(e=[]){return Array.isArray(e)?e.join(", "):String(e||"")}function y(e=""){return String(e||"").split(/[\n,;]+/).map(e=>e.trim()).filter(Boolean)}function v(e={}){return e&&"object"==typeof e?Object.entries(e).map(([e,t])=>`${e}: ${Array.isArray(t)?t.join(", "):String(t||"")}`).join("\n"):""}function j(e){return String(e||"").trim()}function k(e){return"string"==typeof e?e:e&&"object"==typeof e&&(e.value||e.model||e.id)||""}function N(e={}){let t=j(e.provider);return j(e.providerSpecificData?.prefix)||j(e.prefix)||j((0,u.wG)(t))||t}function w(e={}){return j(e.email)||j(e.accountName)||j(e.displayName)||j(e.name)||j(e.providerSpecificData?.nodeName)||j(e.id)}function C(e){return e&&"object"==typeof e?j(e.connectionId||e.connection_id):""}function S(e){let t=k(e),a=C(e);return t?a?`${t}@@${a}`:t:""}function T(e){return"string"==typeof e?e:e&&"object"==typeof e?j(e.label)||j(e.displayName)||j(e.name)||k(e):""}function $(e){if(!e||"object"!=typeof e)return k(e);let t=k(e);return t?Object.fromEntries(Object.entries({model:t,connectionId:C(e)||void 0,label:T(e)||void 0,provider:j(e.provider)||void 0,accountName:j(e.accountName||e.account_name)||void 0,weight:Number(e.weight||0)>0?Number(e.weight):void 0}).filter(([,e])=>void 0!==e&&""!==e)):""}function E(e,t=!0){let a=$(e),l=S(a);return{value:l,enabled:t,target:a,label:T(a)||l,subtitle:function(e){if(!e||"object"!=typeof e)return"";let t=C(e),a=k(e);return t?`Pinned account • ${t}${a?` • ${a}`:""}`:""}(a)}}function M(e={}){return j(e.defaultModel)||j(e.providerSpecificData?.defaultModel)||j(e.model)||""}function O(e,t,a,l=""){let r=j(a);if(!r)return;let n=j(l)||r;t.has(r)||(t.add(r),e.push({value:r,label:n}))}function _(e={},t={}){let a=[],l=new Set,r=j(e.provider),n=[...new Set([N(e),j((0,u.wG)(r)),r].filter(Boolean))];for(let e of(0,m.KC)(r)||[])e?.type&&"llm"!==e.type||O(a,l,e.id,e.name||e.id);for(let[e,r]of Object.entries(t||{})){let t=j(r),s=n.find(e=>e&&t.startsWith(`${e}/`));if(!s)continue;let o=t.slice(s.length+1);O(a,l,o,e||o)}return O(a,l,M(e),"Default model"),a}function P(e={},t=[]){let a={...e},l=[...new Set((t||[]).map(e=>String(e||"").trim()).filter(Boolean))];return l.length>0?a.disabledModels=l:delete a.disabledModels,Object.keys(a).length?a:null}function A(){let[e,t]=(0,r.useState)([]),[a,n]=(0,r.useState)(!0),[s,o]=(0,r.useState)(!1),[i,m]=(0,r.useState)(null),[u,x]=(0,r.useState)([]),[b,p]=(0,r.useState)({}),[f,y]=(0,r.useState)(null),{copied:v,copy:j}=(0,c.C)();(0,r.useEffect)(()=>{k()},[]);let k=async()=>{try{let[e,a,l]=await Promise.all([fetch("/api/combos"),fetch("/api/providers"),fetch("/api/settings")]),r=await e.json(),n=await a.json(),s=l.ok?await l.json():{};e.ok&&t((r.combos||[]).filter(e=>!e.kind)),a.ok&&x(n.connections||[]),p(s.comboStrategies||{})}catch(e){console.log("Error fetching data:",e)}finally{n(!1)}},N=async e=>{await fetch("/api/settings",{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({comboStrategies:e})}),p(e)},w=async(e,t)=>{let a={...b},l=P(a[e]||{},t);l?a[e]=l:delete a[e],await N(a)},C=async e=>{let{disabledModels:t=[],...a}=e;try{let e=await fetch("/api/combos",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(a)});if(e.ok)t.length>0&&await w(a.name,t),await k(),o(!1);else{let t=await e.json();alert(t.error||"Failed to create combo")}}catch(e){console.log("Error creating combo:",e)}},S=async(e,t)=>{let{disabledModels:a=[],...l}=t,r=i?.name;try{let t=await fetch(`/api/combos/${e}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(l)});if(t.ok){let e={...b};r&&r!==l.name&&e[r]&&(e[l.name]=e[r],delete e[r]);let t=P(e[l.name]||{},a);t?e[l.name]=t:delete e[l.name],await N(e),await k(),m(null)}else{let e=await t.json();alert(e.error||"Failed to update combo")}}catch(e){console.log("Error updating combo:",e)}},T=async a=>{let l=e.find(e=>e.id===a);y({title:"Delete Combo",message:"Delete this combo?",onConfirm:async()=>{y(null);try{if((await fetch(`/api/combos/${a}`,{method:"DELETE"})).ok){if(l?.name&&b[l.name]){let e={...b};delete e[l.name],await N(e)}t(e.filter(e=>e.id!==a))}}catch(e){console.log("Error deleting combo:",e)}}})},$=async(e,t)=>{try{let a={...b},l=function(e={}){let t={...e};return t.disabledModels=h(e),"fallback"===(t.fallbackStrategy||"fallback")&&delete t.fallbackStrategy,t.disabledModels.length||delete t.disabledModels,t.requiredTags?.length||delete t.requiredTags,t.modelTagRules&&0!==Object.keys(t.modelTagRules).length||delete t.modelTagRules,"/api/tags"===t.healthCheckPath&&delete t.healthCheckPath,15e3===Number(t.healthCheckIntervalMs)&&delete t.healthCheckIntervalMs,!0===t.lanHealthEnabled&&delete t.lanHealthEnabled,Object.keys(t).length?t:null}({...g(a[e]),...t});l?a[e]=l:delete a[e],await N(a)}catch(e){console.log("Error updating combo routing settings:",e)}};return a?(0,l.jsx)("div",{className:"space-y-6",children:[1,2,3].map(e=>(0,l.jsx)("div",{className:"h-40 animate-pulse rounded-2xl bg-surface"},e))}):(0,l.jsxs)("div",{className:"space-y-8",children:[(0,l.jsxs)("div",{className:"flex items-start justify-between gap-4",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("h1",{className:"text-2xl font-semibold text-text-main",children:"Combos"}),(0,l.jsx)("p",{className:"mt-1 text-sm text-text-muted",children:"Create model combos with fallback, round-robin, least-latency, P2C, and LAN Ollama health-aware routing."})]}),(0,l.jsxs)("button",{type:"button",onClick:()=>o(!0),className:"inline-flex items-center justify-center gap-2 rounded-xl bg-primary px-4 py-2 text-sm font-medium text-white hover:opacity-90",children:[(0,l.jsx)("span",{className:"material-symbols-outlined text-[18px]",children:"add"}),"Create Combo"]})]}),0===e.length?(0,l.jsxs)("div",{className:"rounded-2xl border border-dashed border-border p-10 text-center",children:[(0,l.jsx)("span",{className:"material-symbols-outlined text-4xl text-text-muted",children:"layers"}),(0,l.jsx)("h2",{className:"mt-3 text-lg font-semibold text-text-main",children:"No combos yet"}),(0,l.jsx)("p",{className:"mt-1 text-sm text-text-muted",children:"Create model combos with fallback support."}),(0,l.jsx)("button",{type:"button",onClick:()=>o(!0),className:"mt-4 rounded-xl bg-primary px-4 py-2 text-sm font-medium text-white hover:opacity-90",children:"Create Combo"})]}):(0,l.jsx)("div",{className:"grid gap-5 md:grid-cols-2 xl:grid-cols-3",children:e.map(e=>(0,l.jsx)(I,{combo:e,copied:v,onCopy:j,onEdit:()=>m(e),onDelete:()=>T(e.id),comboStrategy:b[e.name]||{},onUpdateComboStrategy:t=>$(e.name,t)},e.id))}),(0,l.jsx)(q,{isOpen:s,onClose:()=>o(!1),onSave:C,activeProviders:u}),(0,l.jsx)(q,{isOpen:!!i,combo:i,comboStrategy:i&&b[i.name]||{},onClose:()=>m(null),onSave:e=>S(i.id,e),activeProviders:u}),(0,l.jsx)(d.uo,{isOpen:!!f,onClose:()=>y(null),onConfirm:f?.onConfirm,title:f?.title||"Confirm",message:f?.message,variant:"danger"})]})}function I({combo:e,copied:t,onCopy:a,onEdit:n,onDelete:s,comboStrategy:o={},onUpdateComboStrategy:i}){let d=g(o||{}),c=d.fallbackStrategy||"fallback",m=p.find(e=>e.value===c)||p[0],u=d.stickyScope||"global",x=b.find(e=>e.value===u)||b[0],j=h(o),[N,w]=(0,r.useState)(f(d.requiredTags)),[C,$]=(0,r.useState)(v(d.modelTagRules));return(0,r.useEffect)(()=>{w(f(d.requiredTags)),$(v(d.modelTagRules))},[e.name,JSON.stringify(d.requiredTags||[]),JSON.stringify(d.modelTagRules||{})]),(0,l.jsxs)("div",{className:"rounded-2xl border border-border bg-background p-5 shadow-sm",children:[(0,l.jsxs)("div",{className:"mb-4 flex items-start justify-between gap-3",children:[(0,l.jsxs)("div",{className:"min-w-0",children:[(0,l.jsxs)("div",{className:"flex items-center gap-2",children:[(0,l.jsx)("span",{className:"material-symbols-outlined text-primary",children:"layers"}),(0,l.jsx)("h3",{className:"truncate text-lg font-semibold text-text-main",children:e.name})]}),(0,l.jsxs)("p",{className:"mt-1 text-sm text-text-muted",children:[e.models.length," active model(s)",j.length>0?` • ${j.length} disabled`:""]})]}),(0,l.jsxs)("div",{className:"flex items-center gap-1",children:[(0,l.jsx)(R,{icon:t===`combo-${e.id}`?"check":"content_copy",title:"Copy combo name",onClick:()=>a(e.name,`combo-${e.id}`)}),(0,l.jsx)(R,{icon:"edit",title:"Edit",onClick:n}),(0,l.jsx)(R,{icon:"delete",title:"Delete",onClick:s,danger:!0})]})]}),(0,l.jsxs)("div",{className:"mb-4 flex flex-wrap gap-2",children:[0===e.models.length?(0,l.jsx)("span",{className:"rounded-lg border border-border px-3 py-1 text-xs text-text-muted",children:"No active models"}):e.models.slice(0,5).map(e=>(0,l.jsx)("span",{className:"rounded-lg border border-border bg-surface px-3 py-1 font-mono text-xs text-text-main",title:k(e),children:T(e)},S(e))),e.models.length>5&&(0,l.jsxs)("span",{className:"rounded-lg border border-border px-3 py-1 text-xs text-text-muted",children:["+",e.models.length-5," more"]}),j.length>0&&(0,l.jsxs)("span",{className:"rounded-lg border border-red-200 bg-red-50 px-3 py-1 text-xs font-medium text-red-600 dark:border-red-500/30 dark:bg-red-500/10 dark:text-red-300",children:[j.length," disabled"]})]}),(0,l.jsxs)("div",{className:"rounded-xl border border-border bg-surface/50 p-4",children:[(0,l.jsxs)("div",{className:"mb-3 flex items-start justify-between gap-3",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("h4",{className:"font-semibold text-text-main",children:"Routing Strategy"}),(0,l.jsx)("p",{className:"mt-1 text-sm text-text-muted",children:m.description})]}),(0,l.jsx)("span",{className:"rounded-lg border border-primary/20 bg-primary/10 px-2 py-1 text-[11px] font-semibold text-primary",children:m.label})]}),(0,l.jsxs)("label",{className:"mb-3 block",children:[(0,l.jsx)("span",{className:"mb-1 block text-[11px] font-semibold uppercase tracking-wide text-text-muted",children:"Strategy"}),(0,l.jsx)("select",{value:c,onChange:e=>i({fallbackStrategy:e.target.value}),className:"w-full rounded-lg border border-border bg-background px-2 py-2 text-sm text-text-main outline-none focus:border-primary",children:p.map(e=>(0,l.jsx)("option",{value:e.value,children:e.label},e.value))})]}),("least-latency"===c||"p2c"===c)&&(0,l.jsxs)("div",{className:"mb-3 rounded-xl border border-emerald-500/20 bg-emerald-500/5 p-3",children:[(0,l.jsxs)("div",{className:"mb-2 flex items-center gap-2 text-sm font-semibold text-emerald-600 dark:text-emerald-300",children:[(0,l.jsx)("span",{className:"material-symbols-outlined text-[18px]",children:"monitor_heart"}),"LAN health aware routing"]}),(0,l.jsxs)("label",{className:"mb-3 flex items-start gap-2 text-xs text-text-muted",children:[(0,l.jsx)("input",{type:"checkbox",checked:!1!==d.lanHealthEnabled,onChange:e=>i({lanHealthEnabled:e.target.checked}),className:"mt-0.5 h-3.5 w-3.5 accent-primary"}),"Use active health state and circuit breaker data when ranking combo targets."]}),(0,l.jsxs)("div",{className:"grid gap-2 sm:grid-cols-2",children:[(0,l.jsxs)("label",{className:"block",children:[(0,l.jsx)("span",{className:"mb-1 block text-[11px] font-semibold uppercase tracking-wide text-text-muted",children:"Health path"}),(0,l.jsx)("input",{value:d.healthCheckPath||"/api/tags",onChange:e=>i({healthCheckPath:e.target.value||"/api/tags"}),placeholder:"/api/tags",className:"w-full rounded-lg border border-border bg-background px-2 py-1.5 font-mono text-xs text-text-main outline-none focus:border-primary"})]}),(0,l.jsxs)("label",{className:"block",children:[(0,l.jsx)("span",{className:"mb-1 block text-[11px] font-semibold uppercase tracking-wide text-text-muted",children:"Check interval ms"}),(0,l.jsx)("input",{type:"number",min:"1000",value:d.healthCheckIntervalMs||15e3,onChange:e=>i({healthCheckIntervalMs:Number(e.target.value)||15e3}),className:"w-full rounded-lg border border-border bg-background px-2 py-1.5 text-xs text-text-main outline-none focus:border-primary"})]})]}),(0,l.jsxs)("label",{className:"mt-3 block",children:[(0,l.jsx)("span",{className:"mb-1 block text-[11px] font-semibold uppercase tracking-wide text-text-muted",children:"Required endpoint tags"}),(0,l.jsx)("input",{value:N,onChange:e=>w(e.target.value),onBlur:()=>i({requiredTags:y(N)}),onKeyDown:e=>"Enter"===e.key&&e.currentTarget.blur(),placeholder:"gpu, vram:8gb",className:"w-full rounded-lg border border-border bg-background px-2 py-1.5 font-mono text-xs text-text-main outline-none focus:border-primary"}),(0,l.jsx)("span",{className:"mt-1 block text-[11px] text-text-muted",children:"Optional. Every routed endpoint must contain these tags."})]}),(0,l.jsxs)("label",{className:"mt-3 block",children:[(0,l.jsx)("span",{className:"mb-1 block text-[11px] font-semibold uppercase tracking-wide text-text-muted",children:"Model tag rules"}),(0,l.jsx)("textarea",{value:C,onChange:e=>$(e.target.value),onBlur:()=>i({modelTagRules:function(e=""){let t={};for(let a of String(e||"").split(/\n+/)){let e=a.trim();if(!e)continue;let l=e.indexOf(":");if(-1===l)continue;let r=e.slice(0,l).trim(),n=y(e.slice(l+1));r&&n.length&&(t[r]=n)}return t}(C)}),placeholder:"qwen3:32b: gpu, vram:16gb\nqwen3:8b: model_support:qwen3:8b",rows:3,className:"w-full rounded-lg border border-border bg-background px-2 py-1.5 font-mono text-xs text-text-main outline-none focus:border-primary"}),(0,l.jsx)("span",{className:"mt-1 block text-[11px] text-text-muted",children:"One rule per line: model: tag1, tag2. Large models can be forced to GPU/VRAM endpoints."})]})]}),"round-robin"===c&&(0,l.jsxs)("div",{className:"mt-4 rounded-xl border border-primary/15 bg-background p-3",children:[(0,l.jsxs)("div",{className:"mb-3 flex items-center gap-2 text-sm font-semibold text-primary",children:[(0,l.jsx)("span",{className:"material-symbols-outlined text-[18px]",children:"sticky_note_2"}),"Sticky Round Robin"]}),(0,l.jsx)("p",{className:"mb-3 text-xs text-text-muted",children:"Optional sticky behavior for this combo only. Leave it on Global to keep the current behavior."}),(0,l.jsxs)("label",{className:"mb-3 block",children:[(0,l.jsx)("span",{className:"mb-1 block text-[11px] font-semibold uppercase tracking-wide text-text-muted",children:"Sticky mode"}),(0,l.jsx)("select",{value:u,onChange:e=>i({stickyScope:e.target.value}),className:"w-full rounded-lg border border-border bg-background px-2 py-1.5 text-xs text-text-main outline-none focus:border-primary",children:b.map(e=>(0,l.jsx)("option",{value:e.value,children:e.label},e.value))}),(0,l.jsx)("span",{className:"mt-1 block text-[11px] text-text-muted",children:x.description})]}),(0,l.jsxs)("label",{className:"mb-3 block",children:[(0,l.jsx)("span",{className:"mb-1 block text-[11px] font-semibold uppercase tracking-wide text-text-muted",children:"Sticky TTL minutes"}),(0,l.jsx)("input",{type:"number",min:"1",value:d.stickyTtlMinutes||120,onChange:e=>i({stickyTtlMinutes:Number(e.target.value)||120}),className:"w-full rounded-lg border border-border bg-background px-2 py-1.5 text-xs text-text-main outline-none focus:border-primary"}),(0,l.jsx)("span",{className:"mt-1 block text-[11px] text-text-muted",children:"Sticky assignments are in-memory and expire after this time. 120 minutes is recommended."})]}),(0,l.jsxs)("label",{className:"flex items-start gap-2 text-xs text-text-muted",children:[(0,l.jsx)("input",{type:"checkbox",checked:!1!==d.stickyFallbackToClientIp,onChange:e=>i({stickyFallbackToClientIp:e.target.checked}),className:"mt-0.5 h-3.5 w-3.5 accent-primary"}),"If chat session id is missing, fall back to Client IP instead of global rotation."]})]})]})]})}function R({icon:e,title:t,onClick:a,danger:r=!1}){return(0,l.jsx)("button",{type:"button",onClick:e=>{e.stopPropagation(),a?.()},className:`rounded-lg p-2 transition ${r?"text-red-500 hover:bg-red-500/10":"text-text-muted hover:bg-background hover:text-primary"}`,title:t,"aria-label":t,children:(0,l.jsx)("span",{className:"material-symbols-outlined text-[20px]",children:e})})}function D({id:e,index:t,model:a,subtitle:n="",enabled:i,isFirst:d,isLast:c,onEdit:m,onMoveUp:u,onMoveDown:x,onRemove:b,onToggleEnabled:p}){let{attributes:h,listeners:g,setNodeRef:f,transform:y,isDragging:v}=(0,s.gl)({id:e}),j={transform:o.Ks.Transform.toString(y),opacity:v?.4:1,zIndex:v?999:void 0},[k,N]=(0,r.useState)(!1),[w,C]=(0,r.useState)(a);(0,r.useEffect)(()=>{C(a)},[a]);let S=()=>{let e=w.trim();e&&e!==a?m(e):C(a),N(!1)};return(0,l.jsxs)("div",{ref:f,style:j,className:`flex items-center gap-3 rounded-xl border p-3 transition ${i?"border-border bg-background":"border-red-200 bg-red-50/70 opacity-75 dark:border-red-500/30 dark:bg-red-500/10"}`,children:[(0,l.jsx)("button",{type:"button",className:"cursor-grab text-text-muted active:cursor-grabbing",...h,...g,title:"Drag to reorder",children:(0,l.jsx)("span",{className:"material-symbols-outlined text-[20px]",children:"drag_indicator"})}),(0,l.jsx)("span",{className:`flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-xs font-semibold ${i?"bg-primary/10 text-primary":"bg-red-100 text-red-600 dark:bg-red-500/20 dark:text-red-300"}`,children:t+1}),k?(0,l.jsx)("input",{autoFocus:!0,value:w,onChange:e=>C(e.target.value),onBlur:S,onKeyDown:e=>{"Enter"===e.key&&S(),"Escape"===e.key&&(C(a),N(!1))},className:"min-w-0 flex-1 rounded border border-primary/40 bg-background px-2 py-1 font-mono text-xs text-text-main outline-none"}):(0,l.jsxs)("button",{type:"button",onClick:()=>N(!0),className:"min-w-0 flex-1 text-left",title:"Click to edit model",children:[(0,l.jsx)("span",{className:`block truncate font-mono text-xs ${i?"text-text-main":"text-text-muted line-through"}`,children:a}),n?(0,l.jsx)("span",{className:"mt-0.5 block truncate text-[11px] text-text-muted",children:n}):null]}),(0,l.jsx)("button",{type:"button",onClick:p,className:`rounded-lg p-1.5 transition ${i?"text-green-600 hover:bg-green-500/10":"text-red-500 hover:bg-red-500/10"}`,title:i?"Disable this model in combo":"Enable this model in combo",children:(0,l.jsx)("span",{className:"material-symbols-outlined text-[20px]",children:i?"toggle_on":"toggle_off"})}),(0,l.jsx)("button",{type:"button",onClick:u,disabled:d,className:"rounded-lg p-1.5 text-text-muted transition hover:bg-surface disabled:opacity-30",title:"Move up",children:(0,l.jsx)("span",{className:"material-symbols-outlined text-[20px]",children:"arrow_upward"})}),(0,l.jsx)("button",{type:"button",onClick:x,disabled:c,className:"rounded-lg p-1.5 text-text-muted transition hover:bg-surface disabled:opacity-30",title:"Move down",children:(0,l.jsx)("span",{className:"material-symbols-outlined text-[20px]",children:"arrow_downward"})}),(0,l.jsx)("button",{type:"button",onClick:b,className:"rounded-lg p-1.5 text-red-500 transition hover:bg-red-500/10",title:"Remove",children:(0,l.jsx)("span",{className:"material-symbols-outlined text-[20px]",children:"close"})})]})}function q({isOpen:e,combo:t,comboStrategy:a={},onClose:o,onSave:c,activeProviders:m,kindFilter:b=null}){let[p,g]=(0,r.useState)(t?.name||""),[f,y]=(0,r.useState)([]),[v,k]=(0,r.useState)(!1),[C,T]=(0,r.useState)(!1),[O,P]=(0,r.useState)(""),[A,I]=(0,r.useState)({}),[R,F]=(0,r.useState)(""),[B,L]=(0,r.useState)(""),H=a||{},z=(0,n.FR)((0,n.MS)(n.AN,{activationConstraint:{distance:5}}),(0,n.MS)(n.uN,{coordinateGetter:s.JR}));(0,r.useEffect)(()=>{e&&(g(t?.name||""),y(function(e,t={}){let a=(e?.models||[]).map(e=>E(e,!0)).filter(e=>e.value),l=new Set(a.map(e=>e.value));return[...a,...h(t).filter(e=>!l.has(e)).map(e=>E(e,!1)).filter(e=>e.value)]}(t,H)),P(""),F(""),L(""))},[e,t?.id,t?.name]),(0,r.useEffect)(()=>{e&&G()},[e]);let G=async()=>{try{let e=await fetch("/api/models/alias");if(!e.ok)return;let t=await e.json();I(t.aliases||{})}catch(e){console.error("Error fetching modal data:",e)}},K=f.map((e,t)=>({uid:`item-${t}-${e.value}`,row:e})),J=f.map(e=>e.value),U=f.filter(e=>e.enabled).length,V=f.filter(e=>!e.enabled).length,W=(m||[]).filter(e=>e?.isActive!==!1),Z=W.find(e=>e.id===R)||null,Q=Z?_(Z,A):[],X=Q[0]?.value||(Z?M(Z):""),Y=e=>e.trim()?x.test(e)?(P(""),!0):(P("Only letters, numbers, -, _ and . allowed"),!1):(P("Name is required"),!1),ee=async()=>{if(!Y(p))return;let{models:e,disabledModels:t}=function(e=[]){let t=[],a=new Set;for(let l of e){let e=j(l?.value||S(l?.target));!e||a.has(e)||(a.add(e),t.push({...l,value:e,enabled:l?.enabled!==!1,target:$(l?.target||l?.value)}))}return{models:t.filter(e=>e.enabled).map(e=>e.target).filter(Boolean),disabledModels:t.filter(e=>!e.enabled).map(e=>e.value)}}(f);T(!0),await c({name:p.trim(),models:e,disabledModels:t}),T(!1)};if(!e)return null;let et=!!t;return(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)("div",{className:"fixed inset-0 z-[100] flex items-center justify-center bg-black/45 p-4 backdrop-blur-[1px]",children:(0,l.jsxs)("div",{className:"flex max-h-[88vh] w-full max-w-3xl flex-col overflow-hidden rounded-2xl border border-border bg-white shadow-2xl dark:bg-zinc-950",children:[(0,l.jsxs)("div",{className:"flex items-start justify-between border-b border-border px-5 py-4",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("h2",{className:"text-xl font-semibold text-text-main",children:et?"Edit Combo":"Create Combo"}),(0,l.jsx)("p",{className:"mt-1 text-sm text-text-muted",children:"Choose models, disable temporarily, and order active fallback priority."})]}),(0,l.jsx)("button",{type:"button",onClick:o,className:"rounded-lg p-1 text-text-muted transition hover:bg-surface hover:text-text-main",children:(0,l.jsx)("span",{className:"material-symbols-outlined",children:"close"})})]}),(0,l.jsxs)("div",{className:"min-h-0 flex-1 space-y-5 overflow-y-auto bg-white px-5 py-4 dark:bg-zinc-950",children:[(0,l.jsxs)("label",{className:"block",children:[(0,l.jsx)("span",{className:"mb-2 block text-sm font-semibold text-text-main",children:"Name"}),(0,l.jsx)("input",{value:p,onChange:e=>{let t=e.target.value;g(t),t?Y(t):P("")},placeholder:"premium-coding",className:`w-full rounded-xl border bg-white px-4 py-2.5 text-text-main outline-none transition focus:border-primary dark:bg-zinc-900 ${O?"border-red-400":"border-border"}`}),(0,l.jsx)("span",{className:`mt-1 block text-xs ${O?"text-red-500":"text-text-muted"}`,children:O||"Only letters, numbers, -, _ and . allowed"})]}),(0,l.jsxs)("div",{children:[(0,l.jsxs)("div",{className:"mb-3 flex items-center justify-between gap-3",children:[(0,l.jsxs)("div",{children:[(0,l.jsx)("h3",{className:"text-sm font-semibold text-text-main",children:"Models"}),(0,l.jsxs)("p",{className:"mt-1 text-xs text-text-muted",children:[U," active",V>0?`, ${V} disabled`:"",". Disabled models stay in this combo but are not routed."]})]}),(0,l.jsxs)("button",{type:"button",onClick:()=>k(!0),className:"inline-flex items-center gap-1 rounded-lg border border-border px-3 py-1.5 text-xs font-medium text-primary hover:border-primary/50",children:[(0,l.jsx)("span",{className:"material-symbols-outlined text-[18px]",children:"add"}),"Add Model"]})]}),(0,l.jsxs)("div",{className:"mb-4 rounded-xl border border-primary/15 bg-primary/5 p-3",children:[(0,l.jsxs)("div",{className:"mb-2 flex items-center gap-2 text-xs font-semibold uppercase tracking-wide text-primary",children:[(0,l.jsx)("span",{className:"material-symbols-outlined text-[18px]",children:"account_tree"}),"Add Provider Account Target"]}),(0,l.jsx)("p",{className:"mb-3 text-xs text-text-muted",children:"Pin this combo step to a specific provider account. Models come from the provider; the account only pins routing/load balancing."}),(0,l.jsxs)("div",{className:"grid gap-2 md:grid-cols-[minmax(0,1.5fr)_minmax(0,1fr)_auto]",children:[(0,l.jsxs)("select",{value:R,onChange:e=>{let t=e.target.value,a=W.find(e=>e.id===t),l=a?_(a,A):[];F(t),L(l[0]?.value||M(a||{})||"")},className:"min-w-0 rounded-lg border border-border bg-background px-3 py-2 text-xs text-text-main outline-none focus:border-primary",children:[(0,l.jsx)("option",{value:"",children:"Select provider account"}),W.map(e=>{let t=N(e),a=w(e),r=a&&a!==t?`${t} / ${a}`:t;return(0,l.jsx)("option",{value:e.id,children:r},e.id)})]}),(0,l.jsxs)("select",{value:B,onChange:e=>L(e.target.value),disabled:!R||0===Q.length,className:"min-w-0 rounded-lg border border-border bg-background px-3 py-2 font-mono text-xs text-text-main outline-none focus:border-primary disabled:cursor-not-allowed disabled:opacity-60",children:[(0,l.jsx)("option",{value:"",children:R?Q.length>0?"Select model":"No provider models found":"Select account first"}),Q.map(e=>(0,l.jsx)("option",{value:e.value,children:e.label},`${e.value}-${e.label}`))]}),(0,l.jsxs)("button",{type:"button",onClick:()=>{if(!Z)return;let e=function(e,t){let a=N(e),l=function(e={}){let t=j(e.provider),a=N(e);return(0,u.mq)(t)||(0,u.gb)(t)?a||t:t||a}(e),r=w(e),n=j(t||M(e));if(!a||!e?.id||!n)return null;let s=n.includes("/"),o=s?n:`${a}/${n}`,i=s&&n.startsWith(`${a}/`)?n.slice(a.length+1):n,d=r&&r!==l?`${l}/${r}/${i}`:`${l}/${i}`;return{model:o,connectionId:e.id,provider:l||a,accountName:r||void 0,label:d}}(Z,B||X);if(!e)return void alert("Select a provider account and model for this pinned target.");let t=E(e,!0);y(e=>{let a=e.findIndex(e=>e.value===t.value);if(-1!==a){let l=[...e];return l[a]={...l[a],...t,enabled:!0},l}return[...e,t]}),L("")},disabled:!R||!B,className:"inline-flex items-center justify-center gap-1 rounded-lg bg-primary px-3 py-2 text-xs font-semibold text-white disabled:opacity-50",children:[(0,l.jsx)("span",{className:"material-symbols-outlined text-[18px]",children:"add_link"}),"Add Account"]})]})]}),0===f.length?(0,l.jsxs)("div",{className:"rounded-xl border border-dashed border-border p-8 text-center text-text-muted",children:[(0,l.jsx)("span",{className:"material-symbols-outlined",children:"layers"}),(0,l.jsx)("p",{className:"mt-2 text-sm",children:"No models added yet"})]}):(0,l.jsx)(n.Mp,{sensors:z,collisionDetection:n.fp,modifiers:[i.FN,i.gj],onDragEnd:e=>{let{active:t,over:a}=e;if(a&&t.id!==a.id){let e=K.findIndex(e=>e.uid===t.id),l=K.findIndex(e=>e.uid===a.id);-1!==e&&-1!==l&&y(t=>(0,s.be)(t,e,l))}},children:(0,l.jsx)(s.gB,{items:K.map(e=>e.uid),strategy:s._G,children:(0,l.jsx)("div",{className:"space-y-3",children:K.map(({uid:e,row:t},a)=>(0,l.jsx)(D,{id:e,index:a,model:t.label||t.value,subtitle:t.subtitle,enabled:t.enabled,isFirst:0===a,isLast:a===f.length-1,onEdit:e=>{let t=e.trim();if(!t)return;let l=[...f],r=l[a];if(r?.target&&"object"==typeof r.target){let e=$({...r.target,model:t});l[a]=E(e,!1!==r.enabled)}else l[a]=E(t,r?.enabled!==!1);y(l)},onMoveUp:()=>{if(0===a)return;let e=[...f];[e[a-1],e[a]]=[e[a],e[a-1]],y(e)},onMoveDown:()=>{if(a===f.length-1)return;let e=[...f];[e[a],e[a+1]]=[e[a+1],e[a]],y(e)},onToggleEnabled:()=>{let e=[...f];e[a]={...e[a],enabled:!e[a].enabled},y(e)},onRemove:()=>y(f.filter((e,t)=>t!==a))},e))})})})]})]}),(0,l.jsxs)("div",{className:"flex justify-end gap-3 border-t border-border bg-white px-5 py-4 dark:bg-zinc-950",children:[(0,l.jsx)("button",{type:"button",onClick:o,className:"rounded-xl border border-border px-4 py-2 text-sm font-medium text-text-main hover:bg-surface",children:"Cancel"}),(0,l.jsx)("button",{type:"button",onClick:ee,disabled:C,className:"rounded-xl bg-primary px-4 py-2 text-sm font-medium text-white hover:opacity-90 disabled:opacity-50",children:C?"Saving...":et?"Save":"Create"})]})]})}),(0,l.jsx)(d.rq,{isOpen:v,onClose:()=>k(!1),onSelect:e=>{let t=e.value;y(e=>{let a=e.findIndex(e=>e.value===t);if(-1!==a){let t=[...e];return t[a]={...t[a],enabled:!0},t}return[...e,E(t,!0)]})},onDeselect:e=>{y(t=>t.filter(t=>t.value!==e.value))},activeProviders:m,modelAliases:A,title:"Add Model to Combo",kindFilter:b,addedModelValues:J,closeOnSelect:!1})]})}}},e=>{e.O(0,[2574,3862,1051,7055,1321,5497,8441,3794,7358],()=>e(e.s=20061)),_N_E=e.O()}]);