@jheavenknows/bluerouter 1.0.57 → 1.0.59

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 (422) hide show
  1. package/app/.next-cli-build/BUILD_ID +1 -1
  2. package/app/.next-cli-build/app-path-routes-manifest.json +1 -1
  3. package/app/.next-cli-build/build-manifest.json +2 -2
  4. package/app/.next-cli-build/server/app/(dashboard)/dashboard/basic-chat/page_client-reference-manifest.js +1 -1
  5. package/app/.next-cli-build/server/app/(dashboard)/dashboard/cli-tools/[toolId]/page.js.nft.json +1 -1
  6. package/app/.next-cli-build/server/app/(dashboard)/dashboard/cli-tools/[toolId]/page_client-reference-manifest.js +1 -1
  7. package/app/.next-cli-build/server/app/(dashboard)/dashboard/cli-tools/page.js.nft.json +1 -1
  8. package/app/.next-cli-build/server/app/(dashboard)/dashboard/cli-tools/page_client-reference-manifest.js +1 -1
  9. package/app/.next-cli-build/server/app/(dashboard)/dashboard/combos/page_client-reference-manifest.js +1 -1
  10. package/app/.next-cli-build/server/app/(dashboard)/dashboard/console-log/page_client-reference-manifest.js +1 -1
  11. package/app/.next-cli-build/server/app/(dashboard)/dashboard/endpoint/page.js.nft.json +1 -1
  12. package/app/.next-cli-build/server/app/(dashboard)/dashboard/endpoint/page_client-reference-manifest.js +1 -1
  13. package/app/.next-cli-build/server/app/(dashboard)/dashboard/media-providers/[kind]/[id]/page_client-reference-manifest.js +1 -1
  14. package/app/.next-cli-build/server/app/(dashboard)/dashboard/media-providers/[kind]/page_client-reference-manifest.js +1 -1
  15. package/app/.next-cli-build/server/app/(dashboard)/dashboard/media-providers/combo/[id]/page_client-reference-manifest.js +1 -1
  16. package/app/.next-cli-build/server/app/(dashboard)/dashboard/media-providers/web/page_client-reference-manifest.js +1 -1
  17. package/app/.next-cli-build/server/app/(dashboard)/dashboard/mitm/page_client-reference-manifest.js +1 -1
  18. package/app/.next-cli-build/server/app/(dashboard)/dashboard/ollama-nodes/page.js +1 -1
  19. package/app/.next-cli-build/server/app/(dashboard)/dashboard/ollama-nodes/page_client-reference-manifest.js +1 -1
  20. package/app/.next-cli-build/server/app/(dashboard)/dashboard/page.js.nft.json +1 -1
  21. package/app/.next-cli-build/server/app/(dashboard)/dashboard/page_client-reference-manifest.js +1 -1
  22. package/app/.next-cli-build/server/app/(dashboard)/dashboard/profile/page_client-reference-manifest.js +1 -1
  23. package/app/.next-cli-build/server/app/(dashboard)/dashboard/prompt-logs/page_client-reference-manifest.js +1 -1
  24. package/app/.next-cli-build/server/app/(dashboard)/dashboard/provider-health/page_client-reference-manifest.js +1 -1
  25. package/app/.next-cli-build/server/app/(dashboard)/dashboard/providers/[id]/page_client-reference-manifest.js +1 -1
  26. package/app/.next-cli-build/server/app/(dashboard)/dashboard/providers/new/page_client-reference-manifest.js +1 -1
  27. package/app/.next-cli-build/server/app/(dashboard)/dashboard/providers/page_client-reference-manifest.js +1 -1
  28. package/app/.next-cli-build/server/app/(dashboard)/dashboard/proxy-pools/page_client-reference-manifest.js +1 -1
  29. package/app/.next-cli-build/server/app/(dashboard)/dashboard/quota/page_client-reference-manifest.js +1 -1
  30. package/app/.next-cli-build/server/app/(dashboard)/dashboard/skills/page_client-reference-manifest.js +1 -1
  31. package/app/.next-cli-build/server/app/(dashboard)/dashboard/translator/page_client-reference-manifest.js +1 -1
  32. package/app/.next-cli-build/server/app/(dashboard)/dashboard/usage/page_client-reference-manifest.js +1 -1
  33. package/app/.next-cli-build/server/app/(dashboard)/dashboard/virtual-combos/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/route.js.nft.json +1 -1
  72. package/app/.next-cli-build/server/app/api/keys/[id]/route.js.nft.json +1 -1
  73. package/app/.next-cli-build/server/app/api/keys/route.js.nft.json +1 -1
  74. package/app/.next-cli-build/server/app/api/mcp/[plugin]/message/route.js.nft.json +1 -1
  75. package/app/.next-cli-build/server/app/api/mcp/[plugin]/sse/route.js.nft.json +1 -1
  76. package/app/.next-cli-build/server/app/api/media-providers/tts/deepgram/voices/route.js.nft.json +1 -1
  77. package/app/.next-cli-build/server/app/api/media-providers/tts/elevenlabs/voices/route.js.nft.json +1 -1
  78. package/app/.next-cli-build/server/app/api/media-providers/tts/inworld/voices/route.js.nft.json +1 -1
  79. package/app/.next-cli-build/server/app/api/media-providers/tts/minimax/voices/route.js.nft.json +1 -1
  80. package/app/.next-cli-build/server/app/api/models/alias/route.js.nft.json +1 -1
  81. package/app/.next-cli-build/server/app/api/models/availability/route.js.nft.json +1 -1
  82. package/app/.next-cli-build/server/app/api/models/custom/route.js.nft.json +1 -1
  83. package/app/.next-cli-build/server/app/api/models/disabled/route.js.nft.json +1 -1
  84. package/app/.next-cli-build/server/app/api/models/route.js +1 -1
  85. package/app/.next-cli-build/server/app/api/models/route.js.nft.json +1 -1
  86. package/app/.next-cli-build/server/app/api/models/test/route.js +1 -1
  87. package/app/.next-cli-build/server/app/api/models/test/route.js.nft.json +1 -1
  88. package/app/.next-cli-build/server/app/api/oauth/[provider]/[action]/route.js.nft.json +1 -1
  89. package/app/.next-cli-build/server/app/api/oauth/codex/import-token/route.js.nft.json +1 -1
  90. package/app/.next-cli-build/server/app/api/oauth/cursor/auto-import/route.js.nft.json +1 -1
  91. package/app/.next-cli-build/server/app/api/oauth/cursor/import/route.js.nft.json +1 -1
  92. package/app/.next-cli-build/server/app/api/oauth/gitlab/pat/route.js.nft.json +1 -1
  93. package/app/.next-cli-build/server/app/api/oauth/iflow/cookie/route.js.nft.json +1 -1
  94. package/app/.next-cli-build/server/app/api/oauth/kiro/import/route.js.nft.json +1 -1
  95. package/app/.next-cli-build/server/app/api/oauth/kiro/social-exchange/route.js.nft.json +1 -1
  96. package/app/.next-cli-build/server/app/api/ollama-model-memory/route.js +1 -1
  97. package/app/.next-cli-build/server/app/api/ollama-nodes/[id]/install/route.js +573 -134
  98. package/app/.next-cli-build/server/app/api/ollama-nodes/[id]/route.js +1 -1
  99. package/app/.next-cli-build/server/app/api/pricing/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/version/route.js +1 -1
  158. package/app/.next-cli-build/server/app/api/version/shutdown/route.js +1 -1
  159. package/app/.next-cli-build/server/app/api/version/shutdown/route.js.nft.json +1 -1
  160. package/app/.next-cli-build/server/app/api/version/update/route.js +1 -1
  161. package/app/.next-cli-build/server/app/api/version/update/route.js.nft.json +1 -1
  162. package/app/.next-cli-build/server/app/api/virtual-combos/[id]/route.js.nft.json +1 -1
  163. package/app/.next-cli-build/server/app/api/virtual-combos/route.js.nft.json +1 -1
  164. package/app/.next-cli-build/server/app/callback/page_client-reference-manifest.js +1 -1
  165. package/app/.next-cli-build/server/app/callback.html +1 -1
  166. package/app/.next-cli-build/server/app/callback.rsc +3 -3
  167. package/app/.next-cli-build/server/app/callback.segments/_full.segment.rsc +3 -3
  168. package/app/.next-cli-build/server/app/callback.segments/_head.segment.rsc +1 -1
  169. package/app/.next-cli-build/server/app/callback.segments/_index.segment.rsc +3 -3
  170. package/app/.next-cli-build/server/app/callback.segments/_tree.segment.rsc +1 -1
  171. package/app/.next-cli-build/server/app/callback.segments/callback/__PAGE__.segment.rsc +1 -1
  172. package/app/.next-cli-build/server/app/callback.segments/callback.segment.rsc +1 -1
  173. package/app/.next-cli-build/server/app/dashboard/basic-chat.html +1 -1
  174. package/app/.next-cli-build/server/app/dashboard/basic-chat.rsc +5 -5
  175. package/app/.next-cli-build/server/app/dashboard/basic-chat.segments/!KGRhc2hib2FyZCk/dashboard/basic-chat/__PAGE__.segment.rsc +2 -2
  176. package/app/.next-cli-build/server/app/dashboard/basic-chat.segments/!KGRhc2hib2FyZCk/dashboard/basic-chat.segment.rsc +1 -1
  177. package/app/.next-cli-build/server/app/dashboard/basic-chat.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  178. package/app/.next-cli-build/server/app/dashboard/basic-chat.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  179. package/app/.next-cli-build/server/app/dashboard/basic-chat.segments/_full.segment.rsc +5 -5
  180. package/app/.next-cli-build/server/app/dashboard/basic-chat.segments/_head.segment.rsc +1 -1
  181. package/app/.next-cli-build/server/app/dashboard/basic-chat.segments/_index.segment.rsc +3 -3
  182. package/app/.next-cli-build/server/app/dashboard/basic-chat.segments/_tree.segment.rsc +1 -1
  183. package/app/.next-cli-build/server/app/dashboard/cli-tools.html +1 -1
  184. package/app/.next-cli-build/server/app/dashboard/cli-tools.rsc +5 -5
  185. package/app/.next-cli-build/server/app/dashboard/cli-tools.segments/!KGRhc2hib2FyZCk/dashboard/cli-tools/__PAGE__.segment.rsc +2 -2
  186. package/app/.next-cli-build/server/app/dashboard/cli-tools.segments/!KGRhc2hib2FyZCk/dashboard/cli-tools.segment.rsc +1 -1
  187. package/app/.next-cli-build/server/app/dashboard/cli-tools.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  188. package/app/.next-cli-build/server/app/dashboard/cli-tools.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  189. package/app/.next-cli-build/server/app/dashboard/cli-tools.segments/_full.segment.rsc +5 -5
  190. package/app/.next-cli-build/server/app/dashboard/cli-tools.segments/_head.segment.rsc +1 -1
  191. package/app/.next-cli-build/server/app/dashboard/cli-tools.segments/_index.segment.rsc +3 -3
  192. package/app/.next-cli-build/server/app/dashboard/cli-tools.segments/_tree.segment.rsc +1 -1
  193. package/app/.next-cli-build/server/app/dashboard/combos.html +1 -1
  194. package/app/.next-cli-build/server/app/dashboard/combos.rsc +5 -5
  195. package/app/.next-cli-build/server/app/dashboard/combos.segments/!KGRhc2hib2FyZCk/dashboard/combos/__PAGE__.segment.rsc +2 -2
  196. package/app/.next-cli-build/server/app/dashboard/combos.segments/!KGRhc2hib2FyZCk/dashboard/combos.segment.rsc +1 -1
  197. package/app/.next-cli-build/server/app/dashboard/combos.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  198. package/app/.next-cli-build/server/app/dashboard/combos.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  199. package/app/.next-cli-build/server/app/dashboard/combos.segments/_full.segment.rsc +5 -5
  200. package/app/.next-cli-build/server/app/dashboard/combos.segments/_head.segment.rsc +1 -1
  201. package/app/.next-cli-build/server/app/dashboard/combos.segments/_index.segment.rsc +3 -3
  202. package/app/.next-cli-build/server/app/dashboard/combos.segments/_tree.segment.rsc +1 -1
  203. package/app/.next-cli-build/server/app/dashboard/endpoint.html +1 -1
  204. package/app/.next-cli-build/server/app/dashboard/endpoint.rsc +6 -6
  205. package/app/.next-cli-build/server/app/dashboard/endpoint.segments/!KGRhc2hib2FyZCk/dashboard/endpoint/__PAGE__.segment.rsc +3 -3
  206. package/app/.next-cli-build/server/app/dashboard/endpoint.segments/!KGRhc2hib2FyZCk/dashboard/endpoint.segment.rsc +1 -1
  207. package/app/.next-cli-build/server/app/dashboard/endpoint.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  208. package/app/.next-cli-build/server/app/dashboard/endpoint.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  209. package/app/.next-cli-build/server/app/dashboard/endpoint.segments/_full.segment.rsc +6 -6
  210. package/app/.next-cli-build/server/app/dashboard/endpoint.segments/_head.segment.rsc +1 -1
  211. package/app/.next-cli-build/server/app/dashboard/endpoint.segments/_index.segment.rsc +3 -3
  212. package/app/.next-cli-build/server/app/dashboard/endpoint.segments/_tree.segment.rsc +1 -1
  213. package/app/.next-cli-build/server/app/dashboard/media-providers/web.html +1 -1
  214. package/app/.next-cli-build/server/app/dashboard/media-providers/web.rsc +5 -5
  215. package/app/.next-cli-build/server/app/dashboard/media-providers/web.segments/!KGRhc2hib2FyZCk/dashboard/media-providers/web/__PAGE__.segment.rsc +2 -2
  216. package/app/.next-cli-build/server/app/dashboard/media-providers/web.segments/!KGRhc2hib2FyZCk/dashboard/media-providers/web.segment.rsc +1 -1
  217. package/app/.next-cli-build/server/app/dashboard/media-providers/web.segments/!KGRhc2hib2FyZCk/dashboard/media-providers.segment.rsc +1 -1
  218. package/app/.next-cli-build/server/app/dashboard/media-providers/web.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  219. package/app/.next-cli-build/server/app/dashboard/media-providers/web.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  220. package/app/.next-cli-build/server/app/dashboard/media-providers/web.segments/_full.segment.rsc +5 -5
  221. package/app/.next-cli-build/server/app/dashboard/media-providers/web.segments/_head.segment.rsc +1 -1
  222. package/app/.next-cli-build/server/app/dashboard/media-providers/web.segments/_index.segment.rsc +3 -3
  223. package/app/.next-cli-build/server/app/dashboard/media-providers/web.segments/_tree.segment.rsc +1 -1
  224. package/app/.next-cli-build/server/app/dashboard/mitm.html +1 -1
  225. package/app/.next-cli-build/server/app/dashboard/mitm.rsc +5 -5
  226. package/app/.next-cli-build/server/app/dashboard/mitm.segments/!KGRhc2hib2FyZCk/dashboard/mitm/__PAGE__.segment.rsc +2 -2
  227. package/app/.next-cli-build/server/app/dashboard/mitm.segments/!KGRhc2hib2FyZCk/dashboard/mitm.segment.rsc +1 -1
  228. package/app/.next-cli-build/server/app/dashboard/mitm.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  229. package/app/.next-cli-build/server/app/dashboard/mitm.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  230. package/app/.next-cli-build/server/app/dashboard/mitm.segments/_full.segment.rsc +5 -5
  231. package/app/.next-cli-build/server/app/dashboard/mitm.segments/_head.segment.rsc +1 -1
  232. package/app/.next-cli-build/server/app/dashboard/mitm.segments/_index.segment.rsc +3 -3
  233. package/app/.next-cli-build/server/app/dashboard/mitm.segments/_tree.segment.rsc +1 -1
  234. package/app/.next-cli-build/server/app/dashboard/ollama-nodes.html +1 -1
  235. package/app/.next-cli-build/server/app/dashboard/ollama-nodes.rsc +5 -5
  236. package/app/.next-cli-build/server/app/dashboard/ollama-nodes.segments/!KGRhc2hib2FyZCk/dashboard/ollama-nodes/__PAGE__.segment.rsc +2 -2
  237. package/app/.next-cli-build/server/app/dashboard/ollama-nodes.segments/!KGRhc2hib2FyZCk/dashboard/ollama-nodes.segment.rsc +1 -1
  238. package/app/.next-cli-build/server/app/dashboard/ollama-nodes.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  239. package/app/.next-cli-build/server/app/dashboard/ollama-nodes.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  240. package/app/.next-cli-build/server/app/dashboard/ollama-nodes.segments/_full.segment.rsc +5 -5
  241. package/app/.next-cli-build/server/app/dashboard/ollama-nodes.segments/_head.segment.rsc +1 -1
  242. package/app/.next-cli-build/server/app/dashboard/ollama-nodes.segments/_index.segment.rsc +3 -3
  243. package/app/.next-cli-build/server/app/dashboard/ollama-nodes.segments/_tree.segment.rsc +1 -1
  244. package/app/.next-cli-build/server/app/dashboard/profile.html +1 -1
  245. package/app/.next-cli-build/server/app/dashboard/profile.rsc +4 -4
  246. package/app/.next-cli-build/server/app/dashboard/profile.segments/!KGRhc2hib2FyZCk/dashboard/profile/__PAGE__.segment.rsc +1 -1
  247. package/app/.next-cli-build/server/app/dashboard/profile.segments/!KGRhc2hib2FyZCk/dashboard/profile.segment.rsc +1 -1
  248. package/app/.next-cli-build/server/app/dashboard/profile.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  249. package/app/.next-cli-build/server/app/dashboard/profile.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  250. package/app/.next-cli-build/server/app/dashboard/profile.segments/_full.segment.rsc +4 -4
  251. package/app/.next-cli-build/server/app/dashboard/profile.segments/_head.segment.rsc +1 -1
  252. package/app/.next-cli-build/server/app/dashboard/profile.segments/_index.segment.rsc +3 -3
  253. package/app/.next-cli-build/server/app/dashboard/profile.segments/_tree.segment.rsc +1 -1
  254. package/app/.next-cli-build/server/app/dashboard/prompt-logs.html +1 -1
  255. package/app/.next-cli-build/server/app/dashboard/prompt-logs.rsc +4 -4
  256. package/app/.next-cli-build/server/app/dashboard/prompt-logs.segments/!KGRhc2hib2FyZCk/dashboard/prompt-logs/__PAGE__.segment.rsc +1 -1
  257. package/app/.next-cli-build/server/app/dashboard/prompt-logs.segments/!KGRhc2hib2FyZCk/dashboard/prompt-logs.segment.rsc +1 -1
  258. package/app/.next-cli-build/server/app/dashboard/prompt-logs.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  259. package/app/.next-cli-build/server/app/dashboard/prompt-logs.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  260. package/app/.next-cli-build/server/app/dashboard/prompt-logs.segments/_full.segment.rsc +4 -4
  261. package/app/.next-cli-build/server/app/dashboard/prompt-logs.segments/_head.segment.rsc +1 -1
  262. package/app/.next-cli-build/server/app/dashboard/prompt-logs.segments/_index.segment.rsc +3 -3
  263. package/app/.next-cli-build/server/app/dashboard/prompt-logs.segments/_tree.segment.rsc +1 -1
  264. package/app/.next-cli-build/server/app/dashboard/provider-health.html +1 -1
  265. package/app/.next-cli-build/server/app/dashboard/provider-health.rsc +4 -4
  266. package/app/.next-cli-build/server/app/dashboard/provider-health.segments/!KGRhc2hib2FyZCk/dashboard/provider-health/__PAGE__.segment.rsc +1 -1
  267. package/app/.next-cli-build/server/app/dashboard/provider-health.segments/!KGRhc2hib2FyZCk/dashboard/provider-health.segment.rsc +1 -1
  268. package/app/.next-cli-build/server/app/dashboard/provider-health.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  269. package/app/.next-cli-build/server/app/dashboard/provider-health.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  270. package/app/.next-cli-build/server/app/dashboard/provider-health.segments/_full.segment.rsc +4 -4
  271. package/app/.next-cli-build/server/app/dashboard/provider-health.segments/_head.segment.rsc +1 -1
  272. package/app/.next-cli-build/server/app/dashboard/provider-health.segments/_index.segment.rsc +3 -3
  273. package/app/.next-cli-build/server/app/dashboard/provider-health.segments/_tree.segment.rsc +1 -1
  274. package/app/.next-cli-build/server/app/dashboard/providers/new.html +1 -1
  275. package/app/.next-cli-build/server/app/dashboard/providers/new.rsc +5 -5
  276. package/app/.next-cli-build/server/app/dashboard/providers/new.segments/!KGRhc2hib2FyZCk/dashboard/providers/new/__PAGE__.segment.rsc +2 -2
  277. package/app/.next-cli-build/server/app/dashboard/providers/new.segments/!KGRhc2hib2FyZCk/dashboard/providers/new.segment.rsc +1 -1
  278. package/app/.next-cli-build/server/app/dashboard/providers/new.segments/!KGRhc2hib2FyZCk/dashboard/providers.segment.rsc +1 -1
  279. package/app/.next-cli-build/server/app/dashboard/providers/new.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  280. package/app/.next-cli-build/server/app/dashboard/providers/new.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  281. package/app/.next-cli-build/server/app/dashboard/providers/new.segments/_full.segment.rsc +5 -5
  282. package/app/.next-cli-build/server/app/dashboard/providers/new.segments/_head.segment.rsc +1 -1
  283. package/app/.next-cli-build/server/app/dashboard/providers/new.segments/_index.segment.rsc +3 -3
  284. package/app/.next-cli-build/server/app/dashboard/providers/new.segments/_tree.segment.rsc +1 -1
  285. package/app/.next-cli-build/server/app/dashboard/providers.html +1 -1
  286. package/app/.next-cli-build/server/app/dashboard/providers.rsc +5 -5
  287. package/app/.next-cli-build/server/app/dashboard/providers.segments/!KGRhc2hib2FyZCk/dashboard/providers/__PAGE__.segment.rsc +2 -2
  288. package/app/.next-cli-build/server/app/dashboard/providers.segments/!KGRhc2hib2FyZCk/dashboard/providers.segment.rsc +1 -1
  289. package/app/.next-cli-build/server/app/dashboard/providers.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  290. package/app/.next-cli-build/server/app/dashboard/providers.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  291. package/app/.next-cli-build/server/app/dashboard/providers.segments/_full.segment.rsc +5 -5
  292. package/app/.next-cli-build/server/app/dashboard/providers.segments/_head.segment.rsc +1 -1
  293. package/app/.next-cli-build/server/app/dashboard/providers.segments/_index.segment.rsc +3 -3
  294. package/app/.next-cli-build/server/app/dashboard/providers.segments/_tree.segment.rsc +1 -1
  295. package/app/.next-cli-build/server/app/dashboard/proxy-pools.html +1 -1
  296. package/app/.next-cli-build/server/app/dashboard/proxy-pools.rsc +5 -5
  297. package/app/.next-cli-build/server/app/dashboard/proxy-pools.segments/!KGRhc2hib2FyZCk/dashboard/proxy-pools/__PAGE__.segment.rsc +2 -2
  298. package/app/.next-cli-build/server/app/dashboard/proxy-pools.segments/!KGRhc2hib2FyZCk/dashboard/proxy-pools.segment.rsc +1 -1
  299. package/app/.next-cli-build/server/app/dashboard/proxy-pools.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  300. package/app/.next-cli-build/server/app/dashboard/proxy-pools.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  301. package/app/.next-cli-build/server/app/dashboard/proxy-pools.segments/_full.segment.rsc +5 -5
  302. package/app/.next-cli-build/server/app/dashboard/proxy-pools.segments/_head.segment.rsc +1 -1
  303. package/app/.next-cli-build/server/app/dashboard/proxy-pools.segments/_index.segment.rsc +3 -3
  304. package/app/.next-cli-build/server/app/dashboard/proxy-pools.segments/_tree.segment.rsc +1 -1
  305. package/app/.next-cli-build/server/app/dashboard/quota.html +2 -2
  306. package/app/.next-cli-build/server/app/dashboard/quota.rsc +6 -6
  307. package/app/.next-cli-build/server/app/dashboard/quota.segments/!KGRhc2hib2FyZCk/dashboard/quota/__PAGE__.segment.rsc +3 -3
  308. package/app/.next-cli-build/server/app/dashboard/quota.segments/!KGRhc2hib2FyZCk/dashboard/quota.segment.rsc +1 -1
  309. package/app/.next-cli-build/server/app/dashboard/quota.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  310. package/app/.next-cli-build/server/app/dashboard/quota.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  311. package/app/.next-cli-build/server/app/dashboard/quota.segments/_full.segment.rsc +6 -6
  312. package/app/.next-cli-build/server/app/dashboard/quota.segments/_head.segment.rsc +1 -1
  313. package/app/.next-cli-build/server/app/dashboard/quota.segments/_index.segment.rsc +3 -3
  314. package/app/.next-cli-build/server/app/dashboard/quota.segments/_tree.segment.rsc +1 -1
  315. package/app/.next-cli-build/server/app/dashboard/settings/pricing/page_client-reference-manifest.js +1 -1
  316. package/app/.next-cli-build/server/app/dashboard/settings/pricing.html +1 -1
  317. package/app/.next-cli-build/server/app/dashboard/settings/pricing.rsc +3 -3
  318. package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/_full.segment.rsc +3 -3
  319. package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/_head.segment.rsc +1 -1
  320. package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/_index.segment.rsc +3 -3
  321. package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/_tree.segment.rsc +1 -1
  322. package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/dashboard/settings/pricing/__PAGE__.segment.rsc +1 -1
  323. package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/dashboard/settings/pricing.segment.rsc +1 -1
  324. package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/dashboard/settings.segment.rsc +1 -1
  325. package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/dashboard.segment.rsc +1 -1
  326. package/app/.next-cli-build/server/app/dashboard/skills.html +1 -1
  327. package/app/.next-cli-build/server/app/dashboard/skills.rsc +5 -5
  328. package/app/.next-cli-build/server/app/dashboard/skills.segments/!KGRhc2hib2FyZCk/dashboard/skills/__PAGE__.segment.rsc +2 -2
  329. package/app/.next-cli-build/server/app/dashboard/skills.segments/!KGRhc2hib2FyZCk/dashboard/skills.segment.rsc +1 -1
  330. package/app/.next-cli-build/server/app/dashboard/skills.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  331. package/app/.next-cli-build/server/app/dashboard/skills.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  332. package/app/.next-cli-build/server/app/dashboard/skills.segments/_full.segment.rsc +5 -5
  333. package/app/.next-cli-build/server/app/dashboard/skills.segments/_head.segment.rsc +1 -1
  334. package/app/.next-cli-build/server/app/dashboard/skills.segments/_index.segment.rsc +3 -3
  335. package/app/.next-cli-build/server/app/dashboard/skills.segments/_tree.segment.rsc +1 -1
  336. package/app/.next-cli-build/server/app/dashboard/translator.html +1 -1
  337. package/app/.next-cli-build/server/app/dashboard/translator.rsc +5 -5
  338. package/app/.next-cli-build/server/app/dashboard/translator.segments/!KGRhc2hib2FyZCk/dashboard/translator/__PAGE__.segment.rsc +2 -2
  339. package/app/.next-cli-build/server/app/dashboard/translator.segments/!KGRhc2hib2FyZCk/dashboard/translator.segment.rsc +1 -1
  340. package/app/.next-cli-build/server/app/dashboard/translator.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  341. package/app/.next-cli-build/server/app/dashboard/translator.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  342. package/app/.next-cli-build/server/app/dashboard/translator.segments/_full.segment.rsc +5 -5
  343. package/app/.next-cli-build/server/app/dashboard/translator.segments/_head.segment.rsc +1 -1
  344. package/app/.next-cli-build/server/app/dashboard/translator.segments/_index.segment.rsc +3 -3
  345. package/app/.next-cli-build/server/app/dashboard/translator.segments/_tree.segment.rsc +1 -1
  346. package/app/.next-cli-build/server/app/dashboard/usage.html +1 -1
  347. package/app/.next-cli-build/server/app/dashboard/usage.rsc +5 -5
  348. package/app/.next-cli-build/server/app/dashboard/usage.segments/!KGRhc2hib2FyZCk/dashboard/usage/__PAGE__.segment.rsc +2 -2
  349. package/app/.next-cli-build/server/app/dashboard/usage.segments/!KGRhc2hib2FyZCk/dashboard/usage.segment.rsc +1 -1
  350. package/app/.next-cli-build/server/app/dashboard/usage.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  351. package/app/.next-cli-build/server/app/dashboard/usage.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  352. package/app/.next-cli-build/server/app/dashboard/usage.segments/_full.segment.rsc +5 -5
  353. package/app/.next-cli-build/server/app/dashboard/usage.segments/_head.segment.rsc +1 -1
  354. package/app/.next-cli-build/server/app/dashboard/usage.segments/_index.segment.rsc +3 -3
  355. package/app/.next-cli-build/server/app/dashboard/usage.segments/_tree.segment.rsc +1 -1
  356. package/app/.next-cli-build/server/app/dashboard/virtual-combos.html +2 -2
  357. package/app/.next-cli-build/server/app/dashboard/virtual-combos.rsc +4 -4
  358. package/app/.next-cli-build/server/app/dashboard/virtual-combos.segments/!KGRhc2hib2FyZCk/dashboard/virtual-combos/__PAGE__.segment.rsc +1 -1
  359. package/app/.next-cli-build/server/app/dashboard/virtual-combos.segments/!KGRhc2hib2FyZCk/dashboard/virtual-combos.segment.rsc +1 -1
  360. package/app/.next-cli-build/server/app/dashboard/virtual-combos.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  361. package/app/.next-cli-build/server/app/dashboard/virtual-combos.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  362. package/app/.next-cli-build/server/app/dashboard/virtual-combos.segments/_full.segment.rsc +4 -4
  363. package/app/.next-cli-build/server/app/dashboard/virtual-combos.segments/_head.segment.rsc +1 -1
  364. package/app/.next-cli-build/server/app/dashboard/virtual-combos.segments/_index.segment.rsc +3 -3
  365. package/app/.next-cli-build/server/app/dashboard/virtual-combos.segments/_tree.segment.rsc +1 -1
  366. package/app/.next-cli-build/server/app/dashboard.html +1 -1
  367. package/app/.next-cli-build/server/app/dashboard.rsc +5 -5
  368. package/app/.next-cli-build/server/app/dashboard.segments/!KGRhc2hib2FyZCk/dashboard/__PAGE__.segment.rsc +2 -2
  369. package/app/.next-cli-build/server/app/dashboard.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  370. package/app/.next-cli-build/server/app/dashboard.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  371. package/app/.next-cli-build/server/app/dashboard.segments/_full.segment.rsc +5 -5
  372. package/app/.next-cli-build/server/app/dashboard.segments/_head.segment.rsc +1 -1
  373. package/app/.next-cli-build/server/app/dashboard.segments/_index.segment.rsc +3 -3
  374. package/app/.next-cli-build/server/app/dashboard.segments/_tree.segment.rsc +1 -1
  375. package/app/.next-cli-build/server/app/index.html +1 -1
  376. package/app/.next-cli-build/server/app/index.rsc +3 -3
  377. package/app/.next-cli-build/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  378. package/app/.next-cli-build/server/app/index.segments/_full.segment.rsc +3 -3
  379. package/app/.next-cli-build/server/app/index.segments/_head.segment.rsc +1 -1
  380. package/app/.next-cli-build/server/app/index.segments/_index.segment.rsc +3 -3
  381. package/app/.next-cli-build/server/app/index.segments/_tree.segment.rsc +1 -1
  382. package/app/.next-cli-build/server/app/landing/page_client-reference-manifest.js +1 -1
  383. package/app/.next-cli-build/server/app/landing.html +1 -1
  384. package/app/.next-cli-build/server/app/landing.rsc +3 -3
  385. package/app/.next-cli-build/server/app/landing.segments/_full.segment.rsc +3 -3
  386. package/app/.next-cli-build/server/app/landing.segments/_head.segment.rsc +1 -1
  387. package/app/.next-cli-build/server/app/landing.segments/_index.segment.rsc +3 -3
  388. package/app/.next-cli-build/server/app/landing.segments/_tree.segment.rsc +1 -1
  389. package/app/.next-cli-build/server/app/landing.segments/landing/__PAGE__.segment.rsc +1 -1
  390. package/app/.next-cli-build/server/app/landing.segments/landing.segment.rsc +1 -1
  391. package/app/.next-cli-build/server/app/login/page_client-reference-manifest.js +1 -1
  392. package/app/.next-cli-build/server/app/login.html +1 -1
  393. package/app/.next-cli-build/server/app/login.rsc +3 -3
  394. package/app/.next-cli-build/server/app/login.segments/_full.segment.rsc +3 -3
  395. package/app/.next-cli-build/server/app/login.segments/_head.segment.rsc +1 -1
  396. package/app/.next-cli-build/server/app/login.segments/_index.segment.rsc +3 -3
  397. package/app/.next-cli-build/server/app/login.segments/_tree.segment.rsc +1 -1
  398. package/app/.next-cli-build/server/app/login.segments/login/__PAGE__.segment.rsc +1 -1
  399. package/app/.next-cli-build/server/app/login.segments/login.segment.rsc +1 -1
  400. package/app/.next-cli-build/server/app/page_client-reference-manifest.js +1 -1
  401. package/app/.next-cli-build/server/app-paths-manifest.json +1 -1
  402. package/app/.next-cli-build/server/chunks/2769.js +1 -1
  403. package/app/.next-cli-build/server/chunks/3110.js +1 -1
  404. package/app/.next-cli-build/server/chunks/4746.js +1 -1
  405. package/app/.next-cli-build/server/chunks/7130.js +1 -1
  406. package/app/.next-cli-build/server/chunks/7153.js +1 -1
  407. package/app/.next-cli-build/server/chunks/9253.js +1 -1
  408. package/app/.next-cli-build/server/middleware-build-manifest.js +1 -1
  409. package/app/.next-cli-build/server/pages/404.html +1 -1
  410. package/app/.next-cli-build/server/pages/500.html +1 -1
  411. package/app/.next-cli-build/static/chunks/{1321-e0204c13d8cdc0ff.js → 1321-41e9eaeb3270b514.js} +1 -1
  412. package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/ollama-nodes/page-c771cf90148ecc62.js +1 -0
  413. package/app/cli/.build-home/AppData/Roaming/bluerouter/db/backups/upgrade-1.0.57-to-1.0.58-1.0.58-20260527-230328/data.sqlite +0 -0
  414. package/app/cli/.build-home/AppData/Roaming/bluerouter/db/backups/upgrade-1.0.58-to-1.0.59-1.0.59-20260527-233043/data.sqlite +0 -0
  415. package/app/cli/.build-home/AppData/Roaming/bluerouter/db/data.sqlite-shm +0 -0
  416. package/app/cli/.build-home/AppData/Roaming/bluerouter/db/data.sqlite-wal +0 -0
  417. package/app/package.json +1 -1
  418. package/package.json +1 -1
  419. package/app/.next-cli-build/static/chunks/app/(dashboard)/dashboard/ollama-nodes/page-3e97343c80613dec.js +0 -1
  420. /package/app/.next-cli-build/static/{ungjWeMSqfJJ12Xvvm9jM → Q9DHPrSO_c4RIRpf5wwP3}/_buildManifest.js +0 -0
  421. /package/app/.next-cli-build/static/{ungjWeMSqfJJ12Xvvm9jM → Q9DHPrSO_c4RIRpf5wwP3}/_ssgManifest.js +0 -0
  422. /package/app/cli/.build-home/AppData/Roaming/bluerouter/db/backups/{upgrade-1.0.53-to-1.0.54-1.0.54-20260527-190346 → upgrade-1.0.56-to-1.0.57-1.0.57-20260527-212922}/data.sqlite +0 -0
