9router 0.2.93 → 0.2.94

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 (291) hide show
  1. package/app/.next/BUILD_ID +1 -1
  2. package/app/.next/app-path-routes-manifest.json +34 -34
  3. package/app/.next/build-manifest.json +2 -2
  4. package/app/.next/server/app/(dashboard)/dashboard/cli-tools/page.js +2 -2
  5. package/app/.next/server/app/(dashboard)/dashboard/cli-tools/page.js.nft.json +1 -1
  6. package/app/.next/server/app/(dashboard)/dashboard/cli-tools/page_client-reference-manifest.js +1 -1
  7. package/app/.next/server/app/(dashboard)/dashboard/combos/page.js +2 -2
  8. package/app/.next/server/app/(dashboard)/dashboard/combos/page.js.nft.json +1 -1
  9. package/app/.next/server/app/(dashboard)/dashboard/combos/page_client-reference-manifest.js +1 -1
  10. package/app/.next/server/app/(dashboard)/dashboard/endpoint/page.js +2 -2
  11. package/app/.next/server/app/(dashboard)/dashboard/endpoint/page.js.nft.json +1 -1
  12. package/app/.next/server/app/(dashboard)/dashboard/endpoint/page_client-reference-manifest.js +1 -1
  13. package/app/.next/server/app/(dashboard)/dashboard/page.js +2 -2
  14. package/app/.next/server/app/(dashboard)/dashboard/page.js.nft.json +1 -1
  15. package/app/.next/server/app/(dashboard)/dashboard/page_client-reference-manifest.js +1 -1
  16. package/app/.next/server/app/(dashboard)/dashboard/profile/page.js +2 -2
  17. package/app/.next/server/app/(dashboard)/dashboard/profile/page.js.nft.json +1 -1
  18. package/app/.next/server/app/(dashboard)/dashboard/profile/page_client-reference-manifest.js +1 -1
  19. package/app/.next/server/app/(dashboard)/dashboard/providers/[id]/page.js +2 -2
  20. package/app/.next/server/app/(dashboard)/dashboard/providers/[id]/page.js.nft.json +1 -1
  21. package/app/.next/server/app/(dashboard)/dashboard/providers/[id]/page_client-reference-manifest.js +1 -1
  22. package/app/.next/server/app/(dashboard)/dashboard/providers/new/page.js +2 -2
  23. package/app/.next/server/app/(dashboard)/dashboard/providers/new/page.js.nft.json +1 -1
  24. package/app/.next/server/app/(dashboard)/dashboard/providers/new/page_client-reference-manifest.js +1 -1
  25. package/app/.next/server/app/(dashboard)/dashboard/providers/page.js +2 -2
  26. package/app/.next/server/app/(dashboard)/dashboard/providers/page.js.nft.json +1 -1
  27. package/app/.next/server/app/(dashboard)/dashboard/providers/page_client-reference-manifest.js +1 -1
  28. package/app/.next/server/app/(dashboard)/dashboard/translator/page.js +2 -2
  29. package/app/.next/server/app/(dashboard)/dashboard/translator/page.js.nft.json +1 -1
  30. package/app/.next/server/app/(dashboard)/dashboard/translator/page_client-reference-manifest.js +1 -1
  31. package/app/.next/server/app/(dashboard)/dashboard/usage/page.js +2 -2
  32. package/app/.next/server/app/(dashboard)/dashboard/usage/page.js.nft.json +1 -1
  33. package/app/.next/server/app/(dashboard)/dashboard/usage/page_client-reference-manifest.js +1 -1
  34. package/app/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  35. package/app/.next/server/app/_global-error.html +2 -2
  36. package/app/.next/server/app/_global-error.rsc +1 -1
  37. package/app/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  38. package/app/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  39. package/app/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  40. package/app/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  41. package/app/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  42. package/app/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  43. package/app/.next/server/app/_not-found/page.js +2 -2
  44. package/app/.next/server/app/_not-found/page.js.nft.json +1 -1
  45. package/app/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  46. package/app/.next/server/app/_not-found.html +1 -1
  47. package/app/.next/server/app/_not-found.rsc +2 -2
  48. package/app/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  49. package/app/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  50. package/app/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  51. package/app/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  52. package/app/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  53. package/app/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  54. package/app/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
  55. package/app/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
  56. package/app/.next/server/app/api/cli-tools/antigravity-mitm/alias/route_client-reference-manifest.js +1 -1
  57. package/app/.next/server/app/api/cli-tools/antigravity-mitm/route.js +2 -5
  58. package/app/.next/server/app/api/cli-tools/antigravity-mitm/route.js.nft.json +1 -1
  59. package/app/.next/server/app/api/cli-tools/antigravity-mitm/route_client-reference-manifest.js +1 -1
  60. package/app/.next/server/app/api/cli-tools/claude-settings/route_client-reference-manifest.js +1 -1
  61. package/app/.next/server/app/api/cli-tools/codex-settings/route_client-reference-manifest.js +1 -1
  62. package/app/.next/server/app/api/cli-tools/droid-settings/route_client-reference-manifest.js +1 -1
  63. package/app/.next/server/app/api/cli-tools/openclaw-settings/route_client-reference-manifest.js +1 -1
  64. package/app/.next/server/app/api/cloud/auth/route_client-reference-manifest.js +1 -1
  65. package/app/.next/server/app/api/cloud/credentials/update/route_client-reference-manifest.js +1 -1
  66. package/app/.next/server/app/api/cloud/model/resolve/route_client-reference-manifest.js +1 -1
  67. package/app/.next/server/app/api/cloud/models/alias/route_client-reference-manifest.js +1 -1
  68. package/app/.next/server/app/api/combos/[id]/route_client-reference-manifest.js +1 -1
  69. package/app/.next/server/app/api/combos/route_client-reference-manifest.js +1 -1
  70. package/app/.next/server/app/api/init/route.js +1 -1
  71. package/app/.next/server/app/api/init/route.js.nft.json +1 -1
  72. package/app/.next/server/app/api/init/route_client-reference-manifest.js +1 -1
  73. package/app/.next/server/app/api/keys/[id]/route_client-reference-manifest.js +1 -1
  74. package/app/.next/server/app/api/keys/route_client-reference-manifest.js +1 -1
  75. package/app/.next/server/app/api/models/alias/route_client-reference-manifest.js +1 -1
  76. package/app/.next/server/app/api/models/route_client-reference-manifest.js +1 -1
  77. package/app/.next/server/app/api/oauth/[provider]/[action]/route.js +1 -1
  78. package/app/.next/server/app/api/oauth/[provider]/[action]/route_client-reference-manifest.js +1 -1
  79. package/app/.next/server/app/api/oauth/cursor/auto-import/route.js +7 -1
  80. package/app/.next/server/app/api/oauth/cursor/auto-import/route_client-reference-manifest.js +1 -1
  81. package/app/.next/server/app/api/oauth/cursor/import/route_client-reference-manifest.js +1 -1
  82. package/app/.next/server/app/api/oauth/kiro/auto-import/route_client-reference-manifest.js +1 -1
  83. package/app/.next/server/app/api/oauth/kiro/import/route_client-reference-manifest.js +1 -1
  84. package/app/.next/server/app/api/oauth/kiro/social-authorize/route_client-reference-manifest.js +1 -1
  85. package/app/.next/server/app/api/oauth/kiro/social-exchange/route_client-reference-manifest.js +1 -1
  86. package/app/.next/server/app/api/pricing/route_client-reference-manifest.js +1 -1
  87. package/app/.next/server/app/api/provider-nodes/[id]/route_client-reference-manifest.js +1 -1
  88. package/app/.next/server/app/api/provider-nodes/route_client-reference-manifest.js +1 -1
  89. package/app/.next/server/app/api/provider-nodes/validate/route_client-reference-manifest.js +1 -1
  90. package/app/.next/server/app/api/providers/[id]/models/route_client-reference-manifest.js +1 -1
  91. package/app/.next/server/app/api/providers/[id]/route_client-reference-manifest.js +1 -1
  92. package/app/.next/server/app/api/providers/[id]/test/route_client-reference-manifest.js +1 -1
  93. package/app/.next/server/app/api/providers/client/route_client-reference-manifest.js +1 -1
  94. package/app/.next/server/app/api/providers/route_client-reference-manifest.js +1 -1
  95. package/app/.next/server/app/api/providers/test-batch/route_client-reference-manifest.js +1 -1
  96. package/app/.next/server/app/api/providers/validate/route_client-reference-manifest.js +1 -1
  97. package/app/.next/server/app/api/settings/require-login/route_client-reference-manifest.js +1 -1
  98. package/app/.next/server/app/api/settings/route_client-reference-manifest.js +1 -1
  99. package/app/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  100. package/app/.next/server/app/api/tags/route_client-reference-manifest.js +1 -1
  101. package/app/.next/server/app/api/translator/load/route_client-reference-manifest.js +1 -1
  102. package/app/.next/server/app/api/translator/save/route_client-reference-manifest.js +1 -1
  103. package/app/.next/server/app/api/translator/send/route_client-reference-manifest.js +1 -1
  104. package/app/.next/server/app/api/translator/translate/route_client-reference-manifest.js +1 -1
  105. package/app/.next/server/app/api/tunnel/disable/route.js +1 -1
  106. package/app/.next/server/app/api/tunnel/disable/route_client-reference-manifest.js +1 -1
  107. package/app/.next/server/app/api/tunnel/enable/route.js +1 -1
  108. package/app/.next/server/app/api/tunnel/enable/route_client-reference-manifest.js +1 -1
  109. package/app/.next/server/app/api/tunnel/status/route.js +1 -1
  110. package/app/.next/server/app/api/tunnel/status/route_client-reference-manifest.js +1 -1
  111. package/app/.next/server/app/api/usage/[connectionId]/route_client-reference-manifest.js +1 -1
  112. package/app/.next/server/app/api/usage/chart/route_client-reference-manifest.js +1 -1
  113. package/app/.next/server/app/api/usage/history/route_client-reference-manifest.js +1 -1
  114. package/app/.next/server/app/api/usage/providers/route_client-reference-manifest.js +1 -1
  115. package/app/.next/server/app/api/usage/request-details/route_client-reference-manifest.js +1 -1
  116. package/app/.next/server/app/api/usage/request-logs/route_client-reference-manifest.js +1 -1
  117. package/app/.next/server/app/api/usage/stream/route_client-reference-manifest.js +1 -1
  118. package/app/.next/server/app/api/v1/api/chat/route_client-reference-manifest.js +1 -1
  119. package/app/.next/server/app/api/v1/chat/completions/route_client-reference-manifest.js +1 -1
  120. package/app/.next/server/app/api/v1/embeddings/route_client-reference-manifest.js +1 -1
  121. package/app/.next/server/app/api/v1/messages/count_tokens/route_client-reference-manifest.js +1 -1
  122. package/app/.next/server/app/api/v1/messages/route_client-reference-manifest.js +1 -1
  123. package/app/.next/server/app/api/v1/models/route_client-reference-manifest.js +1 -1
  124. package/app/.next/server/app/api/v1/responses/route_client-reference-manifest.js +1 -1
  125. package/app/.next/server/app/api/v1/route_client-reference-manifest.js +1 -1
  126. package/app/.next/server/app/api/v1beta/models/[...path]/route_client-reference-manifest.js +1 -1
  127. package/app/.next/server/app/api/v1beta/models/route_client-reference-manifest.js +1 -1
  128. package/app/.next/server/app/callback/page.js +2 -2
  129. package/app/.next/server/app/callback/page.js.nft.json +1 -1
  130. package/app/.next/server/app/callback/page_client-reference-manifest.js +1 -1
  131. package/app/.next/server/app/callback.html +1 -1
  132. package/app/.next/server/app/callback.rsc +2 -2
  133. package/app/.next/server/app/callback.segments/_full.segment.rsc +2 -2
  134. package/app/.next/server/app/callback.segments/_head.segment.rsc +1 -1
  135. package/app/.next/server/app/callback.segments/_index.segment.rsc +2 -2
  136. package/app/.next/server/app/callback.segments/_tree.segment.rsc +1 -1
  137. package/app/.next/server/app/callback.segments/callback/__PAGE__.segment.rsc +1 -1
  138. package/app/.next/server/app/callback.segments/callback.segment.rsc +1 -1
  139. package/app/.next/server/app/dashboard/cli-tools.html +1 -1
  140. package/app/.next/server/app/dashboard/cli-tools.rsc +4 -4
  141. package/app/.next/server/app/dashboard/cli-tools.segments/!KGRhc2hib2FyZCk/dashboard/cli-tools/__PAGE__.segment.rsc +2 -2
  142. package/app/.next/server/app/dashboard/cli-tools.segments/!KGRhc2hib2FyZCk/dashboard/cli-tools.segment.rsc +1 -1
  143. package/app/.next/server/app/dashboard/cli-tools.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  144. package/app/.next/server/app/dashboard/cli-tools.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  145. package/app/.next/server/app/dashboard/cli-tools.segments/_full.segment.rsc +4 -4
  146. package/app/.next/server/app/dashboard/cli-tools.segments/_head.segment.rsc +1 -1
  147. package/app/.next/server/app/dashboard/cli-tools.segments/_index.segment.rsc +2 -2
  148. package/app/.next/server/app/dashboard/cli-tools.segments/_tree.segment.rsc +1 -1
  149. package/app/.next/server/app/dashboard/combos.html +1 -1
  150. package/app/.next/server/app/dashboard/combos.rsc +4 -4
  151. package/app/.next/server/app/dashboard/combos.segments/!KGRhc2hib2FyZCk/dashboard/combos/__PAGE__.segment.rsc +2 -2
  152. package/app/.next/server/app/dashboard/combos.segments/!KGRhc2hib2FyZCk/dashboard/combos.segment.rsc +1 -1
  153. package/app/.next/server/app/dashboard/combos.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  154. package/app/.next/server/app/dashboard/combos.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  155. package/app/.next/server/app/dashboard/combos.segments/_full.segment.rsc +4 -4
  156. package/app/.next/server/app/dashboard/combos.segments/_head.segment.rsc +1 -1
  157. package/app/.next/server/app/dashboard/combos.segments/_index.segment.rsc +2 -2
  158. package/app/.next/server/app/dashboard/combos.segments/_tree.segment.rsc +1 -1
  159. package/app/.next/server/app/dashboard/endpoint.html +1 -1
  160. package/app/.next/server/app/dashboard/endpoint.rsc +4 -4
  161. package/app/.next/server/app/dashboard/endpoint.segments/!KGRhc2hib2FyZCk/dashboard/endpoint/__PAGE__.segment.rsc +2 -2
  162. package/app/.next/server/app/dashboard/endpoint.segments/!KGRhc2hib2FyZCk/dashboard/endpoint.segment.rsc +1 -1
  163. package/app/.next/server/app/dashboard/endpoint.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  164. package/app/.next/server/app/dashboard/endpoint.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  165. package/app/.next/server/app/dashboard/endpoint.segments/_full.segment.rsc +4 -4
  166. package/app/.next/server/app/dashboard/endpoint.segments/_head.segment.rsc +1 -1
  167. package/app/.next/server/app/dashboard/endpoint.segments/_index.segment.rsc +2 -2
  168. package/app/.next/server/app/dashboard/endpoint.segments/_tree.segment.rsc +1 -1
  169. package/app/.next/server/app/dashboard/profile.html +1 -1
  170. package/app/.next/server/app/dashboard/profile.rsc +4 -4
  171. package/app/.next/server/app/dashboard/profile.segments/!KGRhc2hib2FyZCk/dashboard/profile/__PAGE__.segment.rsc +2 -2
  172. package/app/.next/server/app/dashboard/profile.segments/!KGRhc2hib2FyZCk/dashboard/profile.segment.rsc +1 -1
  173. package/app/.next/server/app/dashboard/profile.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  174. package/app/.next/server/app/dashboard/profile.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  175. package/app/.next/server/app/dashboard/profile.segments/_full.segment.rsc +4 -4
  176. package/app/.next/server/app/dashboard/profile.segments/_head.segment.rsc +1 -1
  177. package/app/.next/server/app/dashboard/profile.segments/_index.segment.rsc +2 -2
  178. package/app/.next/server/app/dashboard/profile.segments/_tree.segment.rsc +1 -1
  179. package/app/.next/server/app/dashboard/providers/new.html +1 -1
  180. package/app/.next/server/app/dashboard/providers/new.rsc +4 -4
  181. package/app/.next/server/app/dashboard/providers/new.segments/!KGRhc2hib2FyZCk/dashboard/providers/new/__PAGE__.segment.rsc +2 -2
  182. package/app/.next/server/app/dashboard/providers/new.segments/!KGRhc2hib2FyZCk/dashboard/providers/new.segment.rsc +1 -1
  183. package/app/.next/server/app/dashboard/providers/new.segments/!KGRhc2hib2FyZCk/dashboard/providers.segment.rsc +1 -1
  184. package/app/.next/server/app/dashboard/providers/new.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  185. package/app/.next/server/app/dashboard/providers/new.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  186. package/app/.next/server/app/dashboard/providers/new.segments/_full.segment.rsc +4 -4
  187. package/app/.next/server/app/dashboard/providers/new.segments/_head.segment.rsc +1 -1
  188. package/app/.next/server/app/dashboard/providers/new.segments/_index.segment.rsc +2 -2
  189. package/app/.next/server/app/dashboard/providers/new.segments/_tree.segment.rsc +1 -1
  190. package/app/.next/server/app/dashboard/providers.html +1 -1
  191. package/app/.next/server/app/dashboard/providers.rsc +4 -4
  192. package/app/.next/server/app/dashboard/providers.segments/!KGRhc2hib2FyZCk/dashboard/providers/__PAGE__.segment.rsc +2 -2
  193. package/app/.next/server/app/dashboard/providers.segments/!KGRhc2hib2FyZCk/dashboard/providers.segment.rsc +1 -1
  194. package/app/.next/server/app/dashboard/providers.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  195. package/app/.next/server/app/dashboard/providers.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  196. package/app/.next/server/app/dashboard/providers.segments/_full.segment.rsc +4 -4
  197. package/app/.next/server/app/dashboard/providers.segments/_head.segment.rsc +1 -1
  198. package/app/.next/server/app/dashboard/providers.segments/_index.segment.rsc +2 -2
  199. package/app/.next/server/app/dashboard/providers.segments/_tree.segment.rsc +1 -1
  200. package/app/.next/server/app/dashboard/settings/pricing/page.js +1 -1
  201. package/app/.next/server/app/dashboard/settings/pricing/page.js.nft.json +1 -1
  202. package/app/.next/server/app/dashboard/settings/pricing/page_client-reference-manifest.js +1 -1
  203. package/app/.next/server/app/dashboard/settings/pricing.html +1 -1
  204. package/app/.next/server/app/dashboard/settings/pricing.rsc +2 -2
  205. package/app/.next/server/app/dashboard/settings/pricing.segments/_full.segment.rsc +2 -2
  206. package/app/.next/server/app/dashboard/settings/pricing.segments/_head.segment.rsc +1 -1
  207. package/app/.next/server/app/dashboard/settings/pricing.segments/_index.segment.rsc +2 -2
  208. package/app/.next/server/app/dashboard/settings/pricing.segments/_tree.segment.rsc +1 -1
  209. package/app/.next/server/app/dashboard/settings/pricing.segments/dashboard/settings/pricing/__PAGE__.segment.rsc +1 -1
  210. package/app/.next/server/app/dashboard/settings/pricing.segments/dashboard/settings/pricing.segment.rsc +1 -1
  211. package/app/.next/server/app/dashboard/settings/pricing.segments/dashboard/settings.segment.rsc +1 -1
  212. package/app/.next/server/app/dashboard/settings/pricing.segments/dashboard.segment.rsc +1 -1
  213. package/app/.next/server/app/dashboard/translator.html +1 -1
  214. package/app/.next/server/app/dashboard/translator.rsc +4 -4
  215. package/app/.next/server/app/dashboard/translator.segments/!KGRhc2hib2FyZCk/dashboard/translator/__PAGE__.segment.rsc +2 -2
  216. package/app/.next/server/app/dashboard/translator.segments/!KGRhc2hib2FyZCk/dashboard/translator.segment.rsc +1 -1
  217. package/app/.next/server/app/dashboard/translator.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  218. package/app/.next/server/app/dashboard/translator.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  219. package/app/.next/server/app/dashboard/translator.segments/_full.segment.rsc +4 -4
  220. package/app/.next/server/app/dashboard/translator.segments/_head.segment.rsc +1 -1
  221. package/app/.next/server/app/dashboard/translator.segments/_index.segment.rsc +2 -2
  222. package/app/.next/server/app/dashboard/translator.segments/_tree.segment.rsc +1 -1
  223. package/app/.next/server/app/dashboard/usage.html +1 -1
  224. package/app/.next/server/app/dashboard/usage.rsc +4 -4
  225. package/app/.next/server/app/dashboard/usage.segments/!KGRhc2hib2FyZCk/dashboard/usage/__PAGE__.segment.rsc +2 -2
  226. package/app/.next/server/app/dashboard/usage.segments/!KGRhc2hib2FyZCk/dashboard/usage.segment.rsc +1 -1
  227. package/app/.next/server/app/dashboard/usage.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  228. package/app/.next/server/app/dashboard/usage.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  229. package/app/.next/server/app/dashboard/usage.segments/_full.segment.rsc +4 -4
  230. package/app/.next/server/app/dashboard/usage.segments/_head.segment.rsc +1 -1
  231. package/app/.next/server/app/dashboard/usage.segments/_index.segment.rsc +2 -2
  232. package/app/.next/server/app/dashboard/usage.segments/_tree.segment.rsc +1 -1
  233. package/app/.next/server/app/dashboard.html +1 -1
  234. package/app/.next/server/app/dashboard.rsc +4 -4
  235. package/app/.next/server/app/dashboard.segments/!KGRhc2hib2FyZCk/dashboard/__PAGE__.segment.rsc +2 -2
  236. package/app/.next/server/app/dashboard.segments/!KGRhc2hib2FyZCk/dashboard.segment.rsc +1 -1
  237. package/app/.next/server/app/dashboard.segments/!KGRhc2hib2FyZCk.segment.rsc +2 -2
  238. package/app/.next/server/app/dashboard.segments/_full.segment.rsc +4 -4
  239. package/app/.next/server/app/dashboard.segments/_head.segment.rsc +1 -1
  240. package/app/.next/server/app/dashboard.segments/_index.segment.rsc +2 -2
  241. package/app/.next/server/app/dashboard.segments/_tree.segment.rsc +1 -1
  242. package/app/.next/server/app/index.html +1 -1
  243. package/app/.next/server/app/index.rsc +2 -2
  244. package/app/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  245. package/app/.next/server/app/index.segments/_full.segment.rsc +2 -2
  246. package/app/.next/server/app/index.segments/_head.segment.rsc +1 -1
  247. package/app/.next/server/app/index.segments/_index.segment.rsc +2 -2
  248. package/app/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  249. package/app/.next/server/app/landing/page.js +2 -2
  250. package/app/.next/server/app/landing/page.js.nft.json +1 -1
  251. package/app/.next/server/app/landing/page_client-reference-manifest.js +1 -1
  252. package/app/.next/server/app/landing.html +1 -1
  253. package/app/.next/server/app/landing.rsc +2 -2
  254. package/app/.next/server/app/landing.segments/_full.segment.rsc +2 -2
  255. package/app/.next/server/app/landing.segments/_head.segment.rsc +1 -1
  256. package/app/.next/server/app/landing.segments/_index.segment.rsc +2 -2
  257. package/app/.next/server/app/landing.segments/_tree.segment.rsc +1 -1
  258. package/app/.next/server/app/landing.segments/landing/__PAGE__.segment.rsc +1 -1
  259. package/app/.next/server/app/landing.segments/landing.segment.rsc +1 -1
  260. package/app/.next/server/app/login/page.js +2 -2
  261. package/app/.next/server/app/login/page.js.nft.json +1 -1
  262. package/app/.next/server/app/login/page_client-reference-manifest.js +1 -1
  263. package/app/.next/server/app/login.html +1 -1
  264. package/app/.next/server/app/login.rsc +3 -3
  265. package/app/.next/server/app/login.segments/_full.segment.rsc +3 -3
  266. package/app/.next/server/app/login.segments/_head.segment.rsc +1 -1
  267. package/app/.next/server/app/login.segments/_index.segment.rsc +2 -2
  268. package/app/.next/server/app/login.segments/_tree.segment.rsc +1 -1
  269. package/app/.next/server/app/login.segments/login/__PAGE__.segment.rsc +2 -2
  270. package/app/.next/server/app/login.segments/login.segment.rsc +1 -1
  271. package/app/.next/server/app/page.js +2 -2
  272. package/app/.next/server/app/page.js.nft.json +1 -1
  273. package/app/.next/server/app/page_client-reference-manifest.js +1 -1
  274. package/app/.next/server/app-paths-manifest.json +34 -34
  275. package/app/.next/server/chunks/2044.js +2 -0
  276. package/app/.next/server/chunks/6186.js +4 -0
  277. package/app/.next/server/chunks/8694.js +1 -0
  278. package/app/.next/server/chunks/9201.js +1 -0
  279. package/app/.next/server/chunks/9489.js +1 -1
  280. package/app/.next/server/pages/404.html +1 -1
  281. package/app/.next/server/pages/500.html +2 -2
  282. package/app/.next/server/server-reference-manifest.js +1 -1
  283. package/app/.next/server/server-reference-manifest.json +1 -1
  284. package/app/.next/static/chunks/{8729-4759fbfb65755c55.js → 8729-a400665e59674a47.js} +1 -1
  285. package/app/package.json +1 -1
  286. package/app/src/mitm/manager.js +321 -99
  287. package/app/src/mitm/server.js +14 -10
  288. package/package.json +1 -1
  289. package/app/.next/server/chunks/8954.js +0 -1
  290. /package/app/.next/static/{cOujbAAf11k5ILzHxROwz → BrLPf6yJlpsLD6WkIP0hO}/_buildManifest.js +0 -0
  291. /package/app/.next/static/{cOujbAAf11k5ILzHxROwz → BrLPf6yJlpsLD6WkIP0hO}/_ssgManifest.js +0 -0
