ai-agent-router 0.1.21 → 0.2.1

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 (368) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +14 -0
  3. package/.next/build-manifest.json +2 -2
  4. package/.next/fallback-build-manifest.json +2 -2
  5. package/.next/routes-manifest.json +84 -0
  6. package/.next/server/app/_global-error/page.js +1 -1
  7. package/.next/server/app/_global-error/page.js.nft.json +1 -1
  8. package/.next/server/app/_global-error.html +2 -2
  9. package/.next/server/app/_global-error.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  13. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  14. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  15. package/.next/server/app/_not-found/page.js +1 -1
  16. package/.next/server/app/_not-found/page.js.nft.json +1 -1
  17. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  18. package/.next/server/app/_not-found.html +1 -1
  19. package/.next/server/app/_not-found.rsc +2 -2
  20. package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  21. package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  22. package/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  23. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  24. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  25. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  26. package/.next/server/app/api/config/route.js.nft.json +1 -1
  27. package/.next/server/app/api/gateway/[...path]/route.js.nft.json +1 -1
  28. package/.next/server/app/api/gateway/models/route.js.nft.json +1 -1
  29. package/.next/server/app/api/gateway/route.js.nft.json +1 -1
  30. package/.next/server/app/api/ide/claude/apply/route.js.nft.json +1 -1
  31. package/.next/server/app/api/ide/claude/available-models/route.js.nft.json +1 -1
  32. package/.next/server/app/api/ide/claude/save/route.js.nft.json +1 -1
  33. package/.next/server/app/api/ide/claude/status/route.js.nft.json +1 -1
  34. package/.next/server/app/api/ide/claude/test/route.js +1 -1
  35. package/.next/server/app/api/ide/claude/test/route.js.nft.json +1 -1
  36. package/.next/server/app/api/ide/openclaw/apply/route/app-paths-manifest.json +3 -0
  37. package/.next/server/app/api/ide/openclaw/apply/route/build-manifest.json +11 -0
  38. package/.next/server/app/api/ide/openclaw/apply/route/server-reference-manifest.json +4 -0
  39. package/.next/server/app/api/ide/openclaw/apply/route.js +7 -0
  40. package/.next/server/app/api/ide/openclaw/apply/route.js.map +5 -0
  41. package/.next/server/app/api/ide/openclaw/apply/route.js.nft.json +1 -0
  42. package/.next/server/app/api/ide/openclaw/apply/route_client-reference-manifest.js +2 -0
  43. package/.next/server/app/api/ide/openclaw/available-models/route/app-paths-manifest.json +3 -0
  44. package/.next/server/app/api/ide/openclaw/available-models/route/build-manifest.json +11 -0
  45. package/.next/server/app/api/ide/openclaw/available-models/route/server-reference-manifest.json +4 -0
  46. package/.next/server/app/api/ide/openclaw/available-models/route.js +7 -0
  47. package/.next/server/app/api/ide/openclaw/available-models/route.js.map +5 -0
  48. package/.next/server/app/api/ide/openclaw/available-models/route.js.nft.json +1 -0
  49. package/.next/server/app/api/ide/openclaw/available-models/route_client-reference-manifest.js +2 -0
  50. package/.next/server/app/api/ide/openclaw/preview/route/app-paths-manifest.json +3 -0
  51. package/.next/server/app/api/ide/openclaw/preview/route/build-manifest.json +11 -0
  52. package/.next/server/app/api/ide/openclaw/preview/route/server-reference-manifest.json +4 -0
  53. package/.next/server/app/api/ide/openclaw/preview/route.js +7 -0
  54. package/.next/server/app/api/ide/openclaw/preview/route.js.map +5 -0
  55. package/.next/server/app/api/ide/openclaw/preview/route.js.nft.json +1 -0
  56. package/.next/server/app/api/ide/openclaw/preview/route_client-reference-manifest.js +2 -0
  57. package/.next/server/app/api/ide/openclaw/restore/route/app-paths-manifest.json +3 -0
  58. package/.next/server/app/api/ide/openclaw/restore/route/build-manifest.json +11 -0
  59. package/.next/server/app/api/ide/openclaw/restore/route/server-reference-manifest.json +4 -0
  60. package/.next/server/app/api/ide/openclaw/restore/route.js +6 -0
  61. package/.next/server/app/api/ide/openclaw/restore/route.js.map +5 -0
  62. package/.next/server/app/api/ide/openclaw/restore/route.js.nft.json +1 -0
  63. package/.next/server/app/api/ide/openclaw/restore/route_client-reference-manifest.js +2 -0
  64. package/.next/server/app/api/ide/openclaw/save/route/app-paths-manifest.json +3 -0
  65. package/.next/server/app/api/ide/openclaw/save/route/build-manifest.json +11 -0
  66. package/.next/server/app/api/ide/openclaw/save/route/server-reference-manifest.json +4 -0
  67. package/.next/server/app/api/ide/openclaw/save/route.js +7 -0
  68. package/.next/server/app/api/ide/openclaw/save/route.js.map +5 -0
  69. package/.next/server/app/api/ide/openclaw/save/route.js.nft.json +1 -0
  70. package/.next/server/app/api/ide/openclaw/save/route_client-reference-manifest.js +2 -0
  71. package/.next/server/app/api/ide/openclaw/status/route/app-paths-manifest.json +3 -0
  72. package/.next/server/app/api/ide/openclaw/status/route/build-manifest.json +11 -0
  73. package/.next/server/app/api/ide/openclaw/status/route/server-reference-manifest.json +4 -0
  74. package/.next/server/app/api/ide/openclaw/status/route.js +7 -0
  75. package/.next/server/app/api/ide/openclaw/status/route.js.map +5 -0
  76. package/.next/server/app/api/ide/openclaw/status/route.js.nft.json +1 -0
  77. package/.next/server/app/api/ide/openclaw/status/route_client-reference-manifest.js +2 -0
  78. package/.next/server/app/api/ide/openclaw/test/route/app-paths-manifest.json +3 -0
  79. package/.next/server/app/api/ide/openclaw/test/route/build-manifest.json +11 -0
  80. package/.next/server/app/api/ide/openclaw/test/route/server-reference-manifest.json +4 -0
  81. package/.next/server/app/api/ide/openclaw/test/route.js +6 -0
  82. package/.next/server/app/api/ide/openclaw/test/route.js.map +5 -0
  83. package/.next/server/app/api/ide/openclaw/test/route.js.nft.json +1 -0
  84. package/.next/server/app/api/ide/openclaw/test/route_client-reference-manifest.js +2 -0
  85. package/.next/server/app/api/ide/opencode/apply/route/app-paths-manifest.json +3 -0
  86. package/.next/server/app/api/ide/opencode/apply/route/build-manifest.json +11 -0
  87. package/.next/server/app/api/ide/opencode/apply/route/server-reference-manifest.json +4 -0
  88. package/.next/server/app/api/ide/opencode/apply/route.js +7 -0
  89. package/.next/server/app/api/ide/opencode/apply/route.js.map +5 -0
  90. package/.next/server/app/api/ide/opencode/apply/route.js.nft.json +1 -0
  91. package/.next/server/app/api/ide/opencode/apply/route_client-reference-manifest.js +2 -0
  92. package/.next/server/app/api/ide/opencode/available-models/route/app-paths-manifest.json +3 -0
  93. package/.next/server/app/api/ide/opencode/available-models/route/build-manifest.json +11 -0
  94. package/.next/server/app/api/ide/opencode/available-models/route/server-reference-manifest.json +4 -0
  95. package/.next/server/app/api/ide/opencode/available-models/route.js +7 -0
  96. package/.next/server/app/api/ide/opencode/available-models/route.js.map +5 -0
  97. package/.next/server/app/api/ide/opencode/available-models/route.js.nft.json +1 -0
  98. package/.next/server/app/api/ide/opencode/available-models/route_client-reference-manifest.js +2 -0
  99. package/.next/server/app/api/ide/opencode/preview/route/app-paths-manifest.json +3 -0
  100. package/.next/server/app/api/ide/opencode/preview/route/build-manifest.json +11 -0
  101. package/.next/server/app/api/ide/opencode/preview/route/server-reference-manifest.json +4 -0
  102. package/.next/server/app/api/ide/opencode/preview/route.js +7 -0
  103. package/.next/server/app/api/ide/opencode/preview/route.js.map +5 -0
  104. package/.next/server/app/api/ide/opencode/preview/route.js.nft.json +1 -0
  105. package/.next/server/app/api/ide/opencode/preview/route_client-reference-manifest.js +2 -0
  106. package/.next/server/app/api/ide/opencode/restore/route/app-paths-manifest.json +3 -0
  107. package/.next/server/app/api/ide/opencode/restore/route/build-manifest.json +11 -0
  108. package/.next/server/app/api/ide/opencode/restore/route/server-reference-manifest.json +4 -0
  109. package/.next/server/app/api/ide/opencode/restore/route.js +6 -0
  110. package/.next/server/app/api/ide/opencode/restore/route.js.map +5 -0
  111. package/.next/server/app/api/ide/opencode/restore/route.js.nft.json +1 -0
  112. package/.next/server/app/api/ide/opencode/restore/route_client-reference-manifest.js +2 -0
  113. package/.next/server/app/api/ide/opencode/save/route/app-paths-manifest.json +3 -0
  114. package/.next/server/app/api/ide/opencode/save/route/build-manifest.json +11 -0
  115. package/.next/server/app/api/ide/opencode/save/route/server-reference-manifest.json +4 -0
  116. package/.next/server/app/api/ide/opencode/save/route.js +7 -0
  117. package/.next/server/app/api/ide/opencode/save/route.js.map +5 -0
  118. package/.next/server/app/api/ide/opencode/save/route.js.nft.json +1 -0
  119. package/.next/server/app/api/ide/opencode/save/route_client-reference-manifest.js +2 -0
  120. package/.next/server/app/api/ide/opencode/status/route/app-paths-manifest.json +3 -0
  121. package/.next/server/app/api/ide/opencode/status/route/build-manifest.json +11 -0
  122. package/.next/server/app/api/ide/opencode/status/route/server-reference-manifest.json +4 -0
  123. package/.next/server/app/api/ide/opencode/status/route.js +7 -0
  124. package/.next/server/app/api/ide/opencode/status/route.js.map +5 -0
  125. package/.next/server/app/api/ide/opencode/status/route.js.nft.json +1 -0
  126. package/.next/server/app/api/ide/opencode/status/route_client-reference-manifest.js +2 -0
  127. package/.next/server/app/api/ide/opencode/test/route/app-paths-manifest.json +3 -0
  128. package/.next/server/app/api/ide/opencode/test/route/build-manifest.json +11 -0
  129. package/.next/server/app/api/ide/opencode/test/route/server-reference-manifest.json +4 -0
  130. package/.next/server/app/api/ide/opencode/test/route.js +6 -0
  131. package/.next/server/app/api/ide/opencode/test/route.js.map +5 -0
  132. package/.next/server/app/api/ide/opencode/test/route.js.nft.json +1 -0
  133. package/.next/server/app/api/ide/opencode/test/route_client-reference-manifest.js +2 -0
  134. package/.next/server/app/api/logs/route.js.nft.json +1 -1
  135. package/.next/server/app/api/models/route.js.nft.json +1 -1
  136. package/.next/server/app/api/providers/route.js.nft.json +1 -1
  137. package/.next/server/app/api/providers/test/route.js.nft.json +1 -1
  138. package/.next/server/app/api/service/force-stop/route.js.nft.json +1 -1
  139. package/.next/server/app/api/service/start/route.js.nft.json +1 -1
  140. package/.next/server/app/api/service/status/route.js.nft.json +1 -1
  141. package/.next/server/app/api/service/stop/route.js.nft.json +1 -1
  142. package/.next/server/app/ide/page.js +1 -1
  143. package/.next/server/app/ide/page.js.nft.json +1 -1
  144. package/.next/server/app/ide/page_client-reference-manifest.js +1 -1
  145. package/.next/server/app/ide.html +1 -1
  146. package/.next/server/app/ide.rsc +3 -3
  147. package/.next/server/app/ide.segments/_full.segment.rsc +3 -3
  148. package/.next/server/app/ide.segments/_head.segment.rsc +1 -1
  149. package/.next/server/app/ide.segments/_index.segment.rsc +2 -2
  150. package/.next/server/app/ide.segments/_tree.segment.rsc +2 -2
  151. package/.next/server/app/ide.segments/ide/__PAGE__.segment.rsc +2 -2
  152. package/.next/server/app/ide.segments/ide.segment.rsc +1 -1
  153. package/.next/server/app/index.html +1 -1
  154. package/.next/server/app/index.rsc +2 -2
  155. package/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  156. package/.next/server/app/index.segments/_full.segment.rsc +2 -2
  157. package/.next/server/app/index.segments/_head.segment.rsc +1 -1
  158. package/.next/server/app/index.segments/_index.segment.rsc +2 -2
  159. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  160. package/.next/server/app/logs/page.js +1 -1
  161. package/.next/server/app/logs/page.js.nft.json +1 -1
  162. package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
  163. package/.next/server/app/logs.html +1 -1
  164. package/.next/server/app/logs.rsc +3 -3
  165. package/.next/server/app/logs.segments/_full.segment.rsc +3 -3
  166. package/.next/server/app/logs.segments/_head.segment.rsc +1 -1
  167. package/.next/server/app/logs.segments/_index.segment.rsc +2 -2
  168. package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
  169. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +2 -2
  170. package/.next/server/app/logs.segments/logs.segment.rsc +1 -1
  171. package/.next/server/app/models/page.js +1 -1
  172. package/.next/server/app/models/page.js.nft.json +1 -1
  173. package/.next/server/app/models/page_client-reference-manifest.js +1 -1
  174. package/.next/server/app/models.html +1 -1
  175. package/.next/server/app/models.rsc +2 -2
  176. package/.next/server/app/models.segments/_full.segment.rsc +2 -2
  177. package/.next/server/app/models.segments/_head.segment.rsc +1 -1
  178. package/.next/server/app/models.segments/_index.segment.rsc +2 -2
  179. package/.next/server/app/models.segments/_tree.segment.rsc +2 -2
  180. package/.next/server/app/models.segments/models/__PAGE__.segment.rsc +1 -1
  181. package/.next/server/app/models.segments/models.segment.rsc +1 -1
  182. package/.next/server/app/page.js +1 -1
  183. package/.next/server/app/page.js.nft.json +1 -1
  184. package/.next/server/app/page_client-reference-manifest.js +1 -1
  185. package/.next/server/app/providers/page.js +1 -1
  186. package/.next/server/app/providers/page.js.nft.json +1 -1
  187. package/.next/server/app/providers/page_client-reference-manifest.js +1 -1
  188. package/.next/server/app/providers.html +1 -1
  189. package/.next/server/app/providers.rsc +2 -2
  190. package/.next/server/app/providers.segments/_full.segment.rsc +2 -2
  191. package/.next/server/app/providers.segments/_head.segment.rsc +1 -1
  192. package/.next/server/app/providers.segments/_index.segment.rsc +2 -2
  193. package/.next/server/app/providers.segments/_tree.segment.rsc +2 -2
  194. package/.next/server/app/providers.segments/providers/__PAGE__.segment.rsc +1 -1
  195. package/.next/server/app/providers.segments/providers.segment.rsc +1 -1
  196. package/.next/server/app-paths-manifest.json +14 -0
  197. package/.next/server/chunks/[root-of-the-server]__001d5756._.js +3 -0
  198. package/.next/server/chunks/[root-of-the-server]__001d5756._.js.map +1 -0
  199. package/.next/server/chunks/[root-of-the-server]__05f8578b._.js +3 -0
  200. package/.next/server/chunks/[root-of-the-server]__05f8578b._.js.map +1 -0
  201. package/.next/server/chunks/[root-of-the-server]__1480f018._.js +1 -1
  202. package/.next/server/chunks/[root-of-the-server]__1480f018._.js.map +1 -1
  203. package/.next/server/chunks/[root-of-the-server]__1909f3aa._.js +1 -1
  204. package/.next/server/chunks/[root-of-the-server]__1909f3aa._.js.map +1 -1
  205. package/.next/server/chunks/[root-of-the-server]__1d4b7fc5._.js +1 -1
  206. package/.next/server/chunks/[root-of-the-server]__1d4b7fc5._.js.map +1 -1
  207. package/.next/server/chunks/[root-of-the-server]__372ef2bf._.js +1 -1
  208. package/.next/server/chunks/[root-of-the-server]__372ef2bf._.js.map +1 -1
  209. package/.next/server/chunks/[root-of-the-server]__3aaf963c._.js +1 -1
  210. package/.next/server/chunks/[root-of-the-server]__3aaf963c._.js.map +1 -1
  211. package/.next/server/chunks/[root-of-the-server]__43810962._.js +3 -0
  212. package/.next/server/chunks/[root-of-the-server]__43810962._.js.map +1 -0
  213. package/.next/server/chunks/[root-of-the-server]__55cd88b8._.js +3 -0
  214. package/.next/server/chunks/[root-of-the-server]__55cd88b8._.js.map +1 -0
  215. package/.next/server/chunks/[root-of-the-server]__5e8276bc._.js +1 -1
  216. package/.next/server/chunks/[root-of-the-server]__5e8276bc._.js.map +1 -1
  217. package/.next/server/chunks/[root-of-the-server]__6ce199d2._.js +1 -1
  218. package/.next/server/chunks/[root-of-the-server]__6ce199d2._.js.map +1 -1
  219. package/.next/server/chunks/[root-of-the-server]__760eaa16._.js +3 -0
  220. package/.next/server/chunks/[root-of-the-server]__760eaa16._.js.map +1 -0
  221. package/.next/server/chunks/[root-of-the-server]__772134c6._.js +1 -1
  222. package/.next/server/chunks/[root-of-the-server]__772134c6._.js.map +1 -1
  223. package/.next/server/chunks/[root-of-the-server]__7b77f523._.js +1 -1
  224. package/.next/server/chunks/[root-of-the-server]__7b77f523._.js.map +1 -1
  225. package/.next/server/chunks/[root-of-the-server]__7c298a19._.js +3 -0
  226. package/.next/server/chunks/[root-of-the-server]__7c298a19._.js.map +1 -0
  227. package/.next/server/chunks/[root-of-the-server]__85540228._.js +3 -0
  228. package/.next/server/chunks/[root-of-the-server]__85540228._.js.map +1 -0
  229. package/.next/server/chunks/[root-of-the-server]__94fe8d3c._.js +3 -0
  230. package/.next/server/chunks/[root-of-the-server]__94fe8d3c._.js.map +1 -0
  231. package/.next/server/chunks/[root-of-the-server]__97622908._.js +3 -0
  232. package/.next/server/chunks/[root-of-the-server]__97622908._.js.map +1 -0
  233. package/.next/server/chunks/[root-of-the-server]__a02e6618._.js +3 -0
  234. package/.next/server/chunks/[root-of-the-server]__a02e6618._.js.map +1 -0
  235. package/.next/server/chunks/[root-of-the-server]__a32a20a7._.js +3 -0
  236. package/.next/server/chunks/[root-of-the-server]__a32a20a7._.js.map +1 -0
  237. package/.next/server/chunks/[root-of-the-server]__af5b556a._.js +3 -0
  238. package/.next/server/chunks/[root-of-the-server]__af5b556a._.js.map +1 -0
  239. package/.next/server/chunks/[root-of-the-server]__c1b4b601._.js +18 -18
  240. package/.next/server/chunks/[root-of-the-server]__c1b4b601._.js.map +1 -1
  241. package/.next/server/chunks/[root-of-the-server]__cafe113e._.js +3 -0
  242. package/.next/server/chunks/[root-of-the-server]__cafe113e._.js.map +1 -0
  243. package/.next/server/chunks/[root-of-the-server]__ccfc7f1d._.js +1 -1
  244. package/.next/server/chunks/[root-of-the-server]__ccfc7f1d._.js.map +1 -1
  245. package/.next/server/chunks/[root-of-the-server]__dc8b0bed._.js +3 -0
  246. package/.next/server/chunks/[root-of-the-server]__dc8b0bed._.js.map +1 -0
  247. package/.next/server/chunks/[root-of-the-server]__f8949f88._.js +1 -1
  248. package/.next/server/chunks/[root-of-the-server]__f8949f88._.js.map +1 -1
  249. package/.next/server/chunks/_next-internal_server_app_api_ide_openclaw_apply_route_actions_2cb9e4b4.js +3 -0
  250. package/.next/server/chunks/_next-internal_server_app_api_ide_openclaw_apply_route_actions_2cb9e4b4.js.map +1 -0
  251. package/.next/server/chunks/_next-internal_server_app_api_ide_openclaw_preview_route_actions_9814a8e4.js +3 -0
  252. package/.next/server/chunks/_next-internal_server_app_api_ide_openclaw_preview_route_actions_9814a8e4.js.map +1 -0
  253. package/.next/server/chunks/_next-internal_server_app_api_ide_openclaw_restore_route_actions_10ad8f9d.js +3 -0
  254. package/.next/server/chunks/_next-internal_server_app_api_ide_openclaw_restore_route_actions_10ad8f9d.js.map +1 -0
  255. package/.next/server/chunks/_next-internal_server_app_api_ide_openclaw_save_route_actions_044ad081.js +3 -0
  256. package/.next/server/chunks/_next-internal_server_app_api_ide_openclaw_save_route_actions_044ad081.js.map +1 -0
  257. package/.next/server/chunks/_next-internal_server_app_api_ide_openclaw_status_route_actions_ed9786d2.js +3 -0
  258. package/.next/server/chunks/_next-internal_server_app_api_ide_openclaw_status_route_actions_ed9786d2.js.map +1 -0
  259. package/.next/server/chunks/_next-internal_server_app_api_ide_openclaw_test_route_actions_ce2cb808.js +3 -0
  260. package/.next/server/chunks/_next-internal_server_app_api_ide_openclaw_test_route_actions_ce2cb808.js.map +1 -0
  261. package/.next/server/chunks/_next-internal_server_app_api_ide_opencode_apply_route_actions_6c422244.js +3 -0
  262. package/.next/server/chunks/_next-internal_server_app_api_ide_opencode_apply_route_actions_6c422244.js.map +1 -0
  263. package/.next/server/chunks/_next-internal_server_app_api_ide_opencode_preview_route_actions_256c82e0.js +3 -0
  264. package/.next/server/chunks/_next-internal_server_app_api_ide_opencode_preview_route_actions_256c82e0.js.map +1 -0
  265. package/.next/server/chunks/_next-internal_server_app_api_ide_opencode_restore_route_actions_371993d3.js +3 -0
  266. package/.next/server/chunks/_next-internal_server_app_api_ide_opencode_restore_route_actions_371993d3.js.map +1 -0
  267. package/.next/server/chunks/_next-internal_server_app_api_ide_opencode_save_route_actions_6e4c9c41.js +3 -0
  268. package/.next/server/chunks/_next-internal_server_app_api_ide_opencode_save_route_actions_6e4c9c41.js.map +1 -0
  269. package/.next/server/chunks/_next-internal_server_app_api_ide_opencode_status_route_actions_498ad77b.js +3 -0
  270. package/.next/server/chunks/_next-internal_server_app_api_ide_opencode_status_route_actions_498ad77b.js.map +1 -0
  271. package/.next/server/chunks/_next-internal_server_app_api_ide_opencode_test_route_actions_c71be510.js +3 -0
  272. package/.next/server/chunks/_next-internal_server_app_api_ide_opencode_test_route_actions_c71be510.js.map +1 -0
  273. package/.next/server/chunks/ce889_server_app_api_ide_openclaw_available-models_route_actions_e568e70b.js +3 -0
  274. package/.next/server/chunks/ce889_server_app_api_ide_openclaw_available-models_route_actions_e568e70b.js.map +1 -0
  275. package/.next/server/chunks/ce889_server_app_api_ide_opencode_available-models_route_actions_95230db3.js +3 -0
  276. package/.next/server/chunks/ce889_server_app_api_ide_opencode_available-models_route_actions_95230db3.js.map +1 -0
  277. package/.next/server/chunks/ssr/{[root-of-the-server]__bec95712._.js → [root-of-the-server]__81937253._.js} +2 -2
  278. package/.next/server/chunks/ssr/{[root-of-the-server]__bec95712._.js.map → [root-of-the-server]__81937253._.js.map} +1 -1
  279. package/.next/server/chunks/ssr/{[root-of-the-server]__71c85955._.js → [root-of-the-server]__976ad963._.js} +2 -2
  280. package/.next/server/chunks/ssr/{[root-of-the-server]__71c85955._.js.map → [root-of-the-server]__976ad963._.js.map} +1 -1
  281. package/.next/server/chunks/ssr/src_app_ide_page_tsx_8962793b._.js +1 -1
  282. package/.next/server/chunks/ssr/src_app_ide_page_tsx_8962793b._.js.map +1 -1
  283. package/.next/server/chunks/ssr/src_app_logs_page_tsx_7b7b7b83._.js +1 -1
  284. package/.next/server/chunks/ssr/src_app_logs_page_tsx_7b7b7b83._.js.map +1 -1
  285. package/.next/server/pages/404.html +1 -1
  286. package/.next/server/pages/500.html +2 -2
  287. package/.next/static/chunks/0f120c117962200b.css +1 -0
  288. package/.next/static/chunks/64f547b3bcd3aef4.js +1 -0
  289. package/.next/static/chunks/{81c904164fe81379.js → 7c8b7cbb3339f139.js} +1 -1
  290. package/.next/static/chunks/8ed839b2e4948968.js +1 -0
  291. package/.next/types/routes.d.ts +15 -1
  292. package/.next/types/validator.ts +126 -0
  293. package/README.md +100 -111
  294. package/dist/.next/dev/types/validator.js +56 -0
  295. package/dist/.next/types/validator.js +56 -0
  296. package/dist/src/app/api/gateway/[...path]/route.js +1 -1
  297. package/dist/src/app/api/gateway/route.js +1 -1
  298. package/dist/src/app/api/ide/claude/apply/route.js +42 -31
  299. package/dist/src/app/api/ide/claude/status/route.js +6 -1
  300. package/dist/src/app/api/ide/openclaw/apply/route.js +92 -0
  301. package/dist/src/app/api/ide/openclaw/available-models/route.js +46 -0
  302. package/dist/src/app/api/ide/openclaw/build-config.js +101 -0
  303. package/dist/src/app/api/ide/openclaw/preview/route.js +49 -0
  304. package/dist/src/app/api/ide/openclaw/restore/route.js +24 -0
  305. package/dist/src/app/api/ide/openclaw/save/route.js +54 -0
  306. package/dist/src/app/api/ide/openclaw/status/route.js +139 -0
  307. package/dist/src/app/api/ide/openclaw/test/route.js +158 -0
  308. package/dist/src/app/api/ide/opencode/apply/route.js +89 -0
  309. package/dist/src/app/api/ide/opencode/available-models/route.js +46 -0
  310. package/dist/src/app/api/ide/opencode/build-config.js +54 -0
  311. package/dist/src/app/api/ide/opencode/preview/route.js +36 -0
  312. package/dist/src/app/api/ide/opencode/restore/route.js +40 -0
  313. package/dist/src/app/api/ide/opencode/save/route.js +123 -0
  314. package/dist/src/app/api/ide/opencode/status/route.js +106 -0
  315. package/dist/src/app/api/ide/opencode/test/route.js +136 -0
  316. package/dist/src/app/api/logs/route.js +2 -2
  317. package/dist/src/app/api/models/route.js +5 -5
  318. package/dist/src/app/api/providers/route.js +4 -4
  319. package/dist/src/app/api/providers/test/route.js +1 -1
  320. package/dist/src/app/api/service/start/route.js +1 -1
  321. package/dist/src/app/api/service/status/route.js +1 -1
  322. package/dist/src/app/api/service/stop/route.js +1 -1
  323. package/dist/src/app/ide/page.js +591 -81
  324. package/dist/src/app/logs/page.js +15 -1
  325. package/dist/src/cli/index.js +218 -20
  326. package/dist/src/db/database.js +56 -5
  327. package/dist/src/db/queries.js +6 -6
  328. package/dist/src/server/logger.js +22 -4
  329. package/package.json +2 -1
  330. package/src/app/api/gateway/[...path]/route.ts +1 -1
  331. package/src/app/api/gateway/route.ts +1 -1
  332. package/src/app/api/ide/claude/apply/route.ts +46 -31
  333. package/src/app/api/ide/claude/status/route.ts +12 -2
  334. package/src/app/api/ide/openclaw/apply/route.ts +103 -0
  335. package/src/app/api/ide/openclaw/available-models/route.ts +59 -0
  336. package/src/app/api/ide/openclaw/build-config.ts +152 -0
  337. package/src/app/api/ide/openclaw/preview/route.ts +57 -0
  338. package/src/app/api/ide/openclaw/restore/route.ts +27 -0
  339. package/src/app/api/ide/openclaw/save/route.ts +67 -0
  340. package/src/app/api/ide/openclaw/status/route.ts +178 -0
  341. package/src/app/api/ide/openclaw/test/route.ts +194 -0
  342. package/src/app/api/ide/opencode/apply/route.ts +92 -0
  343. package/src/app/api/ide/opencode/available-models/route.ts +59 -0
  344. package/src/app/api/ide/opencode/build-config.ts +69 -0
  345. package/src/app/api/ide/opencode/preview/route.ts +40 -0
  346. package/src/app/api/ide/opencode/restore/route.ts +52 -0
  347. package/src/app/api/ide/opencode/save/route.ts +131 -0
  348. package/src/app/api/ide/opencode/status/route.ts +128 -0
  349. package/src/app/api/ide/opencode/test/route.ts +145 -0
  350. package/src/app/api/logs/route.ts +2 -2
  351. package/src/app/api/models/route.ts +5 -5
  352. package/src/app/api/providers/route.ts +4 -4
  353. package/src/app/api/providers/test/route.ts +1 -1
  354. package/src/app/api/service/start/route.ts +1 -1
  355. package/src/app/api/service/status/route.ts +1 -1
  356. package/src/app/api/service/stop/route.ts +1 -1
  357. package/src/app/globals.css +17 -0
  358. package/src/app/ide/page.tsx +1535 -132
  359. package/src/app/logs/page.tsx +17 -5
  360. package/src/cli/index.ts +228 -25
  361. package/src/db/database.ts +60 -8
  362. package/src/db/queries.ts +6 -6
  363. package/src/server/logger.ts +19 -4
  364. package/.next/static/chunks/6418ca50028376b7.css +0 -1
  365. package/.next/static/chunks/9ec3b97741b6575e.js +0 -1
  366. /package/.next/static/{PkfqdzwOZgX-UhSNUuhdp → dYin74gcpdlg8TGoGv-_d}/_buildManifest.js +0 -0
  367. /package/.next/static/{PkfqdzwOZgX-UhSNUuhdp → dYin74gcpdlg8TGoGv-_d}/_clientMiddlewareManifest.json +0 -0
  368. /package/.next/static/{PkfqdzwOZgX-UhSNUuhdp → dYin74gcpdlg8TGoGv-_d}/_ssgManifest.js +0 -0