@@ -1,11 +1,12 @@
1
- (()=>{var a={};a.id=9902,a.ids=[9902],a.modules={261:a=>{"use strict";a.exports=require("next/dist/shared/lib/router/utils/app-paths")},3295:a=>{"use strict";a.exports=require("next/dist/server/app-render/after-task-async-storage.external.js")},6774:(a,b,c)=>{"use strict";c.d(b,{U1:()=>v,Vs:()=>w,YT:()=>z,iJ:()=>y,op:()=>A,ri:()=>x});var d=c(79748),e=c.n(d),f=c(33873),g=c.n(f),h=c(55511),i=c.n(h),j=c(21820),k=c.n(j),l=c(55013);let m=process.env.DATA_DIR||process.env.BLUEROUTER_DATA_DIR||g().join(k().homedir(),".bluerouter"),n=g().join(m,"ollama-nodes.json");async function o(){await e().mkdir(m,{recursive:!0});try{await e().access(n)}catch{await e().writeFile(n,JSON.stringify({nodes:[]},null,2))}}async function p(){await o();try{let a=await e().readFile(n,"utf8"),b=JSON.parse(a||"{}");return{nodes:Array.isArray(b.nodes)?b.nodes:[]}}catch{return{nodes:[]}}}async function q(a){await e().mkdir(m,{recursive:!0}),await e().writeFile(n,JSON.stringify({nodes:a.nodes||[]},null,2))}function r(){return new Date().toISOString()}function s(a,b={}){if(!a)return b;if("object"==typeof a&&!Array.isArray(a))return a;try{let c=JSON.parse(String(a));return c&&"object"==typeof c&&!Array.isArray(c)?c:b}catch{return b}}function t(a){return a?{...a,sshPassword:void 0,sudoPassword:void 0}:null}function u(a={},b={}){var c;let d=String(a.host??b.host??"").trim(),e=Number(a.agentPort??b.agentPort??19999)||19999,f=Array.isArray(c=a.ollamaPorts??b.ollamaPorts??[11434])?[...new Set(c.map(a=>Number(a)).filter(a=>Number.isFinite(a)&&a>0))]:[...new Set(String(c||"11434").split(/[\s,;]+/).map(a=>Number(a.trim())).filter(a=>Number.isFinite(a)&&a>0))],g=s(a.modelMemoryRequirements??b.modelMemoryRequirements,b.modelMemoryRequirements||{}),h=(0,l.nw)(g,{partial:!0,preserveEnabled:!0}),j=b.id||a.id||i().randomUUID(),k=String(a.agentToken??b.agentToken??i().randomBytes(24).toString("hex")).trim(),m=function(a,b,c={}){let d=new Map((Array.isArray(c.instances)?c.instances:[]).map(a=>[Number(a.port),a])),e=new Map((Array.isArray(a)?a:[]).map(a=>[Number(a.port),a]));return b.map(a=>(function(a={},b={}){var c;let d=Number(a.port??b.port??11434)||11434;return{id:String(a.id??b.id??`ollama-${d}`).trim()||`ollama-${d}`,name:String(a.name??b.name??`Ollama :${d}`).trim()||`Ollama :${d}`,port:d,enabled:a.enabled??b.enabled??!0,gpuMode:["auto","cpu","specific","all"].includes(a.gpuMode??b.gpuMode)?a.gpuMode??b.gpuMode:"auto",gpuIndices:Array.isArray(c=a.gpuIndices??b.gpuIndices??[])?[...new Set(c.map(a=>Number(a)).filter(a=>Number.isInteger(a)&&a>=0))]:[...new Set(String(c||"").split(/[\s,;]+/).map(a=>Number(a)).filter(a=>Number.isInteger(a)&&a>=0))],allowMultiGpuSplit:a.allowMultiGpuSplit??b.allowMultiGpuSplit??!1,tags:Array.isArray(a.tags??b.tags)?(a.tags??b.tags).map(a=>String(a).trim()).filter(Boolean):[],modelMemoryRequirements:(0,l.nw)(s(a.modelMemoryRequirements??b.modelMemoryRequirements,b.modelMemoryRequirements||{}),{partial:!0,preserveEnabled:!0})}})(e.get(Number(a))||{},d.get(Number(a))||{port:a}))}(a.instances??b.instances,f.length?f:[11434],b);return{id:j,name:String(a.name??b.name??d??"Ollama Node").trim()||d||"Ollama Node",host:d,sshPort:Number(a.sshPort??b.sshPort??22)||22,sshUser:String(a.sshUser??b.sshUser??"root").trim()||"root",useSudo:a.useSudo??b.useSudo??!1,agentPort:e,agentToken:k,ollamaPorts:f.length?f:[11434],instances:m,refreshIntervalMs:Math.max(5e3,Number(a.refreshIntervalMs??b.refreshIntervalMs??15e3)||15e3),enabled:a.enabled??b.enabled??!0,ufwAllowFrom:String(a.ufwAllowFrom??b.ufwAllowFrom??"").trim(),useGlobalModelMemory:a.useGlobalModelMemory??b.useGlobalModelMemory??!0,modelMemoryRequirements:h,notes:String(a.notes??b.notes??"").trim(),lastStatus:b.lastStatus||null,lastEvaluation:b.lastEvaluation||null,lastStatusAt:b.lastStatusAt||null,lastError:b.lastError||null,lastInstallAt:b.lastInstallAt||null,lastInstallOk:b.lastInstallOk||!1,lastInstallLog:b.lastInstallLog||"",createdAt:b.createdAt||r(),updatedAt:r()}}async function v(){return(await p()).nodes.map(a=>t(u(a,a)))}async function w(a){let b=(await p()).nodes.find(b=>b.id===a)||null;return b?u(b,b):null}async function x(a){let b=await p(),c=u(a);return b.nodes.push(c),await q(b),t(c)}async function y(a,b){let c=await p(),d=c.nodes.findIndex(b=>b.id===a);if(d<0)return null;let e=u(b,c.nodes[d]);return c.nodes[d]=e,await q(c),t(e)}async function z(a,b){let c=await p(),d=c.nodes.findIndex(b=>b.id===a);return d<0?null:(c.nodes[d]={...c.nodes[d],...b,updatedAt:r()},await q(c),t(u(c.nodes[d],c.nodes[d])))}async function A(a){let b=await p(),c=b.nodes.length;return b.nodes=b.nodes.filter(b=>b.id!==a),await q(b),b.nodes.length!==c}},10846:a=>{"use strict";a.exports=require("next/dist/compiled/next-server/app-page.runtime.prod.js")},21820:a=>{"use strict";a.exports=require("os")},29294:a=>{"use strict";a.exports=require("next/dist/server/app-render/work-async-storage.external.js")},33873:a=>{"use strict";a.exports=require("path")},44870:a=>{"use strict";a.exports=require("next/dist/compiled/next-server/app-route.runtime.prod.js")},55013:(a,b,c)=>{"use strict";c.d(b,{LM:()=>v,LQ:()=>u,MW:()=>s,eY:()=>t,nw:()=>o});var d=c(79748),e=c.n(d),f=c(33873),g=c.n(f),h=c(21820),i=c.n(h);let j=process.env.DATA_DIR||process.env.BLUEROUTER_DATA_DIR||g().join(i().homedir(),".bluerouter"),k=g().join(j,"ollama-model-memory.json"),l={"qwen3:1.7b":{minRamFreeMb:2048,minVramFreeMb:0,loadedRamFreeMb:512,loadedVramFreeMb:0,safetyMarginMb:512,notes:"small text model"},"qwen3:4b":{minRamFreeMb:4096,minVramFreeMb:0,loadedRamFreeMb:1024,loadedVramFreeMb:0,safetyMarginMb:512,notes:"small/medium text model"},"qwen3:8b":{minRamFreeMb:6144,minVramFreeMb:0,loadedRamFreeMb:1024,loadedVramFreeMb:512,safetyMarginMb:1024,notes:"8B text model"},"qwen3:14b":{minRamFreeMb:8192,minVramFreeMb:1e4,loadedRamFreeMb:2048,loadedVramFreeMb:1024,safetyMarginMb:1024,notes:"14B class model"},"qwen3:32b":{minRamFreeMb:4096,minVramFreeMb:16e3,loadedRamFreeMb:2048,loadedVramFreeMb:2048,safetyMarginMb:2048,notes:"32B class model, GPU preferred"},"qwen3-coder:30b":{minRamFreeMb:8192,minVramFreeMb:24e3,loadedRamFreeMb:2048,loadedVramFreeMb:3072,safetyMarginMb:2048,notes:"30B coder"},"qwen3-coder:latest":{minRamFreeMb:8192,minVramFreeMb:24e3,loadedRamFreeMb:2048,loadedVramFreeMb:3072,safetyMarginMb:2048,notes:"qwen3 coder latest, treat as 30B unless overridden"},"qwen2.5-coder:14b":{minRamFreeMb:8192,minVramFreeMb:1e4,loadedRamFreeMb:2048,loadedVramFreeMb:1024,safetyMarginMb:1024,notes:"14B coder"},"qwen2.5vl:7b":{minRamFreeMb:8192,minVramFreeMb:8e3,loadedRamFreeMb:2048,loadedVramFreeMb:1024,safetyMarginMb:1024,notes:"7B vision-language"},"qwen2.5vl:latest":{minRamFreeMb:8192,minVramFreeMb:1e4,loadedRamFreeMb:2048,loadedVramFreeMb:1024,safetyMarginMb:1024,notes:"vision-language latest"},"bge-m3:latest":{minRamFreeMb:4096,minVramFreeMb:0,loadedRamFreeMb:512,loadedVramFreeMb:0,safetyMarginMb:512,notes:"embedding model"},"deepseek-coder:6.7b":{minRamFreeMb:4096,minVramFreeMb:0,loadedRamFreeMb:1024,loadedVramFreeMb:512,safetyMarginMb:1024,notes:"6.7B coder"},"gemma4:e4b":{minRamFreeMb:4096,minVramFreeMb:0,loadedRamFreeMb:1024,loadedVramFreeMb:0,safetyMarginMb:1024,notes:"small Gemma variant"},"gemma:7b":{minRamFreeMb:4096,minVramFreeMb:0,loadedRamFreeMb:1024,loadedVramFreeMb:0,safetyMarginMb:1024,notes:"7B text model"},"glm-4.7-flash:latest":{minRamFreeMb:4096,minVramFreeMb:0,loadedRamFreeMb:1024,loadedVramFreeMb:512,safetyMarginMb:1024,notes:"flash/local text model"},"gpt-oss:20b":{minRamFreeMb:8192,minVramFreeMb:12e3,loadedRamFreeMb:2048,loadedVramFreeMb:2048,safetyMarginMb:2048,notes:"20B class model"},"llama3.1:8b":{minRamFreeMb:6144,minVramFreeMb:0,loadedRamFreeMb:1024,loadedVramFreeMb:512,safetyMarginMb:1024,notes:"8B text model"},"llama3.2-vision:latest":{minRamFreeMb:8192,minVramFreeMb:1e4,loadedRamFreeMb:2048,loadedVramFreeMb:1024,safetyMarginMb:1024,notes:"vision model"},"qwen3-*-translate-f16:*":{minRamFreeMb:8192,minVramFreeMb:16e3,loadedRamFreeMb:2048,loadedVramFreeMb:2048,safetyMarginMb:2048,notes:"custom Qwen3 f16 translate wildcard"},"qwen3-*-translate*:latest":{minRamFreeMb:6144,minVramFreeMb:8e3,loadedRamFreeMb:1024,loadedVramFreeMb:1024,safetyMarginMb:1024,notes:"custom Qwen3 translate wildcard"},"bge-m3:*":{minRamFreeMb:4096,minVramFreeMb:0,loadedRamFreeMb:512,loadedVramFreeMb:0,safetyMarginMb:512,notes:"embedding model wildcard"},"gemma*:e4b":{minRamFreeMb:4096,minVramFreeMb:0,loadedRamFreeMb:1024,loadedVramFreeMb:0,safetyMarginMb:1024,notes:"Gemma E4B wildcard"},"gemma*:7b":{minRamFreeMb:4096,minVramFreeMb:0,loadedRamFreeMb:1024,loadedVramFreeMb:0,safetyMarginMb:1024,notes:"Gemma 7B wildcard"},"*:1.7b":{minRamFreeMb:2048,minVramFreeMb:0,loadedRamFreeMb:512,loadedVramFreeMb:0,safetyMarginMb:512,notes:"generic 1.7B fallback"},"*:4b":{minRamFreeMb:4096,minVramFreeMb:0,loadedRamFreeMb:1024,loadedVramFreeMb:0,safetyMarginMb:512,notes:"generic 4B fallback"},"*:6.7b":{minRamFreeMb:4096,minVramFreeMb:0,loadedRamFreeMb:1024,loadedVramFreeMb:512,safetyMarginMb:1024,notes:"generic 6-7B fallback"},"*:7b":{minRamFreeMb:4096,minVramFreeMb:0,loadedRamFreeMb:1024,loadedVramFreeMb:512,safetyMarginMb:1024,notes:"generic 7B fallback"},"*:8b":{minRamFreeMb:6144,minVramFreeMb:0,loadedRamFreeMb:1024,loadedVramFreeMb:512,safetyMarginMb:1024,notes:"generic 8B fallback"},"*:14b":{minRamFreeMb:8192,minVramFreeMb:1e4,loadedRamFreeMb:2048,loadedVramFreeMb:1024,safetyMarginMb:1024,notes:"generic 14B fallback"},"*:20b":{minRamFreeMb:8192,minVramFreeMb:12e3,loadedRamFreeMb:2048,loadedVramFreeMb:2048,safetyMarginMb:2048,notes:"generic 20B fallback"},"*:30b":{minRamFreeMb:8192,minVramFreeMb:24e3,loadedRamFreeMb:2048,loadedVramFreeMb:3072,safetyMarginMb:2048,notes:"generic 30B fallback"},"*:32b":{minRamFreeMb:4096,minVramFreeMb:16e3,loadedRamFreeMb:2048,loadedVramFreeMb:2048,safetyMarginMb:2048,notes:"generic 32B fallback"}},m=["minRamFreeMb","minVramFreeMb","loadedRamFreeMb","loadedVramFreeMb","safetyMarginMb"];function n(a,b=0){let c=Number(a);return Number.isFinite(c)&&c>=0?c:b}function o(a={},b={}){let c={};if(!a||"object"!=typeof a||Array.isArray(a))return c;for(let[d,e]of Object.entries(a)){let a=String(d||"").trim();if(!a||!e||"object"!=typeof e||Array.isArray(e))continue;let f=function(a,{partial:b=!1,preserveEnabled:c=!1}={}){let d={};for(let c of m)if(b){var e;null!=(e=a?.[c])&&""!==String(e).trim()&&(d[c]=n(a[c],0))}else d[c]=n(a?.[c],0);let f=String(a?.notes||"").trim();return f&&(d.notes=f),c&&a&&Object.prototype.hasOwnProperty.call(a,"enabled")&&(d.enabled=!1!==a.enabled),d}(e,b),g=m.some(a=>Object.prototype.hasOwnProperty.call(f,a)),h=b.preserveEnabled&&Object.prototype.hasOwnProperty.call(f,"enabled");(g||f.notes||h)&&(c[a]=f)}return c}async function p(){await e().mkdir(j,{recursive:!0});try{await e().access(k)}catch{await e().writeFile(k,JSON.stringify({requirements:l,updatedAt:new Date().toISOString()},null,2))}}async function q(){await p();try{let a=await e().readFile(k,"utf8"),b=JSON.parse(a||"{}");return{requirements:function(a={}){return o({...l,...a})}(b.requirements||{}),updatedAt:b.updatedAt||null}}catch{return{requirements:o(l),updatedAt:null}}}async function r(a){await e().mkdir(j,{recursive:!0});let b={requirements:o(a),updatedAt:new Date().toISOString()};return await e().writeFile(k,JSON.stringify(b,null,2)),b}async function s(){return(await q()).requirements}async function t(){return q()}async function u(a){return r(a)}async function v(){return r(l)}},55511:a=>{"use strict";a.exports=require("crypto")},63033:a=>{"use strict";a.exports=require("next/dist/server/app-render/work-unit-async-storage.external.js")},68757:(a,b,c)=>{"use strict";c.r(b),c.d(b,{handler:()=>J,patchFetch:()=>I,routeModule:()=>E,serverHooks:()=>H,workAsyncStorage:()=>F,workUnitAsyncStorage:()=>G});var d={};c.r(d),c.d(d,{POST:()=>D,dynamic:()=>C,runtime:()=>B});var e=c(19225),f=c(84006),g=c(8317),h=c(99373),i=c(34775),j=c(24235),k=c(261),l=c(54365),m=c(90771),n=c(73461),o=c(67798),p=c(92280),q=c(62018),r=c(45696),s=c(47929),t=c(86439),u=c(37527),v=c(23211),w=c(6774),x=c(79646);function y(a){return`'${String(a??"").replace(/'/g,"'\"'\"'")}'`}function z(a,b,{input:c="",timeoutMs:d=12e4}={}){return new Promise(e=>{let f=(0,x.spawn)(a,b,{stdio:["pipe","pipe","pipe"]}),g="",h="",i=!1,j=setTimeout(()=>{i||f.kill("SIGKILL")},d);f.stdout.on("data",a=>{g+=a.toString()}),f.stderr.on("data",a=>{h+=a.toString()}),f.on("close",a=>{i=!0,clearTimeout(j),e({ok:0===a,code:a,stdout:g,stderr:h})}),c&&f.stdin.write(c),f.stdin.end()})}async function A(a,b={}){let c=a.sshUser||"root",d=Number(a.sshPort||22),e=`${c}@${a.host}`,f=function(a,b={}){let c="/opt/bluerouter-ollama-agent",d=(a.ollamaPorts||[11434]).join(","),e=String.raw`#!/usr/bin/env python3
1
+ (()=>{var a={};a.id=9902,a.ids=[9902],a.modules={261:a=>{"use strict";a.exports=require("next/dist/shared/lib/router/utils/app-paths")},3295:a=>{"use strict";a.exports=require("next/dist/server/app-render/after-task-async-storage.external.js")},6774:(a,b,c)=>{"use strict";c.d(b,{U1:()=>v,Vs:()=>w,YT:()=>z,iJ:()=>y,op:()=>A,ri:()=>x});var d=c(79748),e=c.n(d),f=c(33873),g=c.n(f),h=c(55511),i=c.n(h),j=c(21820),k=c.n(j),l=c(55013);let m=process.env.DATA_DIR||process.env.BLUEROUTER_DATA_DIR||g().join(k().homedir(),".bluerouter"),n=g().join(m,"ollama-nodes.json");async function o(){await e().mkdir(m,{recursive:!0});try{await e().access(n)}catch{await e().writeFile(n,JSON.stringify({nodes:[]},null,2))}}async function p(){await o();try{let a=await e().readFile(n,"utf8"),b=JSON.parse(a||"{}");return{nodes:Array.isArray(b.nodes)?b.nodes:[]}}catch{return{nodes:[]}}}async function q(a){await e().mkdir(m,{recursive:!0}),await e().writeFile(n,JSON.stringify({nodes:a.nodes||[]},null,2))}function r(){return new Date().toISOString()}function s(a,b={}){if(!a)return b;if("object"==typeof a&&!Array.isArray(a))return a;try{let c=JSON.parse(String(a));return c&&"object"==typeof c&&!Array.isArray(c)?c:b}catch{return b}}function t(a){return a?{...a,sshPassword:void 0,sudoPassword:void 0}:null}function u(a={},b={}){var c;let d=String(a.host??b.host??"").trim(),e=Number(a.agentPort??b.agentPort??19999)||19999,f=Array.isArray(c=a.ollamaPorts??b.ollamaPorts??[11434])?[...new Set(c.map(a=>Number(a)).filter(a=>Number.isFinite(a)&&a>0))]:[...new Set(String(c||"11434").split(/[\s,;]+/).map(a=>Number(a.trim())).filter(a=>Number.isFinite(a)&&a>0))],g=s(a.modelMemoryRequirements??b.modelMemoryRequirements,b.modelMemoryRequirements||{}),h=(0,l.nw)(g,{partial:!0,preserveEnabled:!0}),j=b.id||a.id||i().randomUUID(),k=String(a.agentToken??b.agentToken??i().randomBytes(24).toString("hex")).trim(),m=function(a,b,c={}){let d=new Map((Array.isArray(c.instances)?c.instances:[]).map(a=>[Number(a.port),a])),e=new Map((Array.isArray(a)?a:[]).map(a=>[Number(a.port),a]));return b.map(a=>(function(a={},b={}){var c;let d=Number(a.port??b.port??11434)||11434;return{id:String(a.id??b.id??`ollama-${d}`).trim()||`ollama-${d}`,name:String(a.name??b.name??`Ollama :${d}`).trim()||`Ollama :${d}`,port:d,enabled:a.enabled??b.enabled??!0,gpuMode:["auto","cpu","specific","all"].includes(a.gpuMode??b.gpuMode)?a.gpuMode??b.gpuMode:"auto",gpuIndices:Array.isArray(c=a.gpuIndices??b.gpuIndices??[])?[...new Set(c.map(a=>Number(a)).filter(a=>Number.isInteger(a)&&a>=0))]:[...new Set(String(c||"").split(/[\s,;]+/).map(a=>Number(a)).filter(a=>Number.isInteger(a)&&a>=0))],allowMultiGpuSplit:a.allowMultiGpuSplit??b.allowMultiGpuSplit??!1,tags:Array.isArray(a.tags??b.tags)?(a.tags??b.tags).map(a=>String(a).trim()).filter(Boolean):[],modelMemoryRequirements:(0,l.nw)(s(a.modelMemoryRequirements??b.modelMemoryRequirements,b.modelMemoryRequirements||{}),{partial:!0,preserveEnabled:!0})}})(e.get(Number(a))||{},d.get(Number(a))||{port:a}))}(a.instances??b.instances,f.length?f:[11434],b);return{id:j,name:String(a.name??b.name??d??"Ollama Node").trim()||d||"Ollama Node",host:d,sshPort:Number(a.sshPort??b.sshPort??22)||22,sshUser:String(a.sshUser??b.sshUser??"root").trim()||"root",useSudo:a.useSudo??b.useSudo??!1,agentPort:e,agentToken:k,ollamaPorts:f.length?f:[11434],instances:m,refreshIntervalMs:Math.max(5e3,Number(a.refreshIntervalMs??b.refreshIntervalMs??15e3)||15e3),enabled:a.enabled??b.enabled??!0,ufwAllowFrom:String(a.ufwAllowFrom??b.ufwAllowFrom??"").trim(),useGlobalModelMemory:a.useGlobalModelMemory??b.useGlobalModelMemory??!0,modelMemoryRequirements:h,notes:String(a.notes??b.notes??"").trim(),lastStatus:b.lastStatus||null,lastEvaluation:b.lastEvaluation||null,lastStatusAt:b.lastStatusAt||null,lastError:b.lastError||null,lastInstallAt:b.lastInstallAt||null,lastInstallOk:b.lastInstallOk||!1,lastInstallLog:b.lastInstallLog||"",createdAt:b.createdAt||r(),updatedAt:r()}}async function v(){return(await p()).nodes.map(a=>t(u(a,a)))}async function w(a){let b=(await p()).nodes.find(b=>b.id===a)||null;return b?u(b,b):null}async function x(a){let b=await p(),c=u(a);return b.nodes.push(c),await q(b),t(c)}async function y(a,b){let c=await p(),d=c.nodes.findIndex(b=>b.id===a);if(d<0)return null;let e=u(b,c.nodes[d]);return c.nodes[d]=e,await q(c),t(e)}async function z(a,b){let c=await p(),d=c.nodes.findIndex(b=>b.id===a);return d<0?null:(c.nodes[d]={...c.nodes[d],...b,updatedAt:r()},await q(c),t(u(c.nodes[d],c.nodes[d])))}async function A(a){let b=await p(),c=b.nodes.length;return b.nodes=b.nodes.filter(b=>b.id!==a),await q(b),b.nodes.length!==c}},10846:a=>{"use strict";a.exports=require("next/dist/compiled/next-server/app-page.runtime.prod.js")},21820:a=>{"use strict";a.exports=require("os")},29294:a=>{"use strict";a.exports=require("next/dist/server/app-render/work-async-storage.external.js")},33873:a=>{"use strict";a.exports=require("path")},44870:a=>{"use strict";a.exports=require("next/dist/compiled/next-server/app-route.runtime.prod.js")},55013:(a,b,c)=>{"use strict";c.d(b,{LM:()=>v,LQ:()=>u,MW:()=>s,dV:()=>l,eY:()=>t,iP:()=>m,nw:()=>o});var d=c(79748),e=c.n(d),f=c(33873),g=c.n(f),h=c(21820),i=c.n(h);let j=process.env.DATA_DIR||process.env.BLUEROUTER_DATA_DIR||g().join(i().homedir(),".bluerouter"),k=g().join(j,"ollama-model-memory.json"),l={"qwen3:1.7b":{minRamFreeMb:2048,minVramFreeMb:0,loadedRamFreeMb:512,loadedVramFreeMb:0,safetyMarginMb:512,notes:"small text model"},"qwen3:4b":{minRamFreeMb:4096,minVramFreeMb:0,loadedRamFreeMb:1024,loadedVramFreeMb:0,safetyMarginMb:512,notes:"small/medium text model"},"qwen3:8b":{minRamFreeMb:6144,minVramFreeMb:0,loadedRamFreeMb:1024,loadedVramFreeMb:512,safetyMarginMb:1024,notes:"8B text model"},"qwen3:14b":{minRamFreeMb:8192,minVramFreeMb:1e4,loadedRamFreeMb:2048,loadedVramFreeMb:1024,safetyMarginMb:1024,notes:"14B class model"},"qwen3:32b":{minRamFreeMb:4096,minVramFreeMb:16e3,loadedRamFreeMb:2048,loadedVramFreeMb:2048,safetyMarginMb:2048,notes:"32B class model, GPU preferred"},"qwen3-coder:30b":{minRamFreeMb:8192,minVramFreeMb:24e3,loadedRamFreeMb:2048,loadedVramFreeMb:3072,safetyMarginMb:2048,notes:"30B coder"},"qwen3-coder:latest":{minRamFreeMb:8192,minVramFreeMb:24e3,loadedRamFreeMb:2048,loadedVramFreeMb:3072,safetyMarginMb:2048,notes:"qwen3 coder latest, treat as 30B unless overridden"},"qwen2.5-coder:14b":{minRamFreeMb:8192,minVramFreeMb:1e4,loadedRamFreeMb:2048,loadedVramFreeMb:1024,safetyMarginMb:1024,notes:"14B coder"},"qwen2.5vl:7b":{minRamFreeMb:8192,minVramFreeMb:8e3,loadedRamFreeMb:2048,loadedVramFreeMb:1024,safetyMarginMb:1024,notes:"7B vision-language"},"qwen2.5vl:latest":{minRamFreeMb:8192,minVramFreeMb:1e4,loadedRamFreeMb:2048,loadedVramFreeMb:1024,safetyMarginMb:1024,notes:"vision-language latest"},"bge-m3:latest":{minRamFreeMb:4096,minVramFreeMb:0,loadedRamFreeMb:512,loadedVramFreeMb:0,safetyMarginMb:512,notes:"embedding model"},"deepseek-coder:6.7b":{minRamFreeMb:4096,minVramFreeMb:0,loadedRamFreeMb:1024,loadedVramFreeMb:512,safetyMarginMb:1024,notes:"6.7B coder"},"gemma4:e4b":{minRamFreeMb:4096,minVramFreeMb:0,loadedRamFreeMb:1024,loadedVramFreeMb:0,safetyMarginMb:1024,notes:"small Gemma variant"},"gemma:7b":{minRamFreeMb:4096,minVramFreeMb:0,loadedRamFreeMb:1024,loadedVramFreeMb:0,safetyMarginMb:1024,notes:"7B text model"},"glm-4.7-flash:latest":{minRamFreeMb:4096,minVramFreeMb:0,loadedRamFreeMb:1024,loadedVramFreeMb:512,safetyMarginMb:1024,notes:"flash/local text model"},"gpt-oss:20b":{minRamFreeMb:8192,minVramFreeMb:12e3,loadedRamFreeMb:2048,loadedVramFreeMb:2048,safetyMarginMb:2048,notes:"20B class model"},"llama3.1:8b":{minRamFreeMb:6144,minVramFreeMb:0,loadedRamFreeMb:1024,loadedVramFreeMb:512,safetyMarginMb:1024,notes:"8B text model"},"llama3.2-vision:latest":{minRamFreeMb:8192,minVramFreeMb:1e4,loadedRamFreeMb:2048,loadedVramFreeMb:1024,safetyMarginMb:1024,notes:"vision model"},"qwen3-*-translate-f16:*":{minRamFreeMb:8192,minVramFreeMb:16e3,loadedRamFreeMb:2048,loadedVramFreeMb:2048,safetyMarginMb:2048,notes:"custom Qwen3 f16 translate wildcard"},"qwen3-*-translate*:latest":{minRamFreeMb:6144,minVramFreeMb:8e3,loadedRamFreeMb:1024,loadedVramFreeMb:1024,safetyMarginMb:1024,notes:"custom Qwen3 translate wildcard"},"bge-m3:*":{minRamFreeMb:4096,minVramFreeMb:0,loadedRamFreeMb:512,loadedVramFreeMb:0,safetyMarginMb:512,notes:"embedding model wildcard"},"gemma*:e4b":{minRamFreeMb:4096,minVramFreeMb:0,loadedRamFreeMb:1024,loadedVramFreeMb:0,safetyMarginMb:1024,notes:"Gemma E4B wildcard"},"gemma*:7b":{minRamFreeMb:4096,minVramFreeMb:0,loadedRamFreeMb:1024,loadedVramFreeMb:0,safetyMarginMb:1024,notes:"Gemma 7B wildcard"},"*:1.7b":{minRamFreeMb:2048,minVramFreeMb:0,loadedRamFreeMb:512,loadedVramFreeMb:0,safetyMarginMb:512,notes:"generic 1.7B fallback"},"*:4b":{minRamFreeMb:4096,minVramFreeMb:0,loadedRamFreeMb:1024,loadedVramFreeMb:0,safetyMarginMb:512,notes:"generic 4B fallback"},"*:6.7b":{minRamFreeMb:4096,minVramFreeMb:0,loadedRamFreeMb:1024,loadedVramFreeMb:512,safetyMarginMb:1024,notes:"generic 6-7B fallback"},"*:7b":{minRamFreeMb:4096,minVramFreeMb:0,loadedRamFreeMb:1024,loadedVramFreeMb:512,safetyMarginMb:1024,notes:"generic 7B fallback"},"*:8b":{minRamFreeMb:6144,minVramFreeMb:0,loadedRamFreeMb:1024,loadedVramFreeMb:512,safetyMarginMb:1024,notes:"generic 8B fallback"},"*:14b":{minRamFreeMb:8192,minVramFreeMb:1e4,loadedRamFreeMb:2048,loadedVramFreeMb:1024,safetyMarginMb:1024,notes:"generic 14B fallback"},"*:20b":{minRamFreeMb:8192,minVramFreeMb:12e3,loadedRamFreeMb:2048,loadedVramFreeMb:2048,safetyMarginMb:2048,notes:"generic 20B fallback"},"*:30b":{minRamFreeMb:8192,minVramFreeMb:24e3,loadedRamFreeMb:2048,loadedVramFreeMb:3072,safetyMarginMb:2048,notes:"generic 30B fallback"},"*:32b":{minRamFreeMb:4096,minVramFreeMb:16e3,loadedRamFreeMb:2048,loadedVramFreeMb:2048,safetyMarginMb:2048,notes:"generic 32B fallback"}},m=["minRamFreeMb","minVramFreeMb","loadedRamFreeMb","loadedVramFreeMb","safetyMarginMb"];function n(a,b=0){let c=Number(a);return Number.isFinite(c)&&c>=0?c:b}function o(a={},b={}){let c={};if(!a||"object"!=typeof a||Array.isArray(a))return c;for(let[d,e]of Object.entries(a)){let a=String(d||"").trim();if(!a||!e||"object"!=typeof e||Array.isArray(e))continue;let f=function(a,{partial:b=!1,preserveEnabled:c=!1}={}){let d={};for(let c of m)if(b){var e;null!=(e=a?.[c])&&""!==String(e).trim()&&(d[c]=n(a[c],0))}else d[c]=n(a?.[c],0);let f=String(a?.notes||"").trim();return f&&(d.notes=f),c&&a&&Object.prototype.hasOwnProperty.call(a,"enabled")&&(d.enabled=!1!==a.enabled),d}(e,b),g=m.some(a=>Object.prototype.hasOwnProperty.call(f,a)),h=b.preserveEnabled&&Object.prototype.hasOwnProperty.call(f,"enabled");(g||f.notes||h)&&(c[a]=f)}return c}async function p(){await e().mkdir(j,{recursive:!0});try{await e().access(k)}catch{await e().writeFile(k,JSON.stringify({requirements:l,updatedAt:new Date().toISOString()},null,2))}}async function q(){await p();try{let a=await e().readFile(k,"utf8"),b=JSON.parse(a||"{}");return{requirements:function(a={}){return o({...l,...a})}(b.requirements||{}),updatedAt:b.updatedAt||null}}catch{return{requirements:o(l),updatedAt:null}}}async function r(a){await e().mkdir(j,{recursive:!0});let b={requirements:o(a),updatedAt:new Date().toISOString()};return await e().writeFile(k,JSON.stringify(b,null,2)),b}async function s(){return(await q()).requirements}async function t(){return q()}async function u(a){return r(a)}async function v(){return r(l)}},55511:a=>{"use strict";a.exports=require("crypto")},63033:a=>{"use strict";a.exports=require("next/dist/server/app-render/work-unit-async-storage.external.js")},68757:(a,b,c)=>{"use strict";c.r(b),c.d(b,{handler:()=>J,patchFetch:()=>I,routeModule:()=>E,serverHooks:()=>H,workAsyncStorage:()=>F,workUnitAsyncStorage:()=>G});var d={};c.r(d),c.d(d,{POST:()=>D,dynamic:()=>C,runtime:()=>B});var e=c(19225),f=c(84006),g=c(8317),h=c(99373),i=c(34775),j=c(24235),k=c(261),l=c(54365),m=c(90771),n=c(73461),o=c(67798),p=c(92280),q=c(62018),r=c(45696),s=c(47929),t=c(86439),u=c(37527),v=c(23211),w=c(6774),x=c(79646);function y(a){return`'${String(a??"").replace(/'/g,"'\"'\"'")}'`}function z(a,b,{input:c="",timeoutMs:d=12e4}={}){return new Promise(e=>{let f=(0,x.spawn)(a,b,{stdio:["pipe","pipe","pipe"]}),g="",h="",i=!1,j=setTimeout(()=>{i||f.kill("SIGKILL")},d);f.stdout.on("data",a=>{g+=a.toString()}),f.stderr.on("data",a=>{h+=a.toString()}),f.on("close",a=>{i=!0,clearTimeout(j),e({ok:0===a,code:a,stdout:g,stderr:h})}),c&&f.stdin.write(c),f.stdin.end()})}async function A(a,b={}){let c=a.sshUser||"root",d=Number(a.sshPort||22),e=`${c}@${a.host}`,f=function(a,b={}){let c="/opt/bluerouter-ollama-agent",d=(a.ollamaPorts||[11434]).join(","),e=String.raw`#!/usr/bin/env python3
2
2
  import json
3
3
  import os
4
4
  import re
5
+ import socket
5
6
  import subprocess
6
7
  import urllib.request
7
- from http.server import BaseHTTPRequestHandler, HTTPServer
8
8
  from datetime import datetime, timezone
9
+ from http.server import BaseHTTPRequestHandler, HTTPServer
9
10
 
10
11
  PORT = int(os.environ.get("AGENT_PORT", "19999"))
11
12
  OLLAMA_HOST = os.environ.get("OLLAMA_HOST", "127.0.0.1")
@@ -13,13 +14,23 @@ OLLAMA_PORTS = [p.strip() for p in os.environ.get("OLLAMA_PORTS", "11434").split
13
14
  AGENT_TOKEN = os.environ.get("AGENT_TOKEN", "")
14
15
 
15
16
 
16
- def run_cmd(cmd, timeout=3):
17
+ def run_cmd(cmd, timeout=4):
17
18
  try:
18
19
  return subprocess.check_output(cmd, stderr=subprocess.STDOUT, timeout=timeout, text=True).strip()
19
20
  except Exception:
20
21
  return None
21
22
 
22
23
 
24
+ def run_json(cmd, timeout=6):
25
+ out = run_cmd(cmd, timeout=timeout)
26
+ if not out:
27
+ return None
28
+ try:
29
+ return json.loads(out)
30
+ except Exception:
31
+ return None
32
+
33
+
23
34
  def http_json(url, timeout=3):
24
35
  try:
25
36
  with urllib.request.urlopen(url, timeout=timeout) as r:
@@ -28,32 +39,83 @@ def http_json(url, timeout=3):
28
39
  return None
29
40
 
30
41
 
31
- def get_gpu_uuid_map():
32
- out = run_cmd([
33
- "nvidia-smi",
34
- "--query-gpu=index,uuid,name",
35
- "--format=csv,noheader,nounits",
36
- ])
37
- uuid_to_gpu = {}
38
- if not out:
39
- return uuid_to_gpu
40
- for line in out.splitlines():
41
- parts = [x.strip() for x in line.split(",")]
42
- if len(parts) < 3:
42
+ def read_file(path):
43
+ try:
44
+ with open(path, "rb") as f:
45
+ return f.read()
46
+ except Exception:
47
+ return b""
48
+
49
+
50
+ def read_text(path):
51
+ try:
52
+ with open(path, "r", encoding="utf-8", errors="ignore") as f:
53
+ return f.read()
54
+ except Exception:
55
+ return ""
56
+
57
+
58
+ def read_cmdline(pid):
59
+ raw = read_file(f"/proc/{pid}/cmdline")
60
+ if not raw:
61
+ return ""
62
+ return raw.replace(b"\x00", b" ").decode("utf-8", errors="ignore").strip()
63
+
64
+
65
+ def read_environ(pid):
66
+ raw = read_file(f"/proc/{pid}/environ")
67
+ env = {}
68
+ if not raw:
69
+ return env
70
+ for item in raw.split(b"\x00"):
71
+ if not item or b"=" not in item:
43
72
  continue
44
- try:
45
- idx, uuid, name = parts[:3]
46
- uuid_to_gpu[uuid] = {"index": int(float(idx)), "uuid": uuid, "name": name}
47
- except Exception:
73
+ k, v = item.split(b"=", 1)
74
+ env[k.decode("utf-8", errors="ignore")] = v.decode("utf-8", errors="ignore")
75
+ return env
76
+
77
+
78
+ def parse_systemd_environment(text):
79
+ # systemctl show -p Environment usually returns: Environment=A=1 B=two C=/x/y
80
+ env = {}
81
+ if not text:
82
+ return env
83
+ if text.startswith("Environment="):
84
+ text = text[len("Environment="):]
85
+ for part in re.findall(r'(?:[^\s"\']+|"[^"]*"|\'[^\']*\')+', text):
86
+ item = part.strip().strip('"').strip("'")
87
+ if "=" not in item:
48
88
  continue
49
- return uuid_to_gpu
89
+ k, v = item.split("=", 1)
90
+ if k:
91
+ env[k] = v
92
+ return env
93
+
94
+
95
+ def get_service_from_cgroup(pid):
96
+ text = read_text(f"/proc/{pid}/cgroup")
97
+ m = re.search(r'/system\.slice/([^/]+\.service)', text)
98
+ if m:
99
+ return m.group(1)
100
+ # user services may appear under user.slice/user-1000.slice/.../app.slice/name.service
101
+ m = re.search(r'/([^/]+\.service)(?:$|/)', text)
102
+ if m:
103
+ return m.group(1)
104
+ return None
105
+
106
+
107
+ def get_systemd_env(service):
108
+ if not service:
109
+ return {}
110
+ out = run_cmd(["systemctl", "show", service, "-p", "Environment"], timeout=3)
111
+ return parse_systemd_environment(out or "")
50
112
 
51
113
 
52
114
  def get_gpu_status():
53
115
  out = run_cmd([
54
116
  "nvidia-smi",
55
117
  "--query-gpu=index,uuid,name,memory.total,memory.used,memory.free,utilization.gpu,temperature.gpu",
56
- "--format=csv,noheader,nounits",
118
+ "--format=csv,noheader,nounits"
57
119
  ])
58
120
  if not out:
59
121
  return []
@@ -74,105 +136,13 @@ def get_gpu_status():
74
136
  "vramFreeMb": free,
75
137
  "vramUsedPercent": round((used / total) * 100, 2) if total else None,
76
138
  "gpuUsagePercent": int(float(util)),
77
- "temperatureC": int(float(temp)),
139
+ "temperatureC": int(float(temp))
78
140
  })
79
141
  except Exception:
80
142
  pass
81
143
  return gpus
82
144
 
83
145
 
84
- def get_gpu_processes():
85
- """Return process-level GPU usage from nvidia-smi compute apps.
86
-
87
- This is the key signal used to infer port -> PID -> GPU index. It is still
88
- process-level, not per-model. If one Ollama process serves multiple models,
89
- the mapping says which GPUs that port/process uses, not which model uses each GPU.
90
- """
91
- uuid_map = get_gpu_uuid_map()
92
- out = run_cmd([
93
- "nvidia-smi",
94
- "--query-compute-apps=gpu_uuid,pid,process_name,used_memory",
95
- "--format=csv,noheader,nounits",
96
- ])
97
- processes = []
98
- if not out:
99
- return processes
100
- for line in out.splitlines():
101
- parts = [x.strip() for x in line.split(",")]
102
- if len(parts) < 4:
103
- continue
104
- uuid, pid, process_name, used_memory = parts[:4]
105
- try:
106
- pid_i = int(float(pid))
107
- used_mb = int(float(str(used_memory).replace("MiB", "").strip()))
108
- gpu = uuid_map.get(uuid, {})
109
- processes.append({
110
- "gpuUuid": uuid,
111
- "gpuIndex": gpu.get("index"),
112
- "gpuName": gpu.get("name"),
113
- "pid": pid_i,
114
- "processName": process_name,
115
- "usedMemoryMb": used_mb,
116
- })
117
- except Exception:
118
- continue
119
- return processes
120
-
121
-
122
- def get_process_name(pid):
123
- if not pid:
124
- return None
125
- out = run_cmd(["ps", "-p", str(pid), "-o", "comm="], timeout=2)
126
- return out.strip() if out else None
127
-
128
-
129
- def get_listening_pid(port):
130
- """Best-effort Linux TCP listener PID detection for a local Ollama port."""
131
- port_s = str(port)
132
-
133
- # ss is installed on most modern Linux distributions.
134
- out = run_cmd(["ss", "-ltnp", f"sport = :{port_s}"], timeout=3)
135
- if out:
136
- # users:(("ollama",pid=1234,fd=3))
137
- m = re.search(r"pid=(\d+)", out)
138
- if m:
139
- try:
140
- return int(m.group(1))
141
- except Exception:
142
- pass
143
-
144
- # Fallback to lsof when available.
145
- out = run_cmd(["lsof", "-nP", f"-iTCP:{port_s}", "-sTCP:LISTEN", "-t"], timeout=3)
146
- if out:
147
- for line in out.splitlines():
148
- line = line.strip()
149
- if line.isdigit():
150
- return int(line)
151
-
152
- # Last-resort netstat parse.
153
- out = run_cmd(["netstat", "-ltnp"], timeout=3)
154
- if out:
155
- for line in out.splitlines():
156
- if f":{port_s} " not in line:
157
- continue
158
- m = re.search(r"\s(\d+)/", line)
159
- if m:
160
- return int(m.group(1))
161
- return None
162
-
163
-
164
- def gpu_binding_for_pid(pid, gpu_processes):
165
- if not pid:
166
- return {"gpuIndices": [], "gpuVramUsedMb": 0, "gpuProcesses": []}
167
- matched = [p for p in gpu_processes if int(p.get("pid") or -1) == int(pid)]
168
- indices = sorted({p.get("gpuIndex") for p in matched if p.get("gpuIndex") is not None})
169
- return {
170
- "gpuIndices": indices,
171
- "gpuVramUsedMb": sum(int(p.get("usedMemoryMb") or 0) for p in matched),
172
- "gpuProcesses": matched,
173
- }
174
-
175
-
176
146
  def get_ram_status():
177
147
  out = run_cmd(["free", "-m"])
178
148
  if not out:
@@ -187,7 +157,7 @@ def get_ram_status():
187
157
  "usedMb": used,
188
158
  "freeMb": free,
189
159
  "availableMb": avail,
190
- "usedPercent": round((used / total) * 100, 2) if total else None,
160
+ "usedPercent": round((used / total) * 100, 2) if total else None
191
161
  }
192
162
  return None
193
163
 
@@ -200,10 +170,39 @@ def get_cpu_status():
200
170
  "load1": load[0],
201
171
  "load5": load[1],
202
172
  "load15": load[2],
203
- "load1PercentOfCores": round((load[0] / cores) * 100, 2),
173
+ "load1PercentOfCores": round((load[0] / cores) * 100, 2)
204
174
  }
205
175
 
206
176
 
177
+ def get_gpu_processes(gpus):
178
+ uuid_to_index = {g.get("uuid"): g.get("index") for g in gpus if g.get("uuid")}
179
+ out = run_cmd([
180
+ "nvidia-smi",
181
+ "--query-compute-apps=gpu_uuid,pid,process_name,used_memory",
182
+ "--format=csv,noheader,nounits"
183
+ ])
184
+ processes = []
185
+ if out:
186
+ for line in out.splitlines():
187
+ parts = [x.strip() for x in line.split(",")]
188
+ if len(parts) < 4:
189
+ continue
190
+ gpu_uuid, pid, process_name, used_memory = parts[:4]
191
+ try:
192
+ pid_n = int(float(pid))
193
+ used_n = int(float(str(used_memory).replace("MiB", "").strip()))
194
+ except Exception:
195
+ continue
196
+ processes.append({
197
+ "gpuUuid": gpu_uuid,
198
+ "gpuIndex": uuid_to_index.get(gpu_uuid),
199
+ "pid": pid_n,
200
+ "processName": process_name,
201
+ "usedMemoryMb": used_n
202
+ })
203
+ return processes
204
+
205
+
207
206
  def compact_model(item):
208
207
  if not isinstance(item, dict):
209
208
  return None
@@ -215,11 +214,401 @@ def compact_model(item):
215
214
  "size_vram": item.get("size_vram"),
216
215
  "expires_at": item.get("expires_at"),
217
216
  "context_length": item.get("context_length"),
218
- "details": item.get("details") or {},
217
+ "details": item.get("details") or {}
218
+ }
219
+
220
+
221
+ def find_listener_pid_by_ss(port):
222
+ candidates = []
223
+ commands = [
224
+ ["ss", "-H", "-ltnp", "sport", "=", f":{port}"],
225
+ ["ss", "-H", "-ltnp"],
226
+ ]
227
+ for cmd in commands:
228
+ out = run_cmd(cmd, timeout=3)
229
+ if not out:
230
+ continue
231
+ for line in out.splitlines():
232
+ if f":{port}" not in line:
233
+ continue
234
+ for pid in re.findall(r'pid=([0-9]+)', line):
235
+ candidates.append({"pid": int(pid), "source": "ss", "line": line})
236
+ return candidates
237
+
238
+
239
+ def find_listener_pid_by_lsof(port):
240
+ candidates = []
241
+ out = run_cmd(["lsof", "-nP", f"-iTCP:{port}", "-sTCP:LISTEN"], timeout=4)
242
+ if not out:
243
+ return candidates
244
+ for line in out.splitlines()[1:]:
245
+ parts = line.split()
246
+ if len(parts) >= 2 and parts[1].isdigit():
247
+ candidates.append({"pid": int(parts[1]), "source": "lsof", "line": line})
248
+ return candidates
249
+
250
+
251
+ def find_listener_pid_by_fuser(port):
252
+ candidates = []
253
+ out = run_cmd(["fuser", "-n", "tcp", str(port)], timeout=4)
254
+ if not out:
255
+ return candidates
256
+ for pid in re.findall(r'\b[0-9]+\b', out):
257
+ candidates.append({"pid": int(pid), "source": "fuser", "line": out})
258
+ return candidates
259
+
260
+
261
+ def tcp_port_hex(port):
262
+ return f"{int(port):04X}"
263
+
264
+
265
+ def listener_inodes_for_port(port):
266
+ inodes = set()
267
+ want = tcp_port_hex(port)
268
+ for path in ["/proc/net/tcp", "/proc/net/tcp6"]:
269
+ text = read_text(path)
270
+ for line in text.splitlines()[1:]:
271
+ cols = line.split()
272
+ if len(cols) < 10:
273
+ continue
274
+ local = cols[1]
275
+ state = cols[3]
276
+ inode = cols[9]
277
+ if state != "0A":
278
+ continue
279
+ if local.split(":")[-1].upper() == want:
280
+ inodes.add(inode)
281
+ return inodes
282
+
283
+
284
+ def find_listener_pid_by_proc(port):
285
+ candidates = []
286
+ inodes = listener_inodes_for_port(port)
287
+ if not inodes:
288
+ return candidates
289
+ proc_root = "/proc"
290
+ for pid in os.listdir(proc_root):
291
+ if not pid.isdigit():
292
+ continue
293
+ fd_dir = os.path.join(proc_root, pid, "fd")
294
+ try:
295
+ fds = os.listdir(fd_dir)
296
+ except Exception:
297
+ continue
298
+ for fd in fds:
299
+ try:
300
+ target = os.readlink(os.path.join(fd_dir, fd))
301
+ except Exception:
302
+ continue
303
+ m = re.match(r'socket:\[([0-9]+)\]', target)
304
+ if m and m.group(1) in inodes:
305
+ candidates.append({"pid": int(pid), "source": "proc-net", "line": f"inode={m.group(1)} fd={fd}"})
306
+ break
307
+ return candidates
308
+
309
+
310
+ def find_listener_pid(port):
311
+ all_candidates = []
312
+ for fn in [find_listener_pid_by_ss, find_listener_pid_by_lsof, find_listener_pid_by_fuser, find_listener_pid_by_proc]:
313
+ try:
314
+ all_candidates.extend(fn(port))
315
+ except Exception:
316
+ pass
317
+ seen = set()
318
+ unique = []
319
+ for c in all_candidates:
320
+ pid = c.get("pid")
321
+ if not pid or pid in seen:
322
+ continue
323
+ seen.add(pid)
324
+ unique.append(c)
325
+ if not unique:
326
+ return None, None, []
327
+ chosen = unique[0]
328
+ return chosen.get("pid"), chosen.get("source"), unique
329
+
330
+
331
+ def process_tree_pids(root_pid):
332
+ try:
333
+ root_pid = int(root_pid)
334
+ except Exception:
335
+ return []
336
+ out = run_cmd(["ps", "-eo", "pid=,ppid=,cmd="], timeout=4)
337
+ children = {}
338
+ if out:
339
+ for line in out.splitlines():
340
+ parts = line.strip().split(None, 2)
341
+ if len(parts) < 2:
342
+ continue
343
+ try:
344
+ pid = int(parts[0]); ppid = int(parts[1])
345
+ except Exception:
346
+ continue
347
+ children.setdefault(ppid, []).append(pid)
348
+ result = []
349
+ stack = [root_pid]
350
+ seen = set()
351
+ while stack:
352
+ pid = stack.pop()
353
+ if pid in seen:
354
+ continue
355
+ seen.add(pid)
356
+ result.append(pid)
357
+ stack.extend(children.get(pid, []))
358
+ return result
359
+
360
+
361
+ def docker_ps_rows():
362
+ out = run_cmd(["docker", "ps", "--format", "{{.ID}}\t{{.Names}}\t{{.Ports}}"], timeout=5)
363
+ rows = []
364
+ if not out:
365
+ return rows
366
+ for line in out.splitlines():
367
+ parts = line.split("\t")
368
+ if len(parts) >= 3:
369
+ rows.append({"id": parts[0], "name": parts[1], "ports": parts[2]})
370
+ return rows
371
+
372
+
373
+ def docker_inspect(cid):
374
+ data = run_json(["docker", "inspect", cid], timeout=6)
375
+ if isinstance(data, list) and data:
376
+ return data[0]
377
+ return None
378
+
379
+
380
+ def find_docker_container_for_port(port, docker_proxy_info=None):
381
+ # Prefer Docker's authoritative NetworkSettings.Ports mapping.
382
+ for row in docker_ps_rows():
383
+ info = docker_inspect(row["id"])
384
+ if not info:
385
+ continue
386
+ ports = (((info.get("NetworkSettings") or {}).get("Ports")) or {})
387
+ for container_port, host_bindings in ports.items():
388
+ if not str(container_port).startswith(str(port)) and not str(container_port).startswith(str((docker_proxy_info or {}).get("containerPort", ""))):
389
+ pass
390
+ for binding in host_bindings or []:
391
+ if str(binding.get("HostPort")) == str(port):
392
+ return row, info
393
+ # Fallback: match container IP parsed from docker-proxy command.
394
+ container_ip = (docker_proxy_info or {}).get("containerIp")
395
+ if container_ip:
396
+ networks = ((info.get("NetworkSettings") or {}).get("Networks")) or {}
397
+ for net in networks.values():
398
+ if net.get("IPAddress") == container_ip:
399
+ return row, info
400
+ # Fallback: match docker ps formatted Ports text.
401
+ if f":{port}->" in row.get("ports", "") or f"0.0.0.0:{port}" in row.get("ports", "") or f"[::]:{port}" in row.get("ports", ""):
402
+ return row, info
403
+ return None, None
404
+
405
+
406
+ def parse_docker_proxy_cmd(cmdline):
407
+ if "docker-proxy" not in (cmdline or ""):
408
+ return None
409
+ tokens = cmdline.split()
410
+ out = {"kind": "docker-proxy"}
411
+ for i, token in enumerate(tokens):
412
+ if token in ["-host-port", "--host-port"] and i + 1 < len(tokens):
413
+ out["hostPort"] = tokens[i + 1]
414
+ elif token in ["-container-port", "--container-port"] and i + 1 < len(tokens):
415
+ out["containerPort"] = tokens[i + 1]
416
+ elif token in ["-container-ip", "--container-ip"] and i + 1 < len(tokens):
417
+ out["containerIp"] = tokens[i + 1]
418
+ return out
419
+
420
+
421
+ def docker_env_and_meta(port, pid, cmdline):
422
+ proxy = parse_docker_proxy_cmd(cmdline)
423
+ if not proxy:
424
+ return None
425
+ row, info = find_docker_container_for_port(port, proxy)
426
+ if not info:
427
+ return {
428
+ "runtimeType": "docker-proxy",
429
+ "dockerProxy": proxy,
430
+ "containerFound": False,
431
+ "env": {},
432
+ "source": "docker-proxy-no-container"
433
+ }
434
+ env_list = ((info.get("Config") or {}).get("Env")) or []
435
+ env = {}
436
+ for item in env_list:
437
+ if "=" in item:
438
+ k, v = item.split("=", 1)
439
+ env[k] = v
440
+ host_config = info.get("HostConfig") or {}
441
+ state = info.get("State") or {}
442
+ labels = (info.get("Config") or {}).get("Labels") or {}
443
+ networks = ((info.get("NetworkSettings") or {}).get("Networks")) or {}
444
+ ips = []
445
+ for net_name, net in networks.items():
446
+ if net.get("IPAddress"):
447
+ ips.append({"network": net_name, "ip": net.get("IPAddress")})
448
+ return {
449
+ "runtimeType": "docker",
450
+ "dockerProxy": proxy,
451
+ "containerFound": True,
452
+ "containerId": (info.get("Id") or row.get("id"))[:12],
453
+ "containerName": (info.get("Name") or row.get("name") or "").lstrip("/"),
454
+ "containerImage": (info.get("Config") or {}).get("Image"),
455
+ "containerPid": state.get("Pid"),
456
+ "containerIps": ips,
457
+ "dockerPorts": row.get("ports"),
458
+ "dockerNetworkMode": host_config.get("NetworkMode"),
459
+ "dockerDeviceRequests": host_config.get("DeviceRequests") or [],
460
+ "dockerLabels": labels,
461
+ "env": env,
462
+ "source": "docker-inspect"
463
+ }
464
+
465
+
466
+ def parse_visible_devices(value, gpus, source):
467
+ if value is None:
468
+ return None
469
+ raw = str(value).strip()
470
+ lower = raw.lower()
471
+ if raw == "":
472
+ return None
473
+ if lower in ["none", "void", "no", "false", "-1"]:
474
+ return {"indices": [], "mode": "disabled", "source": source, "raw": raw}
475
+ if lower == "all":
476
+ return {"indices": [g["index"] for g in gpus], "mode": "all-visible", "source": source, "raw": raw}
477
+ uuid_to_index = {str(g.get("uuid")): g.get("index") for g in gpus if g.get("uuid")}
478
+ indices = []
479
+ unknown = []
480
+ for item in re.split(r'[,;\s]+', raw):
481
+ if not item:
482
+ continue
483
+ if item.isdigit():
484
+ indices.append(int(item))
485
+ elif item in uuid_to_index:
486
+ indices.append(uuid_to_index[item])
487
+ elif item.startswith("GPU-") and item in uuid_to_index:
488
+ indices.append(uuid_to_index[item])
489
+ elif item.startswith("MIG-"):
490
+ unknown.append(item)
491
+ else:
492
+ unknown.append(item)
493
+ return {
494
+ "indices": sorted(list(set(indices))),
495
+ "mode": "specific" if indices else "unknown",
496
+ "source": source,
497
+ "raw": raw,
498
+ "unknownDevices": unknown
219
499
  }