@@ -2,22 +2,52 @@ const { spawn, exec } = require("child_process");
2
2
  const path = require("path");
3
3
  const fs = require("fs");
4
4
  const os = require("os");
5
+ const net = require("net");
6
+ const crypto = require("crypto");
5
7
  const { addDNSEntry, removeDNSEntry, checkDNSEntry } = require("./dns/dnsConfig");
6
8
 
7
9
  const IS_WIN = process.platform === "win32";
8
10
  const { generateCert } = require("./cert/generate");
9
11
  const { installCert } = require("./cert/install");
10
12
 
11
- // Store server process
13
+ const MITM_PORT = 443;
14
+ const PID_FILE = path.join(os.homedir(), ".9router", "mitm", ".mitm.pid");
15
+
16
+ // Resolve server.js path robustly:
17
+ // __dirname is unreliable inside Next.js bundles, so we use DATA_DIR env or
18
+ // fall back to locating the file relative to the app's source root.
19
+ function resolveServerPath() {
20
+ // 1. Explicit override via env (useful for packaged/standalone builds)
21
+ if (process.env.MITM_SERVER_PATH) return process.env.MITM_SERVER_PATH;
22
+
23
+ // 2. Try sibling of this file (works in dev where __dirname is real)
24
+ const sibling = path.join(__dirname, "server.js");
25
+ if (fs.existsSync(sibling)) return sibling;
26
+
27
+ // 3. Fallback: resolve from process.cwd() → src/mitm/server.js
28
+ const fromCwd = path.join(process.cwd(), "src", "mitm", "server.js");
29
+ if (fs.existsSync(fromCwd)) return fromCwd;
30
+
31
+ // 4. Standalone build: app root is parent of .next
32
+ const fromNext = path.join(process.cwd(), "..", "src", "mitm", "server.js");
33
+ if (fs.existsSync(fromNext)) return fromNext;
34
+
35
+ return fromCwd; // best guess
36
+ }
37
+
38
+ const SERVER_PATH = resolveServerPath();
39
+
40
+ const ENCRYPT_ALGO = "aes-256-gcm";
41
+ const ENCRYPT_SALT = "9router-mitm-pwd";
42
+
43
+ // Store server process in-memory
12
44
  let serverProcess = null;