@@ -7,9 +7,9 @@ import ConfirmDialog from '../components/ConfirmDialog';
7
7
 
8
8
  interface RequestLog {
9
9
  id: number;
10
- model_id: number;
11
- model_name?: string;
12
- provider_name?: string;
10
+ model_id: number | null;
11
+ model_name?: string | null;
12
+ provider_name?: string | null;
13
13
  request_method: string;
14
14
  request_path: string;
15
15
  request_headers: string;
@@ -21,6 +21,19 @@ interface RequestLog {
21
21
  created_at: string;
22
22
  }
23
23
 
24
+ /** 将服务端返回的 created_at 格式化为本地显示。数据库存储的是本地时间,无需追加 Z 后缀。 */
25
+ function formatCreatedAt(createdAt: string): string {
26
+ if (!createdAt || typeof createdAt !== 'string') return '';
27
+ const s = createdAt.trim();
28
+ if (!s) return '';
29
+ // 数据库使用 datetime('now','localtime') 存储的已经是本地时间,
30
+ // 不带时区后缀时直接按本地时间解析,不要追加 'Z'(否则会多偏移 8 小时)
31
+ const hasTz = /[Z+-]\d{2}:?\d{2}$/.test(s);
32
+ const iso = hasTz ? s : s.replace(' ', 'T');
33
+ const date = new Date(iso);
34
+ return Number.isNaN(date.getTime()) ? s : date.toLocaleString('zh-CN');
35
+ }
36
+
24
37
  export default function LogsPage() {
25
38
  const [logs, setLogs] = useState<RequestLog[]>([]);
26
39
  const [selectedLog, setSelectedLog] = useState<RequestLog | null>(null);
@@ -413,8 +426,7 @@ export default function LogsPage() {
413
426
  </td>
414
427
  <td className="px-4 py-3 whitespace-nowrap">
415
428
  <div className="text-xs text-slate-600">
416
- {/* 使用本地时区显示时间 */}
417
- {new Date(log.created_at + 'Z').toLocaleString('zh-CN')}
429
+ {formatCreatedAt(log.created_at)}
418
430
  </div>
419
431
  </td>
420
432
  <td className="px-4 py-3 whitespace-nowrap">
package/src/cli/index.ts CHANGED
@@ -1,16 +1,88 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { Command } from 'commander';
4
- import { spawn } from 'child_process';
4
+ import { spawn, ChildProcess } from 'child_process';
5
5
  import path from 'path';
6
+ import fs from 'fs';
7
+ import os from 'os';
8
+ import http from 'http';
6
9
  import { getDatabase } from '../db/database';
7
- import { getConfig, setConfig } from '../db/queries';
10
+ import { getConfig, setConfig, setServiceStatus, updateServiceStatus } from '../db/queries';
8
11
 
9
12
  const program = new Command();
10
13
 
11
14
  const packageJsonPath = require.resolve('../../../package.json');
12
15
  const packageJson = require(packageJsonPath);
13
16
 
17
+ const AAR_DIR = path.join(os.homedir(), '.aar');
18
+ const UI_PID_FILE = path.join(AAR_DIR, 'ui.pid');
19
+ const GATEWAY_PID_FILE = path.join(AAR_DIR, 'gateway.pid');
20
+ const GATEWAY_PORT_FILE = path.join(AAR_DIR, 'gateway.port');
21
+
22
+ function ensureAarDir(): void {
23
+ if (!fs.existsSync(AAR_DIR)) {
24
+ fs.mkdirSync(AAR_DIR, { recursive: true });
25
+ }
26
+ }
27
+
28
+ function getPackageRoot(): string {
29
+ return path.dirname(path.dirname(path.dirname(__dirname)));
30
+ }
31
+
32
+ /** Wait for HTTP server to respond (e.g. UI or gateway ready) */
33
+ function waitForReady(baseUrl: string, maxWaitMs: number = 30000): Promise<boolean> {
34
+ const url = new URL(baseUrl);
35
+ return new Promise((resolve) => {
36
+ const start = Date.now();
37
+ const tryOnce = () => {
38
+ const req = http.request(
39
+ {
40
+ hostname: url.hostname,
41
+ port: url.port || '80',
42
+ path: url.pathname || '/',
43
+ method: 'GET',
44
+ timeout: 2000,
45
+ },
46
+ (res) => {
47
+ resolve(res.statusCode !== undefined && res.statusCode < 500);
48
+ }
49
+ );
50
+ req.on('error', () => {
51
+ if (Date.now() - start >= maxWaitMs) {
52
+ resolve(false);
53
+ return;
54
+ }
55
+ setTimeout(tryOnce, 800);
56
+ });
57
+ req.on('timeout', () => {
58
+ req.destroy();
59
+ if (Date.now() - start >= maxWaitMs) resolve(false);
60
+ else setTimeout(tryOnce, 800);
61
+ });
62
+ req.end();
63
+ };
64
+ tryOnce();
65
+ });
66
+ }
67
+
68
+ function printStatus(uiPort: number, gatewayPort: number, gatewayRunning: boolean, background: boolean): void {
69
+ const uiUrl = `http://localhost:${uiPort}`;
70
+ const gatewayUrl = `http://localhost:${gatewayPort}`;
71
+ console.log('');
72
+ console.log('-------------------------------------------');
73
+ console.log(' AI Agent Router');
74
+ console.log('-------------------------------------------');
75
+ console.log(` 前台 UI: ${uiUrl}`);
76
+ console.log(` 网关地址: ${gatewayUrl}`);
77
+ console.log(` 网关状态: ${gatewayRunning ? '运行中' : '未启动'}`);
78
+ if (background) {
79
+ console.log(' 运行模式: 后台运行(关闭终端不影响)');
80
+ console.log(' 停止服务: aar stop');
81
+ }
82
+ console.log('-------------------------------------------');
83
+ console.log('');
84
+ }
85
+
14
86
  program
15
87
  .name('aar')
16
88
  .description('AI Agent Router - Web UI for managing the API gateway')
@@ -18,47 +90,178 @@ program
18
90
 
19
91
  program
20
92
  .command('start')
21
- .description('Start the Web UI management interface')
93
+ .description('Start the Web UI and gateway (default: both; use --no-gateway for UI only)')
22
94
  .option('-p, --port <port>', 'Port for Web UI', '9527')
95
+ .option('-g, --gateway-port <port>', 'Port for gateway (default: from config or 1357)')
96
+ .option('--no-gateway', 'Only start Web UI, do not start gateway')
97
+ .option('--no-background', 'Run in foreground (attach to terminal)')
23
98
  .action(async (options) => {
24
- const port = parseInt(options.port || '9527');
25
-
26
- console.log(`Starting AI Agent Router Web UI`);
27
- console.log(` Port: ${port}`);
28
- console.log(` Access the UI at: http://localhost:${port}`);
29
- console.log('');
99
+ const uiPort = parseInt(options.port || '9527', 10);
100
+ const startGateway = options.gateway !== false;
101
+ const background = options.background !== false;
102
+ const packageRoot = getPackageRoot();
30
103
 
31
- // Get the package root directory (where package.json is located)
32
- const packageRoot = path.dirname(path.dirname(path.dirname(__dirname)));
104
+ let gatewayPort = 1357;
105
+ if (startGateway) {
106
+ try {
107
+ await getDatabase();
108
+ const portConfig = await getConfig('port');
109
+ gatewayPort = options.gatewayPort
110
+ ? parseInt(options.gatewayPort, 10)
111
+ : parseInt(portConfig?.value || '1357', 10);
112
+ } catch (e) {
113
+ gatewayPort = options.gatewayPort ? parseInt(options.gatewayPort, 10) : 1357;
114
+ }
115
+ }
33
116
 
34
- // Start Web UI using Next.js
35
- // Always use production mode for globally installed package
36
117
  const serverPath = path.join(packageRoot, 'node_modules', 'next', 'dist', 'bin', 'next');
37
- const uiProcess = spawn(process.execPath, [serverPath, 'start', '-p', port.toString()], {
118
+ const gatewayScriptPath = path.join(packageRoot, 'dist', 'src', 'cli', 'gateway-server.js');
119
+
120
+ if (background) {
121
+ ensureAarDir();
122
+ const envBase = { ...process.env, NODE_ENV: 'production' as const };
123
+
124
+ const uiProc: ChildProcess = spawn(process.execPath, [serverPath, 'start', '-p', uiPort.toString()], {
125
+ cwd: packageRoot,
126
+ env: { ...envBase, PORT: uiPort.toString() },
127
+ detached: true,
128
+ stdio: 'ignore',
129
+ });
130
+ uiProc.unref();
131
+ if (uiProc.pid) {
132
+ fs.writeFileSync(UI_PID_FILE, String(uiProc.pid));
133
+ }
134
+
135
+ let gatewayRunning = false;
136
+ if (startGateway && fs.existsSync(gatewayScriptPath)) {
137
+ const gwProc: ChildProcess = spawn(process.execPath, [gatewayScriptPath, '--port', String(gatewayPort)], {
138
+ cwd: packageRoot,
139
+ env: envBase,
140
+ detached: true,
141
+ stdio: 'ignore',
142
+ });
143
+ gwProc.unref();
144
+ if (gwProc.pid) {
145
+ fs.writeFileSync(GATEWAY_PID_FILE, String(gwProc.pid));
146
+ fs.writeFileSync(GATEWAY_PORT_FILE, String(gatewayPort));
147
+ gatewayRunning = true;
148
+ }
149
+ try {
150
+ await getDatabase();
151
+ await setServiceStatus({
152
+ status: 'running',
153
+ port: gatewayPort,
154
+ pid: gwProc.pid ?? null,
155
+ started_at: new Date().toISOString(),
156
+ });
157
+ } catch {
158
+ // ignore db errors
159
+ }
160
+ }
161
+
162
+ printStatus(uiPort, gatewayPort, gatewayRunning, true);
163
+ process.exit(0);
164
+ }
165
+
166
+ // Foreground: start UI first
167
+ console.log('Starting AI Agent Router...');
168
+ const uiProcess = spawn(process.execPath, [serverPath, 'start', '-p', uiPort.toString()], {
38
169
  cwd: packageRoot,
39
170
  stdio: ['ignore', 'inherit', 'inherit'],
40
- env: { ...process.env, PORT: port.toString(), NODE_ENV: 'production' },
41
- });
42
-
43
- // Handle UI process exit
44
- uiProcess.on('exit', (code) => {
45
- console.log(`Web UI process exited with code ${code}`);
46
- process.exit(code || 0);
171
+ env: { ...process.env, PORT: uiPort.toString(), NODE_ENV: 'production' },
47
172
  });
48
173
 
49
- uiProcess.on('error', (error) => {
174
+ uiProcess.on('error', (error: Error) => {
50
175
  console.error(`Failed to start Web UI: ${error.message}`);
51
176
  process.exit(1);
52
177
  });
53
178
 
54
- // Keep the process alive
55
- process.on('SIGINT', () => {
56
- console.log('\nShutting down...');
179
+ const gatewayProcess: ChildProcess | null = startGateway && fs.existsSync(gatewayScriptPath)
180
+ ? spawn(process.execPath, [gatewayScriptPath, '--port', String(gatewayPort)], {
181
+ cwd: packageRoot,
182
+ stdio: ['ignore', 'pipe', 'pipe'],
183
+ env: { ...process.env, NODE_ENV: 'production' },
184
+ })
185
+ : null;
186
+
187
+ if (gatewayProcess?.stdout) {
188
+ gatewayProcess.stdout.on('data', (d) => process.stdout.write(d));
189
+ }
190
+ if (gatewayProcess?.stderr) {
191
+ gatewayProcess.stderr.on('data', (d) => process.stderr.write(d));
192
+ }
193
+
194
+ const ready = await waitForReady(`http://localhost:${uiPort}`);
195
+ if (ready) {
196
+ printStatus(uiPort, gatewayPort, !!gatewayProcess, false);
197
+ }
198
+
199
+ const shutdown = (signal: string) => {
200
+ console.log(`\nShutting down (${signal})...`);
201
+ if (gatewayProcess?.pid) {
202
+ try {
203
+ process.kill(gatewayProcess.pid, 'SIGTERM');
204
+ } catch {
205
+ // ignore
206
+ }
207
+ }
57
208
  uiProcess.kill('SIGTERM');
58
209
  process.exit(0);
210
+ };
211
+
212
+ process.on('SIGINT', () => shutdown('SIGINT'));
213
+ process.on('SIGTERM', () => shutdown('SIGTERM'));
214
+
215
+ uiProcess.on('exit', (code) => {
216
+ if (gatewayProcess?.pid) {
217
+ try {
218
+ process.kill(gatewayProcess.pid, 'SIGTERM');
219
+ } catch {
220
+ // ignore
221
+ }
222
+ }
223
+ process.exit(code ?? 0);
59
224
  });
60
225
  });
61
226
 
227
+ program
228
+ .command('stop')
229
+ .description('Stop AI Agent Router when running in background')
230
+ .action(async () => {
231
+ ensureAarDir();
232
+ let stopped = 0;
233
+ const killPidFile = (file: string, name: string) => {
234
+ if (!fs.existsSync(file)) return;
235
+ try {
236
+ const pid = parseInt(fs.readFileSync(file, 'utf8').trim(), 10);
237
+ if (!isNaN(pid)) {
238
+ try {
239
+ process.kill(pid, 'SIGTERM');
240
+ console.log(`Stopped ${name} (PID ${pid})`);
241
+ stopped++;
242
+ } catch (e: any) {
243
+ if (e?.code !== 'ESRCH') console.warn(`${name} (PID ${pid}): ${e.message}`);
244
+ }
245
+ }
246
+ fs.unlinkSync(file);
247
+ } catch (e) {
248
+ // ignore
249
+ }
250
+ };
251
+ killPidFile(GATEWAY_PID_FILE, 'gateway');
252
+ killPidFile(UI_PID_FILE, 'Web UI');
253
+ if (fs.existsSync(GATEWAY_PORT_FILE)) fs.unlinkSync(GATEWAY_PORT_FILE);
254
+ try {
255
+ await getDatabase();
256
+ await updateServiceStatus({ status: 'stopped', pid: null });
257
+ } catch {
258
+ // ignore
259
+ }
260
+ if (stopped === 0) {
261
+ console.log('No background processes found (or already stopped).');
262
+ }
263
+ });
264
+
62
265
  program
63
266
  .command('config')
64
267
  .description('Manage gateway configuration')
@@ -6,15 +6,59 @@ import os from 'os';
6
6
 
7
7
  const DB_PATH = process.env.DB_PATH || path.join(os.homedir(), '.aar', 'gateway.db');
8
8
 
9
+ // Use hardcoded path for the built/distributed WASM file
10
+ // Next.js copies sql-wasm.wasm to its chunks directory
11
+ function findSqlJsWasmPath(): string {
12
+ // Try Next.js chunks directory first (works in dev and production)
13
+ const nextChunksPath = path.join(process.cwd(), '.next', 'dev', 'server', 'chunks', 'sql-wasm.wasm');
14
+ if (fs.existsSync(nextChunksPath)) {
15
+ return nextChunksPath;
16
+ }
17
+
18
+ // Try production .next directory
19
+ const prodChunksPath = path.join(process.cwd(), '.next', 'server', 'chunks', 'sql-wasm.wasm');
20
+ if (fs.existsSync(prodChunksPath)) {
21
+ return prodChunksPath;
22
+ }
23
+
24
+ // Try node_modules as fallback
25
+ const nodeModulesPath = path.join(process.cwd(), 'node_modules', 'sql.js', 'dist', 'sql-wasm.wasm');
26
+ if (fs.existsSync(nodeModulesPath)) {
27
+ return nodeModulesPath;
28
+ }
29
+
30
+ // Use the most likely location as default
31
+ return nodeModulesPath;
32
+ }
33
+
34
+ const SQLJS_WASM_PATH = findSqlJsWasmPath();
35
+
9
36
  let dbInstance: Database | null = null;
10
37
  let sqlJsInstance: any = null;
38
+ let lastLoadMtimeMs: number = 0;
39
+
40
+ function reloadFromFileIfNewer(): void {
41
+ if (!dbInstance || !fs.existsSync(DB_PATH)) return;
42
+ const mtime = fs.statSync(DB_PATH).mtimeMs;
43
+ if (mtime <= lastLoadMtimeMs) return;
44
+ try {
45
+ const fresh = loadDatabase();
46
+ if (fresh) {
47
+ dbInstance.close();
48
+ dbInstance = fresh;
49
+ lastLoadMtimeMs = fs.statSync(DB_PATH).mtimeMs;
50
+ }
51
+ } catch (e) {
52
+ console.warn('[Database] Reload from file failed:', (e as Error).message);
53
+ }
54
+ }
11
55
 
12
56
  async function initSqlJsEngine(): Promise<any> {
13
57
  if (!sqlJsInstance) {
14
58
  sqlJsInstance = await initSqlJs({
15
59
  locateFile: (file: string) => {
16
60
  if (file.endsWith('.wasm')) {
17
- return path.join(process.cwd(), 'node_modules', 'sql.js', 'dist', file);
61
+ return SQLJS_WASM_PATH;
18
62
  }
19
63
  return `https://sql.js.org/dist/${file}`;
20
64
  }
@@ -32,6 +76,9 @@ function saveDatabase(db: Database): void {
32
76
  fs.mkdirSync(dbDir, { recursive: true });
33
77
  }
34
78
  fs.writeFileSync(DB_PATH, buffer);
79
+ if (fs.existsSync(DB_PATH)) {
80
+ lastLoadMtimeMs = fs.statSync(DB_PATH).mtimeMs;
81
+ }
35
82
  } catch (error) {
36
83
  console.error('Failed to save database:', error);
37
84
  }
@@ -53,7 +100,14 @@ export async function getDatabase(): Promise<Database> {
53
100
  if (!dbInstance) {
54
101
  const engine = await initSqlJsEngine();
55
102
 
56
- dbInstance = loadDatabase() || new engine.Database();
103
+ const loaded = loadDatabase();
104
+ if (loaded) {
105
+ dbInstance = loaded;
106
+ if (fs.existsSync(DB_PATH)) lastLoadMtimeMs = fs.statSync(DB_PATH).mtimeMs;
107
+ } else {
108
+ dbInstance = new engine.Database();
109
+ lastLoadMtimeMs = 0;
110
+ }
57
111
 
58
112
  try {
59
113
  dbInstance!.run(CREATE_TABLES_SQL);
@@ -112,9 +166,10 @@ export async function getDatabase(): Promise<Database> {
112
166
  }
113
167
  }
114
168
  }
115
-
116
- return dbInstance!;
117
- }
169
+
170
+ reloadFromFileIfNewer();
171
+ return dbInstance!;
172
+ }
118
173
 
119
174
  export async function closeDatabase(): Promise<void> {
120
175
  if (dbInstance) {
@@ -128,7 +183,6 @@ export function getDbPath(): string {
128
183
  return DB_PATH;
129
184
  }
130
185
 
131
- // Helper to run a query and get single result
132
186
  export async function queryOne<T>(sql: string, params: any[] = []): Promise<T | null> {
133
187
  const db = await getDatabase();
134
188
  const stmt = db.prepare(sql);
@@ -142,7 +196,6 @@ export async function queryOne<T>(sql: string, params: any[] = []): Promise<T |
142
196
  return null;
143
197
  }
144
198
 
145
- // Helper to run a query and get all results
146
199
  export async function queryAll<T>(sql: string, params: any[] = []): Promise<T[]> {
147
200
  const db = await getDatabase();
148
201
  const results: T[] = [];
@@ -155,7 +208,6 @@ export async function queryAll<T>(sql: string, params: any[] = []): Promise<T[]>
155
208
  return results;
156
209
  }
157
210
 
158
- // Helper to run INSERT/UPDATE/DELETE
159
211
  export async function run(sql: string, params: any[] = []): Promise<{ lastInsertRowid: number; changes: number }> {
160
212
  const db = await getDatabase();
161
213
  db.run(sql, params);
package/src/db/queries.ts CHANGED
@@ -157,10 +157,10 @@ export async function createRequestLog(log: Omit<RequestLog, 'id' | 'created_at'
157
157
  const result = await run(
158
158
  `INSERT INTO request_logs (
159
159
  model_id, request_method, request_path, request_headers,
160
- request_query, request_body, response_status, response_body, response_time_ms
161
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
160
+ request_query, request_body, response_status, response_body, response_time_ms, created_at
161
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now', 'localtime'))`,
162
162
  [
163
- log.model_id || null,
163
+ log.model_id === undefined ? null : log.model_id,
164
164
  log.request_method,
165
165
  log.request_path,
166
166
  log.request_headers,
@@ -182,10 +182,10 @@ export async function createRequestLog(log: Omit<RequestLog, 'id' | 'created_at'
182
182
  const result = await run(
183
183
  `INSERT INTO request_logs (
184
184
  model_id, request_method, request_path, request_headers,
185
- request_query, request_body, response_status, response_body, response_time_ms
186
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
185
+ request_query, request_body, response_status, response_body, response_time_ms, created_at
186
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now', 'localtime'))`,
187
187
  [
188
- null,
188
+ log.model_id === undefined ? null : log.model_id,
189
189
  log.request_method,
190
190
  log.request_path,
191
191
  log.request_headers,
@@ -1,6 +1,21 @@
1
1
  import { createRequestLog } from '../db/queries';
2
2
  import { maskApiKey, maskToken } from './crypto';
3
3
 
4
+ /** Safe JSON.stringify that never throws; use for logging arbitrary response/request bodies. */
5
+ function safeStringify(value: any): string {
6
+ if (value === undefined) return '';
7
+ if (value === null) return 'null';
8
+ try {
9
+ return JSON.stringify(value);
10
+ } catch {
11
+ try {
12
+ return String(value);
13
+ } catch {
14
+ return '[Non-serializable]';
15
+ }
16
+ }
17
+ }
18
+
4
19
  export interface LogRequest {
5
20
  modelId: number | null; // Allow null for gateway requests
6
21
  method: string;
@@ -30,11 +45,11 @@ export async function logRequest(
30
45
  model_id: request.modelId,
31
46
  request_method: request.method,
32
47
  request_path: request.path,
33
- request_headers: JSON.stringify(maskedHeaders),
34
- request_query: JSON.stringify(request.query),
35
- request_body: JSON.stringify(maskedBody),
48
+ request_headers: safeStringify(maskedHeaders),
49
+ request_query: safeStringify(request.query),
50
+ request_body: safeStringify(maskedBody),
36
51
  response_status: response.status,
37
- response_body: JSON.stringify(response.body),
52
+ response_body: safeStringify(response.body),
38
53
  response_time_ms: response.responseTimeMs,
39
54
  });
40
55
  } catch (error) {
@@ -1 +0,0 @@
1
- *,:before,:after,::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border:0 solid #e5e7eb}:before,:after{--tw-content:""}html,:host{-webkit-text-size-adjust:100%;tab-size:4;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}body{line-height:inherit;margin:0}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-feature-settings:normal;font-variation-settings:normal;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-feature-settings:inherit;font-variation-settings:inherit;font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:#0000;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{margin:0;padding:0;list-style:none}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder{opacity:1;color:#9ca3af}textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.pointer-events-none{pointer-events:none}.invisible{visibility:hidden}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.bottom-0{bottom:0}.left-1\/2{left:50%}.left-2\.5{left:.625rem}.left-3{left:.75rem}.left-4{left:1rem}.left-9{left:2.25rem}.right-2{right:.5rem}.right-2\.5{right:.625rem}.right-4{right:1rem}.top-0{top:0}.top-1\/2{top:50%}.top-20{top:5rem}.z-10{z-index:10}.z-50{z-index:50}.mx-auto{margin-left:auto;margin-right:auto}.-ml-1{margin-left:-.25rem}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-10{margin-bottom:2.5rem}.mb-2{margin-bottom:.5rem}.mb-2\.5{margin-bottom:.625rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.ml-1{margin-left:.25rem}.ml-1\.5{margin-left:.375rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.mr-1{margin-right:.25rem}.mr-1\.5{margin-right:.375rem}.mr-2{margin-right:.5rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-2\.5{margin-top:.625rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.mt-8{margin-top:2rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.contents{display:contents}.hidden{display:none}.h-10{height:2.5rem}.h-16{height:4rem}.h-2\.5{height:.625rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-\[2px\]{height:2px}.max-h-96{max-height:24rem}.max-h-\[240px\]{max-height:240px}.max-h-\[60vh\]{max-height:60vh}.min-h-screen{min-height:100vh}.w-10{width:2.5rem}.w-2\.5{width:.625rem}.w-28{width:7rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-64{width:16rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-\[68px\]{width:68px}.w-full{width:100%}.min-w-full{min-width:100%}.max-w-3xl{max-width:48rem}.max-w-6xl{max-width:72rem}.max-w-7xl{max-width:80rem}.max-w-xs{max-width:20rem}.flex-1{flex:1}.flex-shrink-0{flex-shrink:0}.-translate-x-1\/2{--tw-translate-x:-50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y))rotate(var(--tw-rotate))skewX(var(--tw-skew-x))skewY(var(--tw-skew-y))scaleX(var(--tw-scale-x))scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y:-50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y))rotate(var(--tw-rotate))skewX(var(--tw-skew-x))skewY(var(--tw-skew-y))scaleX(var(--tw-scale-x))scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y))rotate(var(--tw-rotate))skewX(var(--tw-skew-x))skewY(var(--tw-skew-y))scaleX(var(--tw-scale-x))scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:1s linear infinite spin}.cursor-pointer{cursor:pointer}.list-none{list-style-type:none}.appearance-none{appearance:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-2\.5{gap:.625rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.25rem*var(--tw-space-x-reverse));margin-left:calc(.25rem*calc(1 - var(--tw-space-x-reverse)))}.space-x-12>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(3rem*var(--tw-space-x-reverse));margin-left:calc(3rem*calc(1 - var(--tw-space-x-reverse)))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem*var(--tw-space-x-reverse));margin-left:calc(.5rem*calc(1 - var(--tw-space-x-reverse)))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.75rem*var(--tw-space-x-reverse));margin-left:calc(.75rem*calc(1 - var(--tw-space-x-reverse)))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.375rem*calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem*var(--tw-space-y-reverse))}.space-y-3\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.875rem*calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.875rem*var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem*calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.5rem*calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem*var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-slate-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(241 245 249/var(--tw-divide-opacity,1))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.whitespace-nowrap{white-space:nowrap}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-amber-200\/50{border-color:#fde68a80}.border-amber-200\/60{border-color:#fde68a99}.border-amber-200\/80{border-color:#fde68acc}.border-emerald-100\/30{border-color:#d1fae54d}.border-emerald-100\/50{border-color:#d1fae580}.border-emerald-200\/50{border-color:#a7f3d080}.border-emerald-200\/60{border-color:#a7f3d099}.border-emerald-200\/80{border-color:#a7f3d0cc}.border-emerald-600\/30{border-color:#0596694d}.border-indigo-200{--tw-border-opacity:1;border-color:rgb(199 210 254/var(--tw-border-opacity,1))}.border-indigo-200\/80{border-color:#c7d2fecc}.border-rose-200{--tw-border-opacity:1;border-color:rgb(254 205 211/var(--tw-border-opacity,1))}.border-rose-200\/50{border-color:#fecdd380}.border-rose-200\/60{border-color:#fecdd399}.border-rose-600\/30{border-color:#e11d484d}.border-sky-200\/50{border-color:#bae6fd80}.border-slate-100{--tw-border-opacity:1;border-color:rgb(241 245 249/var(--tw-border-opacity,1))}.border-slate-200{--tw-border-opacity:1;border-color:rgb(226 232 240/var(--tw-border-opacity,1))}.border-slate-200\/40{border-color:#e2e8f066}.border-slate-200\/50{border-color:#e2e8f080}.border-slate-200\/80{border-color:#e2e8f0cc}.border-slate-300{--tw-border-opacity:1;border-color:rgb(203 213 225/var(--tw-border-opacity,1))}.border-slate-700\/30{border-color:#3341554d}.border-slate-800{--tw-border-opacity:1;border-color:rgb(30 41 59/var(--tw-border-opacity,1))}.border-slate-900{--tw-border-opacity:1;border-color:rgb(15 23 42/var(--tw-border-opacity,1))}.border-transparent{border-color:#0000}.border-white\/40{border-color:#fff6}.border-t-slate-900{--tw-border-opacity:1;border-top-color:rgb(15 23 42/var(--tw-border-opacity,1))}.bg-amber-100\/80{background-color:#fef3c7cc}.bg-amber-50{--tw-bg-opacity:1;background-color:rgb(255 251 235/var(--tw-bg-opacity,1))}.bg-amber-50\/80{background-color:#fffbebcc}.bg-amber-500{--tw-bg-opacity:1;background-color:rgb(245 158 11/var(--tw-bg-opacity,1))}.bg-blue-600{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.bg-emerald-100{--tw-bg-opacity:1;background-color:rgb(209 250 229/var(--tw-bg-opacity,1))}.bg-emerald-100\/50{background-color:#d1fae580}.bg-emerald-100\/80{background-color:#d1fae5cc}.bg-emerald-50{--tw-bg-opacity:1;background-color:rgb(236 253 245/var(--tw-bg-opacity,1))}.bg-emerald-50\/20{background-color:#ecfdf533}.bg-emerald-50\/30{background-color:#ecfdf54d}.bg-emerald-50\/60{background-color:#ecfdf599}.bg-emerald-50\/80{background-color:#ecfdf5cc}.bg-emerald-500{--tw-bg-opacity:1;background-color:rgb(16 185 129/var(--tw-bg-opacity,1))}.bg-emerald-600{--tw-bg-opacity:1;background-color:rgb(5 150 105/var(--tw-bg-opacity,1))}.bg-indigo-100{--tw-bg-opacity:1;background-color:rgb(224 231 255/var(--tw-bg-opacity,1))}.bg-indigo-50{--tw-bg-opacity:1;background-color:rgb(238 242 255/var(--tw-bg-opacity,1))}.bg-rose-100\/50{background-color:#ffe4e680}.bg-rose-100\/80{background-color:#ffe4e6cc}.bg-rose-50\/60{background-color:#fff1f299}.bg-rose-500{--tw-bg-opacity:1;background-color:rgb(244 63 94/var(--tw-bg-opacity,1))}.bg-rose-600{--tw-bg-opacity:1;background-color:rgb(225 29 72/var(--tw-bg-opacity,1))}.bg-sky-50{--tw-bg-opacity:1;background-color:rgb(240 249 255/var(--tw-bg-opacity,1))}.bg-slate-100{--tw-bg-opacity:1;background-color:rgb(241 245 249/var(--tw-bg-opacity,1))}.bg-slate-100\/60{background-color:#f1f5f999}.bg-slate-100\/80{background-color:#f1f5f9cc}.bg-slate-50{--tw-bg-opacity:1;background-color:rgb(248 250 252/var(--tw-bg-opacity,1))}.bg-slate-50\/60{background-color:#f8fafc99}.bg-slate-50\/80{background-color:#f8fafccc}.bg-slate-600{--tw-bg-opacity:1;background-color:rgb(71 85 105/var(--tw-bg-opacity,1))}.bg-slate-900{--tw-bg-opacity:1;background-color:rgb(15 23 42/var(--tw-bg-opacity,1))}.bg-slate-900\/40{background-color:#0f172a66}.bg-slate-900\/95{background-color:#0f172af2}.bg-slate-950{--tw-bg-opacity:1;background-color:rgb(2 6 23/var(--tw-bg-opacity,1))}.bg-transparent{background-color:#0000}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.bg-white\/40{background-color:#fff6}.bg-white\/60{background-color:#fff9}.bg-white\/70{background-color:#ffffffb3}.bg-white\/80{background-color:#fffc}.bg-white\/90{background-color:#ffffffe6}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.from-emerald-50\/30{--tw-gradient-from:#ecfdf54d var(--tw-gradient-from-position);--tw-gradient-to:#ecfdf500 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-white\/50{--tw-gradient-from:#ffffff80 var(--tw-gradient-from-position);--tw-gradient-to:#fff0 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.via-white{--tw-gradient-to:#fff0 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#fff var(--tw-gradient-via-position),var(--tw-gradient-to)}.to-slate-50\/50{--tw-gradient-to:#f8fafc80 var(--tw-gradient-to-position)}.to-transparent{--tw-gradient-to:transparent var(--tw-gradient-to-position)}.p-0\.5{padding:.125rem}.p-1{padding:.25rem}.p-3{padding:.75rem}.p-3\.5{padding:.875rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-3\.5{padding-left:.875rem;padding-right:.875rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-3\.5{padding-top:.875rem;padding-bottom:.875rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.pb-20{padding-bottom:5rem}.pb-4{padding-bottom:1rem}.pl-9{padding-left:2.25rem}.pr-20{padding-right:5rem}.pr-3{padding-right:.75rem}.pr-7{padding-right:1.75rem}.pr-9{padding-right:2.25rem}.pt-1{padding-top:.25rem}.pt-3{padding-top:.75rem}.pt-4{padding-top:1rem}.pt-5{padding-top:1.25rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-bottom{vertical-align:bottom}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[9px\]{font-size:9px}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.leading-4{line-height:1rem}.leading-none{line-height:1}.leading-tight{line-height:1.25}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.text-amber-700{--tw-text-opacity:1;color:rgb(180 83 9/var(--tw-text-opacity,1))}.text-blue-600{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity,1))}.text-emerald-500{--tw-text-opacity:1;color:rgb(16 185 129/var(--tw-text-opacity,1))}.text-emerald-600{--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1))}.text-emerald-700{--tw-text-opacity:1;color:rgb(4 120 87/var(--tw-text-opacity,1))}.text-emerald-700\/80{color:#047857cc}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity,1))}.text-green-600{--tw-text-opacity:1;color:rgb(22 163 74/var(--tw-text-opacity,1))}.text-indigo-700{--tw-text-opacity:1;color:rgb(67 56 202/var(--tw-text-opacity,1))}.text-red-600{--tw-text-opacity:1;color:rgb(220 38 38/var(--tw-text-opacity,1))}.text-rose-500{--tw-text-opacity:1;color:rgb(244 63 94/var(--tw-text-opacity,1))}.text-rose-700{--tw-text-opacity:1;color:rgb(190 18 60/var(--tw-text-opacity,1))}.text-rose-700\/80{color:#be123ccc}.text-sky-700{--tw-text-opacity:1;color:rgb(3 105 161/var(--tw-text-opacity,1))}.text-slate-200{--tw-text-opacity:1;color:rgb(226 232 240/var(--tw-text-opacity,1))}.text-slate-300{--tw-text-opacity:1;color:rgb(203 213 225/var(--tw-text-opacity,1))}.text-slate-400{--tw-text-opacity:1;color:rgb(148 163 184/var(--tw-text-opacity,1))}.text-slate-500{--tw-text-opacity:1;color:rgb(100 116 139/var(--tw-text-opacity,1))}.text-slate-600{--tw-text-opacity:1;color:rgb(71 85 105/var(--tw-text-opacity,1))}.text-slate-700{--tw-text-opacity:1;color:rgb(51 65 85/var(--tw-text-opacity,1))}.text-slate-800{--tw-text-opacity:1;color:rgb(30 41 59/var(--tw-text-opacity,1))}.text-slate-900{--tw-text-opacity:1;color:rgb(15 23 42/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.text-yellow-600{--tw-text-opacity:1;color:rgb(202 138 4/var(--tw-text-opacity,1))}.underline{text-decoration-line:underline}.opacity-0{opacity:0}.opacity-25{opacity:.25}.opacity-70{opacity:.7}.opacity-75{opacity:.75}.shadow-lg{--tw-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px #0000001a,0 8px 10px -6px #0000001a;--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-emerald-500\/20{--tw-shadow-color:#10b98133;--tw-shadow:var(--tw-shadow-colored)}.shadow-rose-500\/20{--tw-shadow-color:#f43f5e33;--tw-shadow:var(--tw-shadow-colored)}.shadow-slate-600\/20{--tw-shadow-color:#47556933;--tw-shadow:var(--tw-shadow-colored)}.filter{filter:var(--tw-blur)var(--tw-brightness)var(--tw-contrast)var(--tw-grayscale)var(--tw-hue-rotate)var(--tw-invert)var(--tw-saturate)var(--tw-sepia)var(--tw-drop-shadow)}.backdrop-blur-md{--tw-backdrop-blur:blur(12px);-webkit-backdrop-filter:var(--tw-backdrop-blur)var(--tw-backdrop-brightness)var(--tw-backdrop-contrast)var(--tw-backdrop-grayscale)var(--tw-backdrop-hue-rotate)var(--tw-backdrop-invert)var(--tw-backdrop-opacity)var(--tw-backdrop-saturate)var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur)var(--tw-backdrop-brightness)var(--tw-backdrop-contrast)var(--tw-backdrop-grayscale)var(--tw-backdrop-hue-rotate)var(--tw-backdrop-invert)var(--tw-backdrop-opacity)var(--tw-backdrop-saturate)var(--tw-backdrop-sepia)}.backdrop-blur-sm{--tw-backdrop-blur:blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur)var(--tw-backdrop-brightness)var(--tw-backdrop-contrast)var(--tw-backdrop-grayscale)var(--tw-backdrop-hue-rotate)var(--tw-backdrop-invert)var(--tw-backdrop-opacity)var(--tw-backdrop-saturate)var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur)var(--tw-backdrop-brightness)var(--tw-backdrop-contrast)var(--tw-backdrop-grayscale)var(--tw-backdrop-hue-rotate)var(--tw-backdrop-invert)var(--tw-backdrop-opacity)var(--tw-backdrop-saturate)var(--tw-backdrop-sepia)}.backdrop-blur-xl{--tw-backdrop-blur:blur(24px);-webkit-backdrop-filter:var(--tw-backdrop-blur)var(--tw-backdrop-brightness)var(--tw-backdrop-contrast)var(--tw-backdrop-grayscale)var(--tw-backdrop-hue-rotate)var(--tw-backdrop-invert)var(--tw-backdrop-opacity)var(--tw-backdrop-saturate)var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur)var(--tw-backdrop-brightness)var(--tw-backdrop-contrast)var(--tw-backdrop-grayscale)var(--tw-backdrop-hue-rotate)var(--tw-backdrop-invert)var(--tw-backdrop-opacity)var(--tw-backdrop-saturate)var(--tw-backdrop-sepia)}.transition-all{transition-property:all;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-opacity{transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-transform{transition-property:transform;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}:root{--background:#fafbf9;--foreground:#2d3436;--emerald-50:#ecfdf5;--emerald-100:#d1fae5;--emerald-500:#10b981;--emerald-700:#047857;--slate-50:#f8fafc;--slate-100:#f1f5f9;--slate-400:#94a3b8;--slate-500:#64748b;--slate-600:#475569;--slate-700:#334155;--slate-800:#1e293b}body{color:var(--foreground);background:var(--background);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;letter-spacing:-.01em;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif}input,select,textarea{transition-property:all;transition-duration:.3s;transition-timing-function:cubic-bezier(.4,0,.2,1)}input:focus,select:focus,textarea:focus{outline-offset:2px;--tw-ring-offset-shadow:var(--tw-ring-inset)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-color:#34d39933;--tw-ring-offset-width:2px;--tw-ring-offset-color:#fff;outline:2px solid #0000}button{transition-property:all;transition-duration:.3s;transition-timing-function:cubic-bezier(.4,0,.2,1)}button:disabled{cursor:not-allowed;opacity:.5}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:#f1f5f9}::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:4px}::-webkit-scrollbar-thumb:hover{background:#94a3b8}.last\:border-0:last-child{border-width:0}.hover\:border-emerald-300:hover{--tw-border-opacity:1;border-color:rgb(110 231 183/var(--tw-border-opacity,1))}.hover\:border-slate-300:hover{--tw-border-opacity:1;border-color:rgb(203 213 225/var(--tw-border-opacity,1))}.hover\:border-slate-300\/60:hover{border-color:#cbd5e199}.hover\:border-slate-300\/80:hover{border-color:#cbd5e1cc}.hover\:bg-amber-100:hover{--tw-bg-opacity:1;background-color:rgb(254 243 199/var(--tw-bg-opacity,1))}.hover\:bg-amber-600:hover{--tw-bg-opacity:1;background-color:rgb(217 119 6/var(--tw-bg-opacity,1))}.hover\:bg-blue-700:hover{--tw-bg-opacity:1;background-color:rgb(29 78 216/var(--tw-bg-opacity,1))}.hover\:bg-emerald-100:hover{--tw-bg-opacity:1;background-color:rgb(209 250 229/var(--tw-bg-opacity,1))}.hover\:bg-emerald-50:hover{--tw-bg-opacity:1;background-color:rgb(236 253 245/var(--tw-bg-opacity,1))}.hover\:bg-emerald-50\/20:hover{background-color:#ecfdf533}.hover\:bg-emerald-50\/50:hover{background-color:#ecfdf580}.hover\:bg-emerald-700:hover{--tw-bg-opacity:1;background-color:rgb(4 120 87/var(--tw-bg-opacity,1))}.hover\:bg-indigo-100:hover{--tw-bg-opacity:1;background-color:rgb(224 231 255/var(--tw-bg-opacity,1))}.hover\:bg-rose-50:hover{--tw-bg-opacity:1;background-color:rgb(255 241 242/var(--tw-bg-opacity,1))}.hover\:bg-rose-600:hover{--tw-bg-opacity:1;background-color:rgb(225 29 72/var(--tw-bg-opacity,1))}.hover\:bg-rose-700:hover{--tw-bg-opacity:1;background-color:rgb(190 18 60/var(--tw-bg-opacity,1))}.hover\:bg-slate-50:hover{--tw-bg-opacity:1;background-color:rgb(248 250 252/var(--tw-bg-opacity,1))}.hover\:bg-slate-700:hover{--tw-bg-opacity:1;background-color:rgb(51 65 85/var(--tw-bg-opacity,1))}.hover\:bg-slate-800:hover{--tw-bg-opacity:1;background-color:rgb(30 41 59/var(--tw-bg-opacity,1))}.hover\:bg-white:hover{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.hover\:from-slate-50\/60:hover{--tw-gradient-from:#f8fafc99 var(--tw-gradient-from-position);--tw-gradient-to:#f8fafc00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.hover\:text-blue-700:hover{--tw-text-opacity:1;color:rgb(29 78 216/var(--tw-text-opacity,1))}.hover\:text-emerald-600:hover{--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1))}.hover\:text-emerald-700:hover{--tw-text-opacity:1;color:rgb(4 120 87/var(--tw-text-opacity,1))}.hover\:text-rose-500:hover{--tw-text-opacity:1;color:rgb(244 63 94/var(--tw-text-opacity,1))}.hover\:text-rose-600:hover{--tw-text-opacity:1;color:rgb(225 29 72/var(--tw-text-opacity,1))}.hover\:text-slate-600:hover{--tw-text-opacity:1;color:rgb(71 85 105/var(--tw-text-opacity,1))}.hover\:text-slate-700:hover{--tw-text-opacity:1;color:rgb(51 65 85/var(--tw-text-opacity,1))}.hover\:opacity-100:hover{opacity:1}.hover\:shadow-sm:hover{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.focus\:border-emerald-400:focus{--tw-border-opacity:1;border-color:rgb(52 211 153/var(--tw-border-opacity,1))}.focus\:border-emerald-500:focus{--tw-border-opacity:1;border-color:rgb(16 185 129/var(--tw-border-opacity,1))}.focus\:border-slate-400\/80:focus{border-color:#94a3b8cc}.focus\:outline-none:focus{outline-offset:2px;outline:2px solid #0000}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-amber-500\/50:focus{--tw-ring-color:#f59e0b80}.focus\:ring-blue-500\/50:focus{--tw-ring-color:#3b82f680}.focus\:ring-emerald-400\/20:focus{--tw-ring-color:#34d39933}.focus\:ring-emerald-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(16 185 129/var(--tw-ring-opacity,1))}.focus\:ring-emerald-500\/30:focus{--tw-ring-color:#10b9814d}.focus\:ring-emerald-500\/50:focus{--tw-ring-color:#10b98180}.focus\:ring-rose-500\/50:focus{--tw-ring-color:#f43f5e80}.focus\:ring-slate-200\/50:focus{--tw-ring-color:#e2e8f080}.focus\:ring-slate-400\/30:focus{--tw-ring-color:#94a3b84d}.focus\:ring-offset-1:focus{--tw-ring-offset-width:1px}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.group[open] .group-open\:rotate-90{--tw-rotate:90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y))rotate(var(--tw-rotate))skewX(var(--tw-skew-x))skewY(var(--tw-skew-y))scaleX(var(--tw-scale-x))scaleY(var(--tw-scale-y))}.group\/tip:hover .group-hover\/tip\:visible{visibility:visible}.group:hover .group-hover\:bg-slate-50\/40{background-color:#f8fafc66}.group:hover .group-hover\:text-emerald-600{--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1))}.group:hover .group-hover\:text-slate-600{--tw-text-opacity:1;color:rgb(71 85 105/var(--tw-text-opacity,1))}.group:hover .group-hover\:text-slate-700{--tw-text-opacity:1;color:rgb(51 65 85/var(--tw-text-opacity,1))}.group\/tip:hover .group-hover\/tip\:opacity-100{opacity:1}@media (min-width:640px){.sm\:my-8{margin-top:2rem;margin-bottom:2rem}.sm\:block{display:block}.sm\:flex{display:flex}.sm\:w-full{width:100%}.sm\:max-w-3xl{max-width:48rem}.sm\:max-w-md{max-width:28rem}.sm\:max-w-sm{max-width:24rem}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:p-0{padding:0}.sm\:p-6{padding:1.5rem}.sm\:px-0{padding-left:0;padding-right:0}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:px-8{padding-left:2rem;padding-right:2rem}.sm\:align-middle{vertical-align:middle}}@media (min-width:1024px){.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:px-10{padding-left:2.5rem;padding-right:2.5rem}.lg\:px-8{padding-left:2rem;padding-right:2rem}}