220
500
 
221
501
 
222
- def get_ollama_instance(port, gpu_processes):
502
+ def parse_device_requests(device_requests, gpus):
503
+ if not device_requests:
504
+ return None
505
+ ids = []
506
+ all_gpu = False
507
+ for req in device_requests:
508
+ count = req.get("Count")
509
+ caps = req.get("Capabilities") or []
510
+ driver = req.get("Driver")
511
+ req_ids = req.get("DeviceIDs") or []
512
+ if driver == "nvidia" or any("gpu" in str(x).lower() for cap in caps for x in cap):
513
+ if count == -1 and not req_ids:
514
+ all_gpu = True
515
+ ids.extend(req_ids)
516
+ if all_gpu:
517
+ return {"indices": [g["index"] for g in gpus], "mode": "all-visible", "source": "docker-device-requests:all", "raw": "all"}
518
+ if ids:
519
+ return parse_visible_devices(",".join(ids), gpus, "docker-device-requests")
520
+ return None
521
+
522
+
523
+ def configured_gpu_from_env(runtime_type, env, gpus, docker_meta=None):
524
+ # Prefer NVIDIA_VISIBLE_DEVICES in Docker, CUDA_VISIBLE_DEVICES for direct/systemd.
525
+ candidates = []
526
+ if runtime_type == "docker":
527
+ candidates = [
528
+ ("NVIDIA_VISIBLE_DEVICES", env.get("NVIDIA_VISIBLE_DEVICES")),
529
+ ("CUDA_VISIBLE_DEVICES", env.get("CUDA_VISIBLE_DEVICES")),
530
+ ]
531
+ req = parse_device_requests((docker_meta or {}).get("dockerDeviceRequests"), gpus)
532
+ if req:
533
+ return req
534
+ else:
535
+ candidates = [
536
+ ("CUDA_VISIBLE_DEVICES", env.get("CUDA_VISIBLE_DEVICES")),
537
+ ("NVIDIA_VISIBLE_DEVICES", env.get("NVIDIA_VISIBLE_DEVICES")),
538
+ ("HIP_VISIBLE_DEVICES", env.get("HIP_VISIBLE_DEVICES")),
539
+ ("ROCR_VISIBLE_DEVICES", env.get("ROCR_VISIBLE_DEVICES")),
540
+ ]
541
+ for key, value in candidates:
542
+ parsed = parse_visible_devices(value, gpus, f"{runtime_type}-env:{key}")
543
+ if parsed:
544
+ return parsed
545
+ if runtime_type in ["systemd", "direct"]:
546
+ return {"indices": [g["index"] for g in gpus], "mode": "all-visible", "source": "all-visible-no-env", "raw": None}
547
+ if runtime_type == "docker":
548
+ # For Docker, lack of NVIDIA_VISIBLE_DEVICES is less conclusive.
549
+ return {"indices": [], "mode": "unknown", "source": "docker-no-visible-devices-env", "raw": None}
550
+ return {"indices": [], "mode": "unknown", "source": "no-gpu-config", "raw": None}
551
+
552
+
553
+ def process_metadata(port, pid, gpus):
554
+ if not pid:
555
+ return {"runtimeType": "unknown", "env": {}, "configured": configured_gpu_from_env("unknown", {}, gpus)}
556
+ cmdline = read_cmdline(pid)
557
+ service = get_service_from_cgroup(pid)
558
+ runtime_type = "systemd" if service else "direct"
559
+ docker_meta = docker_env_and_meta(port, pid, cmdline)
560
+ if docker_meta:
561
+ runtime_type = docker_meta.get("runtimeType") or "docker"
562
+ env = docker_meta.get("env") or {}
563
+ configured = configured_gpu_from_env("docker", env, gpus, docker_meta)
564
+ return {
565
+ "runtimeType": runtime_type,
566
+ "processCmdline": cmdline,
567
+ "serviceName": service,
568
+ "env": env,
569
+ "configured": configured,
570
+ **{k: v for k, v in docker_meta.items() if k not in ["env"]}
571
+ }
572
+ env = {}
573
+ env.update(get_systemd_env(service))
574
+ env.update(read_environ(pid))
575
+ configured = configured_gpu_from_env("systemd" if service else "direct", env, gpus)
576
+ return {
577
+ "runtimeType": runtime_type,
578
+ "processCmdline": cmdline,
579
+ "serviceName": service,
580
+ "env": {k: v for k, v in env.items() if re.search(r'OLLAMA|CUDA|NVIDIA|HIP|ROCR|GPU', k, re.I)},
581
+ "configured": configured,
582
+ }
583
+
584
+
585
+ def gpu_processes_for_pids(gpu_processes, pids):
586
+ pid_set = set(int(x) for x in pids if x)
587
+ return [p for p in gpu_processes if int(p.get("pid") or -1) in pid_set]
588
+
589
+
590
+ def infer_gpu_from_ps(ps_models, gpus, configured):
591
+ size_bytes = sum(int(m.get("size_vram") or 0) for m in ps_models or [])
592
+ size_mb = int(size_bytes / (1024 * 1024)) if size_bytes else 0
593
+ if size_mb <= 0:
594
+ return [], 0, None
595
+ configured_indices = configured.get("indices") or [] if configured else []
596
+ if configured_indices:
597
+ return configured_indices, size_mb, "ollama-ps-configured-gpu-inferred"
598
+ if len(gpus) == 1:
599
+ return [gpus[0]["index"]], size_mb, "ollama-ps-single-gpu-inferred"
600
+ # Conservative multi-GPU inference: only infer if one GPU's used memory is close enough.
601
+ candidates = []
602
+ for gpu in gpus:
603
+ used = int(gpu.get("vramUsedMb") or 0)
604
+ if used >= max(512, int(size_mb * 0.45)):
605
+ candidates.append(gpu)
606
+ if len(candidates) == 1:
607
+ return [candidates[0]["index"]], size_mb, "ollama-ps-single-used-gpu-inferred"
608
+ return [], 0, None
609
+
610
+
611
+ def get_ollama_instance(port, gpus, gpu_processes):
223
612
  base = f"http://{OLLAMA_HOST}:{port}"