13
45
  let serverPid = null;
14
- // Persist across Next.js hot reloads
46
+
47
+ // Persist sudo password across Next.js hot reloads (in-memory only)
15
48
  function getCachedPassword() { return globalThis.__mitmSudoPassword || null; }
16
49
  function setCachedPassword(pwd) { globalThis.__mitmSudoPassword = pwd; }
17
50
 
18
- // server.js is in same directory as this file
19
- const PID_FILE = path.join(os.homedir(), ".9router", "mitm", ".mitm.pid");
20
-
21
51
  // Check if a PID is alive
22
52
  function isProcessAlive(pid) {
23
53
  try {
@@ -34,7 +64,193 @@ function killProcess(pid, force = false) {
34
64
  const flag = force ? "/F " : "";
35
65
  exec(`taskkill ${flag}/PID ${pid}`, () => {});
36
66
  } else {
37
- process.kill(pid, force ? "SIGKILL" : "SIGTERM");
67
+ // Use pkill to kill entire process group (catches sudo + child node process)
68
+ const sig = force ? "SIGKILL" : "SIGTERM";
69
+ exec(`pkill -${sig} -P ${pid} 2>/dev/null; kill -${sig} ${pid} 2>/dev/null`, () => {});
70
+ }
71
+ }
72
+
73
+ /** Derive a 32-byte encryption key from machineId */
74
+ function deriveKey() {
75
+ try {
76
+ const { machineIdSync } = require("node-machine-id");
77
+ const raw = machineIdSync();
78
+ return crypto.createHash("sha256").update(raw + ENCRYPT_SALT).digest();
79
+ } catch {
80
+ // Fallback: fixed key derived from salt (less secure but functional)
81
+ return crypto.createHash("sha256").update(ENCRYPT_SALT).digest();
82
+ }
83
+ }
84
+
85
+ /** Encrypt sudo password with AES-256-GCM */
86
+ function encryptPassword(plaintext) {
87
+ const key = deriveKey();
88
+ const iv = crypto.randomBytes(12);
89
+ const cipher = crypto.createCipheriv(ENCRYPT_ALGO, key, iv);
90
+ const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
91
+ const tag = cipher.getAuthTag();
92
+ // Store as hex: iv:tag:ciphertext
93
+ return `${iv.toString("hex")}:${tag.toString("hex")}:${encrypted.toString("hex")}`;
94
+ }
95
+
96
+ /** Decrypt sudo password */
97
+ function decryptPassword(stored) {
98
+ try {
99
+ const [ivHex, tagHex, dataHex] = stored.split(":");
100
+ if (!ivHex || !tagHex || !dataHex) return null;
101
+ const key = deriveKey();
102
+ const decipher = crypto.createDecipheriv(ENCRYPT_ALGO, key, Buffer.from(ivHex, "hex"));
103
+ decipher.setAuthTag(Buffer.from(tagHex, "hex"));
104
+ return decipher.update(Buffer.from(dataHex, "hex")) + decipher.final("utf8");
105
+ } catch {
106
+ return null;
107
+ }
108
+ }
109
+
110
+ // DB hooks — injected from ESM context (initializeApp / route handlers)
111
+ // to avoid webpack bundling issues with dynamic imports in CJS modules.
112
+ let _getSettings = null;
113
+ let _updateSettings = null;
114
+
115
+ /** Called once from ESM context to inject DB access functions */
116
+ function initDbHooks(getSettingsFn, updateSettingsFn) {
117
+ _getSettings = getSettingsFn;
118
+ _updateSettings = updateSettingsFn;
119
+ }
120
+
121
+ /** Save encrypted sudo password + mitmEnabled to db */
122
+ async function saveMitmSettings(enabled, password) {
123
+ if (!_updateSettings) {
124
+ console.log("[MITM] DB hooks not initialized, skipping save");
125
+ return;
126
+ }
127
+ try {
128
+ const updates = { mitmEnabled: enabled };
129
+ if (password) updates.mitmSudoEncrypted = encryptPassword(password);
130
+ await _updateSettings(updates);
131
+ } catch (e) {
132
+ console.log("[MITM] Failed to save settings:", e.message);
133
+ }
134
+ }
135
+
136
+ /** Load and decrypt sudo password from db */
137
+ async function loadEncryptedPassword() {
138
+ if (!_getSettings) return null;
139
+ try {
140
+ const settings = await _getSettings();
141
+ if (!settings.mitmSudoEncrypted) return null;
142
+ return decryptPassword(settings.mitmSudoEncrypted);
143
+ } catch {
144
+ return null;
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Check if port 443 is available
150
+ * Returns: "free" | "in-use" | "no-permission"
151
+ */
152
+ function checkPort443Free() {
153
+ return new Promise((resolve) => {
154
+ const tester = net.createServer();
155
+ tester.once("error", (err) => {
156
+ if (err.code === "EADDRINUSE") resolve("in-use");
157
+ else resolve("no-permission"); // EACCES or other → port free but needs sudo
158
+ });
159
+ tester.once("listening", () => { tester.close(() => resolve("free")); });
160
+ tester.listen(MITM_PORT, "127.0.0.1");
161
+ });
162
+ }
163
+
164
+ /**
165
+ * Get PID and process name currently holding port 443
166
+ * Returns { pid, name } or null if port is free / cannot determine
167
+ */
168
+ function getPort443Owner() {
169
+ return new Promise((resolve) => {
170
+ const cmd = IS_WIN
171
+ ? `netstat -ano | findstr ":443 "`
172
+ // Only match TCP processes actually LISTEN-ing on port 443 (not outbound UDP/QUIC)
173
+ : `lsof -i TCP:${MITM_PORT} -n -P -sTCP:LISTEN`;
174
+
175
+ exec(cmd, (err, stdout) => {
176
+ if (err || !stdout.trim()) return resolve(null);
177
+
178
+ let pid = null;
179
+
180
+ if (IS_WIN) {
181
+ // netstat line: " TCP 0.0.0.0:443 0.0.0.0:0 LISTENING 1234"
182
+ for (const line of stdout.split("\n")) {
183
+ const match = line.match(/LISTENING\s+(\d+)/i);
184
+ if (match) { pid = parseInt(match[1], 10); break; }
185
+ }
186
+ } else {
187
+ // lsof line: "node 1234 user ..."
188
+ for (const line of stdout.split("\n").slice(1)) {
189
+ const parts = line.trim().split(/\s+/);
190
+ if (parts.length >= 2) { pid = parseInt(parts[1], 10); break; }
191
+ }
192
+ }
193
+
194
+ if (!pid || isNaN(pid)) return resolve(null);
195
+
196
+ // Get process name by PID
197
+ const nameCmd = IS_WIN
198
+ ? `tasklist /FI "PID eq ${pid}" /FO CSV /NH`
199
+ : `ps -p ${pid} -o comm=`;
200
+
201
+ exec(nameCmd, (e2, out2) => {
202
+ let name = "unknown";
203
+ if (!e2 && out2.trim()) {
204
+ if (IS_WIN) {
205
+ // CSV: "node.exe","1234",...
206
+ const m = out2.match(/"([^"]+)"/);
207
+ if (m) name = m[1];
208
+ } else {
209
+ name = out2.trim();
210
+ }
211
+ }
212
+ resolve({ pid, name });
213
+ });
214
+ });
215
+ });
216
+ }
217
+
218
+ /**
219
+ * Kill any leftover MITM server process (from previous failed start)
220
+ * Uses sudo to kill the node process that was spawned with sudo
221
+ */
222
+ async function killLeftoverMitm(sudoPassword) {
223
+ // Kill in-memory process if still alive
224
+ if (serverProcess && !serverProcess.killed) {
225
+ try { serverProcess.kill("SIGKILL"); } catch { /* ignore */ }
226
+ serverProcess = null;
227
+ serverPid = null;
228
+ }
229
+
230
+ // Kill from PID file
231
+ try {
232
+ if (fs.existsSync(PID_FILE)) {
233
+ const savedPid = parseInt(fs.readFileSync(PID_FILE, "utf-8").trim(), 10);
234
+ if (savedPid && isProcessAlive(savedPid)) {
235
+ killProcess(savedPid, true);
236
+ await new Promise(r => setTimeout(r, 500));
237
+ }
238
+ fs.unlinkSync(PID_FILE);
239
+ }
240
+ } catch { /* ignore */ }
241
+
242
+ // Also kill any node process running server.js via sudo (belt-and-suspenders)
243
+ if (!IS_WIN && SERVER_PATH) {
244
+ try {
245
+ const escaped = SERVER_PATH.replace(/'/g, "'\\''");
246
+ if (sudoPassword) {
247
+ const { execWithPassword } = require("./dns/dnsConfig");
248
+ await execWithPassword(`pkill -SIGKILL -f "${escaped}" 2>/dev/null || true`, sudoPassword).catch(() => {});
249
+ } else {
250
+ exec(`pkill -SIGKILL -f "${escaped}" 2>/dev/null || true`, () => {});
251
+ }
252
+ await new Promise(r => setTimeout(r, 500));
253
+ } catch { /* ignore */ }
38
254
  }
39
255
  }
40
256
 
@@ -42,7 +258,6 @@ function killProcess(pid, force = false) {
42
258
  * Get MITM status
43
259
  */
44
260
  async function getMitmStatus() {
45
- // Check in-memory process first, then fallback to PID file
46
261
  let running = serverProcess !== null && !serverProcess.killed;
47
262
  let pid = serverPid;
48
263
 
@@ -54,7 +269,6 @@ async function getMitmStatus() {
54
269
  running = true;
55
270
  pid = savedPid;
56
271
  } else {
57
- // Stale PID file, clean up
58
272
  fs.unlinkSync(PID_FILE);
59
273
  }
60
274
  }
@@ -63,10 +277,7 @@ async function getMitmStatus() {
63
277
  }
64
278
  }
65
279
 
66
- // Check DNS configuration (cross-platform via dnsConfig)
67
280
  const dnsConfigured = checkDNSEntry();
68
-
69
- // Check cert
70
281
  const certDir = path.join(os.homedir(), ".9router", "mitm");
71
282
  const certExists = fs.existsSync(path.join(certDir, "server.crt"));
72
283
 
@@ -79,109 +290,133 @@ async function getMitmStatus() {
79
290
  * @param {string} sudoPassword - Sudo password for DNS/cert operations
80
291
  */
81
292
  async function startMitm(apiKey, sudoPassword) {
82
- // Check if already running
293
+ // Check orphan process from PID file before spawning
294
+ if (!serverProcess || serverProcess.killed) {
295
+ try {
296
+ if (fs.existsSync(PID_FILE)) {
297
+ const savedPid = parseInt(fs.readFileSync(PID_FILE, "utf-8").trim(), 10);
298
+ if (savedPid && isProcessAlive(savedPid)) {
299
+ // Orphan MITM process still alive — reuse it
300
+ serverPid = savedPid;
301
+ console.log(`[MITM] Reusing existing process PID ${savedPid}`);
302
+ await saveMitmSettings(true, sudoPassword);
303
+ if (sudoPassword) setCachedPassword(sudoPassword);
304
+ return { running: true, pid: savedPid };
305
+ } else {
306
+ fs.unlinkSync(PID_FILE);
307
+ }
308
+ }
309
+ } catch {
310
+ // Ignore stale PID file errors
311
+ }
312
+ }
313
+
83
314
  if (serverProcess && !serverProcess.killed) {
84
315
  throw new Error("MITM proxy is already running");
85
316
  }
86
-
317
+
318
+ // Kill any leftover MITM server from a previous failed start attempt
319
+ await killLeftoverMitm(sudoPassword);
320
+
321
+ // Check port 443 availability BEFORE modifying system
322
+ const portStatus = await checkPort443Free();
323
+ if (portStatus === "in-use") {
324
+ const owner = await getPort443Owner();
325
+ let ownerDesc = "another process";
326
+ if (owner) {
327
+ const shortName = owner.name.includes("/")
328
+ ? owner.name.split("/").filter(Boolean).pop()
329
+ : owner.name;
330
+ ownerDesc = `"${shortName}" (PID ${owner.pid})`;
331
+ }
332
+ throw new Error(
333
+ `Port 443 is already in use by ${ownerDesc}. Stop that process first, then retry.`
334
+ );
335
+ }
336
+
87
337
  // 1. Generate SSL certificate if not exists
88
338
  const certPath = path.join(os.homedir(), ".9router", "mitm", "server.crt");
89
339
  if (!fs.existsSync(certPath)) {
90
340
  console.log("Generating SSL certificate...");
91
341
  await generateCert();
92
342
  }
93
-
343
+
94
344
  // 2. Install certificate to system keychain
95
- await installCert(sudoPassword, certPath);
96
-
345
+ // Skip if db flag says installed AND cert file still exists (same cert in keychain)
346
+ const settings = _getSettings ? await _getSettings().catch(() => ({})) : {};
347
+ const certAlreadyInstalled = settings.mitmCertInstalled && fs.existsSync(certPath);
348
+ if (!certAlreadyInstalled) {
349
+ await installCert(sudoPassword, certPath);
350
+ if (_updateSettings) await _updateSettings({ mitmCertInstalled: true }).catch(() => {});
351
+ }
352
+
97
353
  // 3. Add DNS entry
98
354
  console.log("Adding DNS entry...");
99
355
  await addDNSEntry(sudoPassword);
100
-
101
- // 4. Start MITM server (port 443 requires elevated privileges)
356
+
357
+ // 4. Spawn MITM server with sudo (port 443 requires root on macOS/Linux)
102
358
  console.log("Starting MITM server...");
103
- const serverPath = path.join(process.cwd(), "src/mitm/server.js");
104
359
 
105
360
  if (IS_WIN) {
106
- // Windows: spawn via powershell elevated to bind port 443
107
361
  const nodePath = process.execPath;
108
- const envArgs = `$env:ROUTER_API_KEY='${apiKey}'; $env:NODE_ENV='production'; & '${nodePath}' '${serverPath}'`;
362
+ const envArgs = `$env:ROUTER_API_KEY='${apiKey}'; $env:NODE_ENV='production'; & '${nodePath}' '${SERVER_PATH}'`;
109
363
  serverProcess = spawn("powershell", [
110
364
  "-Command",
111
365
  `Start-Process powershell -ArgumentList '-NoProfile','-Command','${envArgs.replace(/'/g, "''")}' -Verb RunAs -PassThru`
112
- ], {
113
- detached: false,
114
- stdio: ["ignore", "pipe", "pipe"]
115
- });
366
+ ], { detached: false, stdio: ["ignore", "pipe", "pipe"] });
116
367
  } else {
117
- serverProcess = spawn("node", [serverPath], {
118
- env: {
119
- ...process.env,
120
- ROUTER_API_KEY: apiKey,
121
- NODE_ENV: "production"
122
- },
123
- detached: false,
124
- stdio: ["ignore", "pipe", "pipe"]
125
- });
368
+ // sudo -S: read password from stdin, -E: preserve env vars
369
+ // Pass ROUTER_API_KEY inline via env=... wrapper to avoid sudo stripping env
370
+ const inlineCmd = `ROUTER_API_KEY='${apiKey}' NODE_ENV='production' '${process.execPath}' '${SERVER_PATH}'`;
371
+ serverProcess = spawn(
372
+ "sudo", ["-S", "-E", "sh", "-c", inlineCmd],
373
+ { detached: false, stdio: ["pipe", "pipe", "pipe"] }
374
+ );
375
+ // Write password then close stdin so sudo proceeds
376
+ serverProcess.stdin.write(`${sudoPassword}\n`);
377
+ serverProcess.stdin.end();
126
378
  }
127
-
379
+
128
380
  serverPid = serverProcess.pid;
129
-
130
- // Save PID to file
131
381
  fs.writeFileSync(PID_FILE, String(serverPid));
132
-
133
- // Log server output
382
+
383
+ let startError = null;
134
384
  serverProcess.stdout.on("data", (data) => {
135
385
  console.log(`[MITM Server] ${data.toString().trim()}`);
136
386
  });
137
-
138
387
  serverProcess.stderr.on("data", (data) => {
139
- console.error(`[MITM Server Error] ${data.toString().trim()}`);
388
+ const msg = data.toString().trim();
389
+ // Capture meaningful errors (ignore sudo password prompt noise)
390
+ if (msg && !msg.includes("Password:") && !msg.includes("password for")) {
391
+ console.error(`[MITM Server Error] ${msg}`);
392
+ startError = msg;
393
+ }
140
394
  });
141
-
142
395
  serverProcess.on("exit", (code) => {
143
396
  console.log(`MITM server exited with code ${code}`);
144
397
  serverProcess = null;
145
398
  serverPid = null;
146
-
147
- // Remove PID file
148
- try {
149
- fs.unlinkSync(PID_FILE);
150
- } catch (error) {
151
- // Ignore
152
- }
399
+ try { fs.unlinkSync(PID_FILE); } catch { /* ignore */ }
153
400
  });
154
-
155
- // Wait and verify server actually started
401
+
402
+ // Wait up to 8s sudo + Node startup takes longer than plain spawn
156
403
  const started = await new Promise((resolve) => {
157
404
  let resolved = false;
158
- const timeout = setTimeout(() => {
159
- if (!resolved) { resolved = true; resolve(true); }
160
- }, 2000);
161
-
162
- serverProcess.on("exit", (code) => {
163
- clearTimeout(timeout);
164
- if (!resolved) { resolved = true; resolve(false); }
165
- });
166
-
167
- // Check stderr for error messages
168
- serverProcess.stderr.on("data", (data) => {
169
- const msg = data.toString().trim();
170
- if (msg.includes("Port") && msg.includes("already in use")) {
171
- clearTimeout(timeout);
172
- if (!resolved) { resolved = true; resolve(false); }
173
- }
174
- });
405
+ const done = (val) => { if (!resolved) { resolved = true; resolve(val); } };
406
+ const timeout = setTimeout(() => done(true), 8000);
407
+ serverProcess.once("exit", () => { clearTimeout(timeout); done(false); });
175
408
  });
176
409
 
177
410
  if (!started) {
178
- throw new Error("MITM server failed to start (port 443 may be in use)");
411
+ try { await removeDNSEntry(sudoPassword); } catch { /* best effort */ }
412
+ const reason = startError || "Check sudo password or port 443 access.";
413
+ throw new Error(`MITM server failed to start. ${reason}`);
179
414
  }
180
415
 
181
- return {
182
- running: true,
183
- pid: serverPid
184
- };
416
+ await saveMitmSettings(true, sudoPassword);
417
+ if (sudoPassword) setCachedPassword(sudoPassword);
418
+
419
+ return { running: true, pid: serverPid };
185
420
  }
186
421
 
187
422
  /**
@@ -189,19 +424,15 @@ async function startMitm(apiKey, sudoPassword) {
189
424
  * @param {string} sudoPassword - Sudo password for DNS cleanup
190
425
  */
191
426
  async function stopMitm(sudoPassword) {
192
- // 1. Kill server process (in-memory or from PID file)
193
427
  const proc = serverProcess;
194
428
  if (proc && !proc.killed) {
195
429
  console.log("Stopping MITM server...");
196
430
  killProcess(proc.pid, false);
197
431
  await new Promise(resolve => setTimeout(resolve, 1000));
198
- if (isProcessAlive(proc.pid)) {
199
- killProcess(proc.pid, true);
200
- }
432
+ if (isProcessAlive(proc.pid)) killProcess(proc.pid, true);
201
433
  serverProcess = null;
202
434
  serverPid = null;
203
435
  } else {
204
- // Fallback: kill by PID file
205
436
  try {
206
437
  if (fs.existsSync(PID_FILE)) {
207
438
  const savedPid = parseInt(fs.readFileSync(PID_FILE, "utf-8").trim(), 10);
@@ -209,33 +440,22 @@ async function stopMitm(sudoPassword) {
209
440
  console.log(`Killing MITM server (PID: ${savedPid})...`);
210
441
  killProcess(savedPid, false);
211
442
  await new Promise(resolve => setTimeout(resolve, 1000));
212
- if (isProcessAlive(savedPid)) {
213
- killProcess(savedPid, true);
214
- }
443
+ if (isProcessAlive(savedPid)) killProcess(savedPid, true);
215
444
  }
216
445
  }
217
- } catch {
218
- // Ignore
219
- }
446
+ } catch { /* ignore */ }
220
447
  serverProcess = null;
221
448
  serverPid = null;
222
449
  }
223
-
224
- // 2. Remove DNS entry
450
+
225
451
  console.log("Removing DNS entry...");
226
452
  await removeDNSEntry(sudoPassword);
227
-
228
- // 3. Remove PID file
229
- try {
230
- fs.unlinkSync(PID_FILE);
231
- } catch (error) {
232
- // Ignore
233
- }
234
-
235
- return {
236
- running: false,
237
- pid: null
238
- };
453
+
454
+ try { fs.unlinkSync(PID_FILE); } catch { /* ignore */ }
455
+
456
+ await saveMitmSettings(false, null);
457
+
458
+ return { running: false, pid: null };
239
459
  }
240
460
 
241
461
  module.exports = {
@@ -243,5 +463,7 @@ module.exports = {
243
463
  startMitm,
244
464
  stopMitm,
245
465
  getCachedPassword,
246
- setCachedPassword
466
+ setCachedPassword,
467
+ loadEncryptedPassword,
468
+ initDbHooks,
247
469
  };
@@ -23,10 +23,16 @@ if (!API_KEY) {
23
23
 
24
24
  // Load SSL certificates
25
25
  const certDir = path.join(os.homedir(), ".9router", "mitm");
26
- const sslOptions = {
27
- key: fs.readFileSync(path.join(certDir, "server.key")),
28
- cert: fs.readFileSync(path.join(certDir, "server.crt"))
29
- };
26
+ let sslOptions;
27
+ try {
28
+ sslOptions = {
29
+ key: fs.readFileSync(path.join(certDir, "server.key")),
30
+ cert: fs.readFileSync(path.join(certDir, "server.crt"))
31
+ };
32
+ } catch (e) {
33
+ console.error(`❌ SSL cert not found in ${certDir}: ${e.message}`);
34
+ process.exit(1);
35
+ }
30
36
 
31
37
  // Chat endpoints that should be intercepted
32
38
  const CHAT_URL_PATTERNS = [":generateContent", ":streamGenerateContent"];
@@ -146,12 +152,10 @@ async function intercept(req, res, bodyBuffer, mappedModel) {
146
152
  throw new Error(`9Router ${response.status}: ${errText}`);
147
153
  }
148
154
 
149
- res.writeHead(200, {
150
- "Content-Type": "text/event-stream",
151
- "Cache-Control": "no-cache",
152
- "Connection": "keep-alive",
153
- "X-Accel-Buffering": "no"
154
- });
155
+ const ct = response.headers.get("content-type") || "application/json";
156
+ const resHeaders = { "Content-Type": ct, "Cache-Control": "no-cache", "Connection": "keep-alive" };
157
+ if (ct.includes("text/event-stream")) resHeaders["X-Accel-Buffering"] = "no";
158
+ res.writeHead(200, resHeaders);
155
159
 
156
160
  const reader = response.body.getReader();
157
161
  const decoder = new TextDecoder();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "9router",
3
- "version": "0.2.93",
3
+ "version": "0.2.94",
4
4
  "description": "9Router CLI - Start and manage 9Router server",
5
5
  "bin": {
6
6
  "9router": "./cli.js"
@@ -1 +0,0 @@
1
- exports.id=8954,exports.ids=[8954],exports.modules={3179:(a,b,c)=>{"use strict";c.d(b,{Al:()=>t,F0:()=>v,nN:()=>x,ss:()=>w});var d=c(29021),e=c.n(d),f=c(33873),g=c.n(f),h=c(55591),i=c.n(h),j=c(21820),k=c.n(j),l=c(79646),m=c(85567);let n=g().join(k().homedir(),".9router","bin"),o="cloudflared",p="win32"===k().platform(),q=p?`${o}.exe`:o,r=g().join(n,q),s={darwin:{x64:"cloudflared-darwin-amd64.tgz",arm64:"cloudflared-darwin-amd64.tgz"},win32:{x64:"cloudflared-windows-amd64.exe"},linux:{x64:"cloudflared-linux-amd64",arm64:"cloudflared-linux-arm64"}};async function t(){if(e().existsSync(n)||e().mkdirSync(n,{recursive:!0}),e().existsSync(r))return p||e().chmodSync(r,"755"),r;let a=function(){let a=k().platform(),b=k().arch(),c=s[a];if(!c)throw Error(`Unsupported platform: ${a}`);let d=c[b];if(!d)throw Error(`Unsupported architecture: ${b} for platform ${a}`);return`https://github.com/cloudflare/cloudflared/releases/latest/download/${d}`}(),b=a.endsWith(".tgz"),c=b?g().join(n,"cloudflared.tgz"):r;return await function a(b,c){return new Promise((d,f)=>{let g=e().createWriteStream(c);i().get(b,b=>{if([301,302].includes(b.statusCode)){g.close(),e().unlinkSync(c),a(b.headers.location,c).then(d).catch(f);return}if(200!==b.statusCode){g.close(),e().unlinkSync(c),f(Error(`Download failed with status ${b.statusCode}`));return}b.pipe(g),g.on("finish",()=>{g.close(()=>d(c))}),g.on("error",a=>{g.close(),e().unlinkSync(c),f(a)})}).on("error",a=>{g.close(),e().existsSync(c)&&e().unlinkSync(c),f(a)})})}(a,c),b&&((0,l.execSync)(`tar -xzf "${c}" -C "${n}"`,{stdio:"pipe"}),e().unlinkSync(c)),p||e().chmodSync(r,"755"),r}let u=null;async function v(a){let b=await t(),c=(0,l.spawn)(b,["tunnel","run","--dns-resolver-addrs","1.1.1.1:53","--token",a],{detached:!1,stdio:["ignore","pipe","pipe"]});return u=c,(0,m.xS)(c.pid),new Promise((a,b)=>{let d=0,e=setTimeout(()=>{a(c)},9e4),f=b=>{b.toString().includes("Registered tunnel connection")&&++d>=4&&(clearTimeout(e),a(c))};c.stdout.on("data",f),c.stderr.on("data",f),c.on("error",a=>{clearTimeout(e),b(a)}),c.on("exit",a=>{clearTimeout(e),0===d&&b(Error(`cloudflared exited with code ${a}`))})})}function w(){if(u){try{u.kill()}catch(a){}u=null}let a=(0,m.Cr)();if(a){try{process.kill(a)}catch(a){}(0,m.r4)()}try{(0,l.execSync)("pkill -f cloudflared 2>/dev/null || true",{stdio:"ignore"})}catch(a){}}function x(){let a=(0,m.Cr)();if(!a)return!1;try{return process.kill(a,0),!0}catch(a){return!1}}},11020:(a,b,c)=>{Promise.resolve().then(c.t.bind(c,95547,23)),Promise.resolve().then(c.t.bind(c,15098,23)),Promise.resolve().then(c.t.bind(c,47644,23)),Promise.resolve().then(c.t.bind(c,33859,23)),Promise.resolve().then(c.t.bind(c,98099,23)),Promise.resolve().then(c.t.bind(c,16237,23)),Promise.resolve().then(c.t.bind(c,98562,23)),Promise.resolve().then(c.t.bind(c,36675,23))},12020:(a,b,c)=>{"use strict";c.d(b,{Q2:()=>d.Q2,fg:()=>d.fg,vQ:()=>e,MA:()=>d.MA,zN:()=>d.zN,B2:()=>f});var d=c(96919);c(91431);let e={name:"Endpoint Proxy",description:"AI Infrastructure Management",version:"0.2.93"},f={storageKey:"theme",defaultTheme:"system"}},13033:(a,b,c)=>{"use strict";c.d(b,{A:()=>g});var d=c(31417),e=c(300),f=c(12020);let g=(0,d.v)((0,e.Zr)((a,b)=>({theme:f.B2.defaultTheme,setTheme:b=>{a({theme:b})},toggleTheme:()=>{let c="dark"===b().theme?"light":"dark";a({theme:c})},initTheme:()=>{b().theme}}),{name:f.B2.storageKey}))},19201:(a,b,c)=>{"use strict";var d=c(89718),e=c(53855),f=c(3179);process.setMaxListeners(20);let g=!1;async function h(){try{if(await (0,d.bI)(),(await (0,d.getSettings)()).tunnelEnabled&&!(0,f.nN)()){console.log("[InitApp] Tunnel was enabled, auto-reconnecting...");try{await (0,e.cb)(),console.log("[InitApp] Tunnel reconnected")}catch(a){console.log("[InitApp] Tunnel reconnect failed:",a.message)}}if(!g){let a=()=>{(0,f.ss)(),process.exit()};process.on("SIGINT",a),process.on("SIGTERM",a),g=!0}(0,f.Al)().catch(()=>{})}catch(a){console.error("[InitApp] Error:",a)}}let i=!1;(async function(){if(!i)try{await h(),i=!0}catch(a){console.error("[ServerInit] Error initializing app:",a)}return i})().catch(console.log)},19308:(a,b,c)=>{"use strict";c.d(b,{ThemeProvider:()=>f});var d=c(48249);c(67484);var e=c(13033);function f({children:a}){let{initTheme:b}=(0,e.A)();return(0,d.jsx)(d.Fragment,{children:a})}},50385:(a,b,c)=>{"use strict";c.r(b),c.d(b,{default:()=>i,metadata:()=>h});var d=c(5735),e=c(75329),f=c.n(e);c(61135);var g=c(89886);c(19201);let h={title:"9Router - AI Infrastructure Management",description:"One endpoint for all your AI providers. Manage keys, monitor usage, and scale effortlessly.",icons:{icon:"/favicon.svg"}};function i({children:a}){return(0,d.jsxs)("html",{lang:"en",suppressHydrationWarning:!0,children:[(0,d.jsxs)("head",{children:[(0,d.jsx)("link",{rel:"preconnect",href:"https://fonts.googleapis.com"}),(0,d.jsx)("link",{rel:"preconnect",href:"https://fonts.gstatic.com",crossOrigin:"anonymous"}),(0,d.jsx)("link",{href:"https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&display=swap",rel:"stylesheet"})]}),(0,d.jsx)("body",{className:`${f().variable} font-sans antialiased`,children:(0,d.jsx)(g.ThemeProvider,{children:a})})]})}},53855:(a,b,c)=>{"use strict";c.d(b,{Jv:()=>n,Rg:()=>m,cb:()=>l});var d=c(55511),e=c.n(d),f=c(85567),g=c(3179),h=c(89718);let i=process.env.TUNNEL_WORKER_URL||"https://tunnel.9router.com",j="abcdefghijklmnpqrstuvwxyz23456789";async function k(a,b={}){let c=`${i}${a}`;return(await fetch(c,{...b,headers:{"Content-Type":"application/json",...b.headers}})).json()}async function l(){let a=(0,f.C7)();if(a&&a.tunnelUrl&&(0,g.nN)())return{success:!0,tunnelUrl:a.tunnelUrl,shortId:a.shortId,alreadyRunning:!0};(0,g.ss)();let b=function(){try{let{machineIdSync:a}=c(19713),b=a();return e().createHash("sha256").update(b+"9router-tunnel-salt").digest("hex").substring(0,16)}catch(a){return e().randomUUID().replace(/-/g,"").substring(0,16)}}(),d=a?.shortId||function(){let a="";for(let b=0;b<6;b++)a+=j.charAt(Math.floor(Math.random()*j.length));return a}(),i=a?.apiKey||function(a){let b="abcdefghijklmnopqrstuvwxyz0123456789",c="";for(let a=0;a<6;a++)c+=b.charAt(Math.floor(Math.random()*b.length));let d=e().createHmac("sha256","9router-tunnel-api-key-secret").update(a+c).digest("hex").slice(0,8);return`sk-${a}-${c}-${d}`}(b);await k("/api/session/create",{method:"POST",body:JSON.stringify({apiKey:i,shortId:d})});let l=await k("/api/tunnel/create",{method:"POST",body:JSON.stringify({apiKey:i})});if(l.error)throw Error(l.error);let{token:m,hostname:n}=l;return await (0,g.F0)(m),(0,f.LZ)({shortId:d,apiKey:i,tunnelUrl:n,machineId:b}),await (0,h.Xx)({tunnelEnabled:!0,tunnelUrl:n}),{success:!0,tunnelUrl:n,shortId:d}}async function m(){let a=(0,f.C7)();if((0,g.ss)(),a?.apiKey)try{await k("/api/tunnel/delete",{method:"DELETE",body:JSON.stringify({apiKey:a.apiKey})})}catch(a){}return a&&(0,f.LZ)({shortId:a.shortId,apiKey:a.apiKey,machineId:a.machineId,tunnelUrl:null}),await (0,h.Xx)({tunnelEnabled:!1,tunnelUrl:""}),{success:!0}}async function n(){let a=(0,f.C7)(),b=(0,g.nN)();return{enabled:!0===(await (0,h.getSettings)()).tunnelEnabled&&b,tunnelUrl:a?.tunnelUrl||"",shortId:a?.shortId||"",running:b}}},56680:(a,b,c)=>{Promise.resolve().then(c.t.bind(c,81921,23))},60757:(a,b,c)=>{"use strict";c.d(b,{KC:()=>f,Xg:()=>e,vq:()=>d});let d={cc:[{id:"claude-opus-4-6",name:"Claude Opus 4.6"},{id:"claude-sonnet-4-6",name:"Claude Sonnet 4.6"},{id:"claude-opus-4-5-20251101",name:"Claude 4.5 Opus"},{id:"claude-sonnet-4-5-20250929",name:"Claude 4.5 Sonnet"},{id:"claude-haiku-4-5-20251001",name:"Claude 4.5 Haiku"}],cx:[{id:"gpt-5.3-codex",name:"GPT 5.3 Codex"},{id:"gpt-5.3-codex-xhigh",name:"GPT 5.3 Codex (xHigh)"},{id:"gpt-5.3-codex-high",name:"GPT 5.3 Codex (High)"},{id:"gpt-5.3-codex-low",name:"GPT 5.3 Codex (Low)"},{id:"gpt-5.3-codex-none",name:"GPT 5.3 Codex (None)"},{id:"gpt-5.3-codex-spark",name:"GPT 5.3 Codex Spark"},{id:"gpt-5.1-codex-mini",name:"GPT 5.1 Codex Mini"},{id:"gpt-5.1-codex-mini-high",name:"GPT 5.1 Codex Mini (High)"},{id:"gpt-5.2-codex",name:"GPT 5.2 Codex"},{id:"gpt-5.2",name:"GPT 5.2"},{id:"gpt-5.1-codex-max",name:"GPT 5.1 Codex Max"},{id:"gpt-5.1-codex",name:"GPT 5.1 Codex"},{id:"gpt-5.1",name:"GPT 5.1"},{id:"gpt-5-codex",name:"GPT 5 Codex"},{id:"gpt-5-codex-mini",name:"GPT 5 Codex Mini"}],gc:[{id:"gemini-3-flash-preview",name:"Gemini 3 Flash Preview"},{id:"gemini-3-pro-preview",name:"Gemini 3 Pro Preview"},{id:"gemini-2.5-pro",name:"Gemini 2.5 Pro"},{id:"gemini-2.5-flash",name:"Gemini 2.5 Flash"},{id:"gemini-2.5-flash-lite",name:"Gemini 2.5 Flash Lite"}],qw:[{id:"qwen3-coder-plus",name:"Qwen3 Coder Plus"},{id:"qwen3-coder-flash",name:"Qwen3 Coder Flash"},{id:"vision-model",name:"Qwen3 Vision Model"},{id:"coder-model",name:"Qwen3.5 Coder Model"}],if:[{id:"qwen3-coder-plus",name:"Qwen3 Coder Plus"},{id:"kimi-k2",name:"Kimi K2"},{id:"kimi-k2-thinking",name:"Kimi K2 Thinking"},{id:"kimi-k2.5",name:"Kimi K2.5"},{id:"deepseek-r1",name:"DeepSeek R1"},{id:"deepseek-v3.2-chat",name:"DeepSeek V3.2 Chat"},{id:"minimax-m2.1",name:"MiniMax M2.1"},{id:"minimax-m2.5",name:"MiniMax M2.5"},{id:"glm-4.7",name:"GLM 4.7"},{id:"glm-4.6",name:"GLM 4.6"},{id:"glm-5",name:"GLM 5"}],ag:[{id:"gemini-3.1-pro-high",name:"Gemini 3 Pro High"},{id:"gemini-3.1-pro-low",name:"Gemini 3 Pro Low"},{id:"gemini-3-flash",name:"Gemini 3 Flash"},{id:"claude-sonnet-4-6",name:"Claude Sonnet 4.6"},{id:"claude-opus-4-6-thinking",name:"Claude Opus 4.6 Thinking"},{id:"gpt-oss-120b-medium",name:"GPT OSS 120B Medium"}],gh:[{id:"gpt-3.5-turbo",name:"GPT-3.5 Turbo"},{id:"gpt-4",name:"GPT-4"},{id:"gpt-4o",name:"GPT-4o"},{id:"gpt-4o-mini",name:"GPT-4o mini"},{id:"gpt-4.1",name:"GPT-4.1"},{id:"gpt-5",name:"GPT-5"},{id:"gpt-5-mini",name:"GPT-5 Mini"},{id:"gpt-5-codex",name:"GPT-5 Codex"},{id:"gpt-5.1",name:"GPT-5.1"},{id:"gpt-5.1-codex",name:"GPT-5.1 Codex"},{id:"gpt-5.1-codex-mini",name:"GPT-5.1 Codex Mini"},{id:"gpt-5.1-codex-max",name:"GPT-5.1 Codex Max"},{id:"gpt-5.2",name:"GPT-5.2"},{id:"gpt-5.2-codex",name:"GPT-5.2 Codex"},{id:"gpt-5.3-codex",name:"GPT-5.3 Codex"},{id:"claude-haiku-4.5",name:"Claude Haiku 4.5"},{id:"claude-opus-4.1",name:"Claude Opus 4.1"},{id:"claude-opus-4.5",name:"Claude Opus 4.5"},{id:"claude-sonnet-4",name:"Claude Sonnet 4"},{id:"claude-sonnet-4.5",name:"Claude Sonnet 4.5"},{id:"claude-sonnet-4.6",name:"Claude Sonnet 4.6"},{id:"claude-opus-4.6",name:"Claude Opus 4.6"},{id:"gemini-2.5-pro",name:"Gemini 2.5 Pro"},{id:"gemini-3-flash-preview",name:"Gemini 3 Flash"},{id:"gemini-3-pro-preview",name:"Gemini 3 Pro"},{id:"grok-code-fast-1",name:"Grok Code Fast 1"},{id:"oswe-vscode-prime",name:"Raptor Mini"}],kr:[{id:"claude-sonnet-4.5",name:"Claude Sonnet 4.5"},{id:"claude-haiku-4.5",name:"Claude Haiku 4.5"}],cu:[{id:"default",name:"Auto (Server Picks)"},{id:"claude-4.5-opus-high-thinking",name:"Claude 4.5 Opus High Thinking"},{id:"claude-4.5-opus-high",name:"Claude 4.5 Opus High"},{id:"claude-4.5-sonnet-thinking",name:"Claude 4.5 Sonnet Thinking"},{id:"claude-4.5-sonnet",name:"Claude 4.5 Sonnet"},{id:"claude-4.5-haiku",name:"Claude 4.5 Haiku"},{id:"claude-4.5-opus",name:"Claude 4.5 Opus"},{id:"gpt-5.2-codex",name:"GPT 5.2 Codex"}],kmc:[{id:"kimi-k2.5",name:"Kimi K2.5"},{id:"kimi-k2.5-thinking",name:"Kimi K2.5 Thinking"},{id:"kimi-latest",name:"Kimi Latest"}],kc:[{id:"anthropic/claude-sonnet-4-20250514",name:"Claude Sonnet 4"},{id:"anthropic/claude-opus-4-20250514",name:"Claude Opus 4"},{id:"google/gemini-2.5-pro",name:"Gemini 2.5 Pro"},{id:"google/gemini-2.5-flash",name:"Gemini 2.5 Flash"},{id:"openai/gpt-4.1",name:"GPT-4.1"},{id:"openai/o3",name:"o3"},{id:"deepseek/deepseek-chat",name:"DeepSeek Chat"},{id:"deepseek/deepseek-reasoner",name:"DeepSeek Reasoner"}],cl:[{id:"anthropic/claude-sonnet-4-20250514",name:"Claude Sonnet 4"},{id:"anthropic/claude-opus-4-20250514",name:"Claude Opus 4"},{id:"google/gemini-2.5-pro",name:"Gemini 2.5 Pro"},{id:"google/gemini-2.5-flash",name:"Gemini 2.5 Flash"},{id:"openai/gpt-4.1",name:"GPT-4.1"},{id:"openai/o3",name:"o3"},{id:"deepseek/deepseek-chat",name:"DeepSeek Chat"}],openai:[{id:"gpt-4o",name:"GPT-4o"},{id:"gpt-4o-mini",name:"GPT-4o Mini"},{id:"gpt-4-turbo",name:"GPT-4 Turbo"},{id:"o1",name:"O1"},{id:"o1-mini",name:"O1 Mini"}],anthropic:[{id:"claude-sonnet-4-20250514",name:"Claude Sonnet 4"},{id:"claude-opus-4-20250514",name:"Claude Opus 4"},{id:"claude-3-5-sonnet-20241022",name:"Claude 3.5 Sonnet"}],gemini:[{id:"gemini-3-pro-preview",name:"Gemini 3 Pro Preview"},{id:"gemini-2.5-pro",name:"Gemini 2.5 Pro"},{id:"gemini-2.5-flash",name:"Gemini 2.5 Flash"},{id:"gemini-2.5-flash-lite",name:"Gemini 2.5 Flash Lite"},{id:"gemini-embedding-001",name:"Gemini Embedding 001",type:"embedding"},{id:"text-embedding-005",name:"Text Embedding 005",type:"embedding"},{id:"text-embedding-004",name:"Text Embedding 004 (Legacy)",type:"embedding"}],openrouter:[{id:"auto",name:"Auto (Best Available)"}],glm:[{id:"glm-5",name:"GLM 5"},{id:"glm-4.7",name:"GLM 4.7"},{id:"glm-4.6v",name:"GLM 4.6V (Vision)"}],"glm-cn":[{id:"glm-5",name:"GLM 5"},{id:"glm-4.7",name:"GLM-4.7"},{id:"glm-4.6",name:"GLM-4.6"},{id:"glm-4.5-air",name:"GLM-4.5-Air"}],kimi:[{id:"kimi-k2.5",name:"Kimi K2.5"},{id:"kimi-k2.5-thinking",name:"Kimi K2.5 Thinking"},{id:"kimi-latest",name:"Kimi Latest"}],minimax:[{id:"MiniMax-M2.5",name:"MiniMax M2.5"},{id:"MiniMax-M2.1",name:"MiniMax M2.1"}],"minimax-cn":[{id:"MiniMax-M2.5",name:"MiniMax M2.5"},{id:"MiniMax-M2.1",name:"MiniMax M2.1"}],deepseek:[{id:"deepseek-chat",name:"DeepSeek V3.2 Chat"},{id:"deepseek-reasoner",name:"DeepSeek V3.2 Reasoner"}],groq:[{id:"llama-3.3-70b-versatile",name:"Llama 3.3 70B"},{id:"meta-llama/llama-4-maverick-17b-128e-instruct",name:"Llama 4 Maverick"},{id:"qwen/qwen3-32b",name:"Qwen3 32B"},{id:"openai/gpt-oss-120b",name:"GPT-OSS 120B"}],xai:[{id:"grok-4",name:"Grok 4"},{id:"grok-4-fast-reasoning",name:"Grok 4 Fast Reasoning"},{id:"grok-code-fast-1",name:"Grok Code Fast"},{id:"grok-3",name:"Grok 3"}],mistral:[{id:"mistral-large-latest",name:"Mistral Large 3"},{id:"codestral-latest",name:"Codestral"},{id:"mistral-medium-latest",name:"Mistral Medium 3"}],perplexity:[{id:"sonar-pro",name:"Sonar Pro"},{id:"sonar",name:"Sonar"}],together:[{id:"meta-llama/Llama-3.3-70B-Instruct-Turbo",name:"Llama 3.3 70B Turbo"},{id:"deepseek-ai/DeepSeek-R1",name:"DeepSeek R1"},{id:"Qwen/Qwen3-235B-A22B",name:"Qwen3 235B"},{id:"meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8",name:"Llama 4 Maverick"}],fireworks:[{id:"accounts/fireworks/models/deepseek-v3p1",name:"DeepSeek V3.1"},{id:"accounts/fireworks/models/llama-v3p3-70b-instruct",name:"Llama 3.3 70B"},{id:"accounts/fireworks/models/qwen3-235b-a22b",name:"Qwen3 235B"}],cerebras:[{id:"gpt-oss-120b",name:"GPT OSS 120B"},{id:"zai-glm-4.7",name:"ZAI GLM 4.7"},{id:"llama-3.3-70b",name:"Llama 3.3 70B"},{id:"llama-4-scout-17b-16e-instruct",name:"Llama 4 Scout"},{id:"qwen-3-235b-a22b-instruct-2507",name:"Qwen3 235B A22B"},{id:"qwen-3-32b",name:"Qwen3 32B"}],cohere:[{id:"command-r-plus-08-2024",name:"Command R+ (Aug 2024)"},{id:"command-r-08-2024",name:"Command R (Aug 2024)"},{id:"command-a-03-2025",name:"Command A (Mar 2025)"}],nvidia:[{id:"moonshotai/kimi-k2.5",name:"Kimi K2.5"},{id:"z-ai/glm4.7",name:"GLM 4.7"},{id:"deepseek-ai/deepseek-v3.2",name:"DeepSeek V3.2"},{id:"nvidia/llama-3.3-70b-instruct",name:"Llama 3.3 70B"},{id:"meta/llama-4-maverick-17b-128e-instruct",name:"Llama 4 Maverick"},{id:"deepseek/deepseek-r1",name:"DeepSeek R1"}],nebius:[{id:"meta-llama/Llama-3.3-70B-Instruct",name:"Llama 3.3 70B Instruct"}],siliconflow:[{id:"deepseek-ai/DeepSeek-V3.2",name:"DeepSeek V3.2"},{id:"deepseek-ai/DeepSeek-V3.1",name:"DeepSeek V3.1"},{id:"deepseek-ai/DeepSeek-R1",name:"DeepSeek R1"},{id:"Qwen/Qwen3-235B-A22B-Instruct-2507",name:"Qwen3 235B"},{id:"Qwen/Qwen3-Coder-480B-A35B-Instruct",name:"Qwen3 Coder 480B"},{id:"Qwen/Qwen3-32B",name:"Qwen3 32B"},{id:"moonshotai/Kimi-K2.5",name:"Kimi K2.5"},{id:"zai-org/GLM-4.7",name:"GLM 4.7"},{id:"openai/gpt-oss-120b",name:"GPT OSS 120B"},{id:"baidu/ERNIE-4.5-300B-A47B",name:"ERNIE 4.5 300B"}],hyperbolic:[{id:"Qwen/QwQ-32B",name:"QwQ 32B"},{id:"deepseek-ai/DeepSeek-R1",name:"DeepSeek R1"},{id:"deepseek-ai/DeepSeek-V3",name:"DeepSeek V3"},{id:"meta-llama/Llama-3.3-70B-Instruct",name:"Llama 3.3 70B"},{id:"meta-llama/Llama-3.2-3B-Instruct",name:"Llama 3.2 3B"},{id:"Qwen/Qwen2.5-72B-Instruct",name:"Qwen 2.5 72B"},{id:"Qwen/Qwen2.5-Coder-32B-Instruct",name:"Qwen 2.5 Coder 32B"},{id:"NousResearch/Hermes-3-Llama-3.1-70B",name:"Hermes 3 70B"}]},e={claude:"cc",codex:"cx","gemini-cli":"gc",qwen:"qw",iflow:"if",antigravity:"ag",github:"gh",kiro:"kr",cursor:"cu","kimi-coding":"kmc",kilocode:"kc",cline:"cl",openai:"openai",anthropic:"anthropic",gemini:"gemini",openrouter:"openrouter",glm:"glm","glm-cn":"glm-cn",kimi:"kimi",minimax:"minimax","minimax-cn":"minimax-cn",deepseek:"deepseek",groq:"groq",xai:"xai",mistral:"mistral",perplexity:"perplexity",together:"together",fireworks:"fireworks",cerebras:"cerebras",cohere:"cohere",nvidia:"nvidia",nebius:"nebius",siliconflow:"siliconflow",hyperbolic:"hyperbolic"};function f(a){return d[e[a]||a]||[]}},61135:()=>{},69832:(a,b,c)=>{Promise.resolve().then(c.t.bind(c,95547,23))},70440:(a,b,c)=>{"use strict";c.r(b),c.d(b,{default:()=>e});var d=c(88868);let e=async a=>[{type:"image/x-icon",sizes:"16x16",url:(0,d.fillMetadataSegment)(".",await a.params,"favicon.ico")+"?603d046c9a6fdfbb"}]},78335:()=>{},85440:(a,b,c)=>{Promise.resolve().then(c.bind(c,19308))},85567:(a,b,c)=>{"use strict";c.d(b,{C7:()=>n,Cr:()=>q,LZ:()=>o,r4:()=>r,xS:()=>p});var d=c(29021),e=c.n(d),f=c(33873),g=c.n(f),h=c(21820),i=c.n(h);let j=g().join(i().homedir(),".9router","tunnel"),k=g().join(j,"state.json"),l=g().join(j,"cloudflared.pid");function m(){e().existsSync(j)||e().mkdirSync(j,{recursive:!0})}function n(){try{if(e().existsSync(k))return JSON.parse(e().readFileSync(k,"utf8"))}catch(a){}return null}function o(a){m(),e().writeFileSync(k,JSON.stringify(a,null,2))}function p(a){m(),e().writeFileSync(l,a.toString())}function q(){try{if(e().existsSync(l))return parseInt(e().readFileSync(l,"utf8"))}catch(a){}return null}function r(){try{e().existsSync(l)&&e().unlinkSync(l)}catch(a){}}},89886:(a,b,c)=>{"use strict";c.d(b,{ThemeProvider:()=>d});let d=(0,c(77943).registerClientReference)(function(){throw Error("Attempted to call ThemeProvider() from the server but ThemeProvider is on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.")},"/Users/Working/router4/app/src/shared/components/ThemeProvider.js","ThemeProvider")},91431:(a,b,c)=>{"use strict";c.d(b,{KC:()=>d.KC,Xg:()=>d.Xg});var d=c(60757);Object.entries(c(96919).Q2).filter(([,a])=>a.passthroughModels).map(([a])=>a),Object.entries(d.vq).flatMap(([a,b])=>b.map(b=>({provider:a,model:b.id,name:b.name})))},96487:()=>{},96919:(a,b,c)=>{"use strict";c.d(b,{DI:()=>h,IS:()=>d,JH:()=>g,MA:()=>l,Q2:()=>k,fg:()=>f,gb:()=>j,mq:()=>i,wG:()=>n,wb:()=>o,zN:()=>e,zt:()=>m});let d={iflow:{id:"iflow",alias:"if",name:"iFlow AI",icon:"water_drop",color:"#6366F1"},qwen:{id:"qwen",alias:"qw",name:"Qwen Code",icon:"psychology",color:"#10B981"},"gemini-cli":{id:"gemini-cli",alias:"gc",name:"Gemini CLI",icon:"terminal",color:"#4285F4"},kiro:{id:"kiro",alias:"kr",name:"Kiro AI",icon:"psychology_alt",color:"#FF6B35"}},e={claude:{id:"claude",alias:"cc",name:"Claude Code",icon:"smart_toy",color:"#D97757"},antigravity:{id:"antigravity",alias:"ag",name:"Antigravity",icon:"rocket_launch",color:"#F59E0B"},codex:{id:"codex",alias:"cx",name:"OpenAI Codex",icon:"code",color:"#3B82F6"},github:{id:"github",alias:"gh",name:"GitHub Copilot",icon:"code",color:"#333333"},cursor:{id:"cursor",alias:"cu",name:"Cursor IDE",icon:"edit_note",color:"#00D4AA"}},f={openrouter:{id:"openrouter",alias:"openrouter",name:"OpenRouter",icon:"router",color:"#F97316",textIcon:"OR",passthroughModels:!0,website:"https://openrouter.ai"},glm:{id:"glm",alias:"glm",name:"GLM Coding",icon:"code",color:"#2563EB",textIcon:"GL",website:"https://open.bigmodel.cn"},kimi:{id:"kimi",alias:"kimi",name:"Kimi",icon:"psychology",color:"#1E3A8A",textIcon:"KM",website:"https://kimi.moonshot.cn"},minimax:{id:"minimax",alias:"minimax",name:"Minimax Coding",icon:"memory",color:"#7C3AED",textIcon:"MM",website:"https://www.minimaxi.com"},"minimax-cn":{id:"minimax-cn",alias:"minimax-cn",name:"Minimax (China)",icon:"memory",color:"#DC2626",textIcon:"MC",website:"https://www.minimaxi.com"},openai:{id:"openai",alias:"openai",name:"OpenAI",icon:"auto_awesome",color:"#10A37F",textIcon:"OA",website:"https://platform.openai.com"},anthropic:{id:"anthropic",alias:"anthropic",name:"Anthropic",icon:"smart_toy",color:"#D97757",textIcon:"AN",website:"https://console.anthropic.com"},gemini:{id:"gemini",alias:"gemini",name:"Gemini",icon:"diamond",color:"#4285F4",textIcon:"GE",website:"https://ai.google.dev"},deepseek:{id:"deepseek",alias:"ds",name:"DeepSeek",icon:"bolt",color:"#4D6BFE",textIcon:"DS",website:"https://deepseek.com"},groq:{id:"groq",alias:"groq",name:"Groq",icon:"speed",color:"#F55036",textIcon:"GQ",website:"https://groq.com"},xai:{id:"xai",alias:"xai",name:"xAI (Grok)",icon:"auto_awesome",color:"#1DA1F2",textIcon:"XA",website:"https://x.ai"},mistral:{id:"mistral",alias:"mistral",name:"Mistral",icon:"air",color:"#FF7000",textIcon:"MI",website:"https://mistral.ai"},perplexity:{id:"perplexity",alias:"pplx",name:"Perplexity",icon:"search",color:"#20808D",textIcon:"PP",website:"https://www.perplexity.ai"},together:{id:"together",alias:"together",name:"Together AI",icon:"group_work",color:"#0F6FFF",textIcon:"TG",website:"https://www.together.ai"},fireworks:{id:"fireworks",alias:"fireworks",name:"Fireworks AI",icon:"local_fire_department",color:"#7B2EF2",textIcon:"FW",website:"https://fireworks.ai"},cerebras:{id:"cerebras",alias:"cerebras",name:"Cerebras",icon:"memory",color:"#FF4F00",textIcon:"CB",website:"https://www.cerebras.ai"},cohere:{id:"cohere",alias:"cohere",name:"Cohere",icon:"hub",color:"#39594D",textIcon:"CO",website:"https://cohere.com"},nvidia:{id:"nvidia",alias:"nvidia",name:"NVIDIA NIM",icon:"developer_board",color:"#76B900",textIcon:"NV",website:"https://developer.nvidia.com/nim"},nebius:{id:"nebius",alias:"nebius",name:"Nebius AI",icon:"cloud",color:"#6C5CE7",textIcon:"NB",website:"https://nebius.com"},siliconflow:{id:"siliconflow",alias:"siliconflow",name:"SiliconFlow",icon:"cloud_queue",color:"#5B6EF5",textIcon:"SF",website:"https://cloud.siliconflow.com"},hyperbolic:{id:"hyperbolic",alias:"hyp",name:"Hyperbolic",icon:"bolt",color:"#00D4FF",textIcon:"HY",website:"https://hyperbolic.xyz"},deepgram:{id:"deepgram",alias:"dg",name:"Deepgram",icon:"mic",color:"#13EF93",textIcon:"DG",website:"https://deepgram.com"},assemblyai:{id:"assemblyai",alias:"aai",name:"AssemblyAI",icon:"record_voice_over",color:"#0062FF",textIcon:"AA",website:"https://assemblyai.com"},nanobanana:{id:"nanobanana",alias:"nb",name:"NanoBanana",icon:"image",color:"#FFD700",textIcon:"NB",website:"https://nanobananaapi.ai"}},g="openai-compatible-",h="anthropic-compatible-";function i(a){return"string"==typeof a&&a.startsWith(g)}function j(a){return"string"==typeof a&&a.startsWith(h)}let k={...d,...e,...f},l={oauth:{id:"oauth",name:"OAuth",icon:"lock"},apikey:{id:"apikey",name:"API Key",icon:"key"}};function m(a){for(let b of Object.values(k))if(b.alias===a||b.id===a)return b;return null}function n(a){let b=k[a];return b?.alias||a}Object.values(k).reduce((a,b)=>(a[b.alias]=b.id,a),{}),Object.values(k).reduce((a,b)=>(a[b.id]=b.alias,a),{});let o=["antigravity","kiro","github","codex"]},97868:(a,b,c)=>{Promise.resolve().then(c.t.bind(c,81921,23)),Promise.resolve().then(c.t.bind(c,60440,23)),Promise.resolve().then(c.t.bind(c,84342,23)),Promise.resolve().then(c.t.bind(c,82265,23)),Promise.resolve().then(c.t.bind(c,35421,23)),Promise.resolve().then(c.t.bind(c,61335,23)),Promise.resolve().then(c.t.bind(c,70664,23)),Promise.resolve().then(c.bind(c,74661))},98592:(a,b,c)=>{Promise.resolve().then(c.bind(c,89886))}};