224
613
  tags = http_json(f"{base}/api/tags")
225
614
  ps = http_json(f"{base}/api/ps")
@@ -242,36 +631,88 @@ def get_ollama_instance(port, gpu_processes):
242
631
  ps_models.append(cm)
243
632
  loaded_models.append(cm["name"])
244
633
 
245
- pid = get_listening_pid(port)
246
- binding = gpu_binding_for_pid(pid, gpu_processes)
634
+ pid, pid_source, pid_candidates = find_listener_pid(port)
635
+ meta = process_metadata(port, pid, gpus)
636
+ pid_tree = process_tree_pids(pid) if pid else []
637
+ # Docker containers may expose a container init PID whose children hold GPU handles.
638
+ container_pid = meta.get("containerPid")
639
+ if container_pid:
640
+ pid_tree = sorted(list(set(pid_tree + process_tree_pids(container_pid))))
641
+ matched_gpu_processes = gpu_processes_for_pids(gpu_processes, pid_tree)
642
+ runtime_indices = sorted(list(set([p.get("gpuIndex") for p in matched_gpu_processes if p.get("gpuIndex") is not None])))
643
+ runtime_used = sum(int(p.get("usedMemoryMb") or 0) for p in matched_gpu_processes)
644
+ runtime_source = "port-pid-nvidia-smi" if runtime_indices else None
645
+ ps_indices, ps_used, ps_source = infer_gpu_from_ps(ps_models, gpus, meta.get("configured") or {})
646
+
647
+ configured = meta.get("configured") or {}
648
+ effective_indices = []
649
+ effective_source = None
650
+ if configured.get("indices"):
651
+ effective_indices = configured.get("indices")
652
+ effective_source = configured.get("source")
653
+ elif runtime_indices:
654
+ effective_indices = runtime_indices
655
+ effective_source = runtime_source
656
+ elif ps_indices:
657
+ effective_indices = ps_indices
658
+ effective_source = ps_source
659
+
660
+ detected_indices = runtime_indices or ps_indices
661
+ detected_used = runtime_used if runtime_indices else ps_used
662
+ detected_source = runtime_source or ps_source or ("port-pid-no-gpu" if pid else None)
247
663
 
248
664
  return {
249
665
  "id": f"ollama-{port}",
250
666
  "port": int(port),
251
667
  "baseUrl": base,
252
668
  "alive": tags is not None,
253
- "pid": pid,
254
- "processName": get_process_name(pid),
255
669
  "models": models,
256
670
  "modelDetails": model_details,
257
671
  "loadedModels": loaded_models,
258
672
  "psModels": ps_models,
259
673
  "loadedDigests": sorted(list(set([m.get("digest") for m in ps_models if m.get("digest")]))),
260
- "detectedGpuIndices": binding["gpuIndices"],
261
- "detectedGpuVramUsedMb": binding["gpuVramUsedMb"],
262
- "detectedGpuProcesses": binding["gpuProcesses"],
263
- "detectedGpuBindingSource": "port-pid-nvidia-smi" if pid and binding["gpuIndices"] else ("port-pid-no-gpu" if pid else "unknown"),
674
+ "pid": pid,
675
+ "pidDetectionSource": pid_source,
676
+ "pidCandidates": pid_candidates,
677
+ "pidTree": pid_tree,
678
+ "runtimeType": meta.get("runtimeType"),
679
+ "serviceName": meta.get("serviceName"),
680
+ "processCmdline": meta.get("processCmdline"),
681
+ "processEnv": meta.get("env"),
682
+ "containerId": meta.get("containerId"),
683
+ "containerName": meta.get("containerName"),
684
+ "containerImage": meta.get("containerImage"),
685
+ "containerPid": meta.get("containerPid"),
686
+ "containerIps": meta.get("containerIps"),
687
+ "dockerNetworkMode": meta.get("dockerNetworkMode"),
688
+ "dockerPorts": meta.get("dockerPorts"),
689
+ "dockerDeviceRequests": meta.get("dockerDeviceRequests"),
690
+ "dockerLabels": meta.get("dockerLabels"),
691
+ "dockerProxy": meta.get("dockerProxy"),
692
+ "configuredGpuIndices": configured.get("indices") or [],
693
+ "configuredGpuMode": configured.get("mode"),
694
+ "configuredGpuSource": configured.get("source"),
695
+ "configuredGpuRaw": configured.get("raw"),
696
+ "configuredGpuUnknownDevices": configured.get("unknownDevices") or [],
697
+ "runtimeGpuIndices": runtime_indices,
698
+ "runtimeGpuVramUsedMb": runtime_used,
699
+ "runtimeGpuProcesses": matched_gpu_processes,
700
+ "detectedGpuIndices": detected_indices,
701
+ "detectedGpuVramUsedMb": detected_used,
702
+ "detectedGpuProcesses": matched_gpu_processes,
703
+ "detectedGpuBindingSource": detected_source,
704
+ "effectiveGpuIndices": effective_indices,
705
+ "effectiveGpuSource": effective_source,
264
706
  }
265
707
 
266
708
 
267
709
  def build_status():
268
710
  hostname = run_cmd(["hostname"]) or "unknown"
269
711
  gpus = get_gpu_status()
270
- gpu_processes = get_gpu_processes()
712
+ gpu_processes = get_gpu_processes(gpus)
271
713
  ram = get_ram_status()
272
714
  cpu = get_cpu_status()
273
- instances = [get_ollama_instance(p, gpu_processes) for p in OLLAMA_PORTS]
274
-
715
+ instances = [get_ollama_instance(p, gpus, gpu_processes) for p in OLLAMA_PORTS]
275
716
  tags = ["ollama", "lan"]
276
717
  if gpus:
277
718
  tags += ["gpu", "nvidia"]
@@ -279,11 +720,9 @@ def build_status():
279
720
  tags.append(f"gpu:{gpu.get('index')}:{str(gpu.get('name','')).lower().replace(' ','-')}")
280
721
  else:
281
722
  tags.append("cpu")
282
-
283
723
  for inst in instances:
284
724
  for model in inst.get("models", []):
285
725
  tags.append(f"model_support:{model}")
286
-
287
726
  best_gpu = max(gpus, key=lambda x: x.get("vramFreeMb", 0)) if gpus else None
288
727
  return {
289
728
  "ok": True,
@@ -294,10 +733,10 @@ def build_status():
294
733
  "score": {
295
734
  "gpuFreeVramMb": best_gpu.get("vramFreeMb") if best_gpu else 0,
296
735
  "gpuUsagePercent": best_gpu.get("gpuUsagePercent") if best_gpu else None,
297
- "ramAvailableMb": ram.get("availableMb") if ram else None,
736
+ "ramAvailableMb": ram.get("availableMb") if ram else None
298
737
  },
299
738
  "tags": sorted(set(tags)),
300
- "updatedAt": datetime.now(timezone.utc).isoformat(),
739
+ "updatedAt": datetime.now(timezone.utc).isoformat()
301
740
  }
302
741
 
303
742
 
@@ -346,7 +785,7 @@ chmod +x "$AGENT_DIR/agent.py"
346
785
  cat > /etc/systemd/system/bluerouter-ollama-agent.service <<'SERVICE'
347
786
  [Unit]
348
787
  Description=BlueRouter Ollama Hardware Status Agent
349
- After=network.target ollama.service
788
+ After=network.target docker.service ollama.service
350
789
 
351
790
  [Service]
352
791
  Type=simple