@lastbrain/module-ai 2.0.26 → 2.0.30

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 (365) hide show
  1. package/README.md +52 -1
  2. package/dist/ai.build.config.d.ts.map +1 -1
  3. package/dist/ai.build.config.js +508 -9
  4. package/dist/api/admin/ai-provider-models/[id].d.ts +18 -0
  5. package/dist/api/admin/ai-provider-models/[id].d.ts.map +1 -0
  6. package/dist/api/admin/ai-provider-models/[id].js +58 -0
  7. package/dist/api/admin/ai-provider-models.d.ts +20 -0
  8. package/dist/api/admin/ai-provider-models.d.ts.map +1 -0
  9. package/dist/api/admin/ai-provider-models.js +26 -0
  10. package/dist/api/admin/ai-providers/[key].d.ts +18 -0
  11. package/dist/api/admin/ai-providers/[key].d.ts.map +1 -0
  12. package/dist/api/admin/ai-providers/[key].js +55 -0
  13. package/dist/api/admin/ai-providers.d.ts +20 -0
  14. package/dist/api/admin/ai-providers.d.ts.map +1 -0
  15. package/dist/api/admin/ai-providers.js +26 -0
  16. package/dist/api/admin/billing-analytics.d.ts +43 -0
  17. package/dist/api/admin/billing-analytics.d.ts.map +1 -0
  18. package/dist/api/admin/billing-analytics.js +144 -0
  19. package/dist/api/admin/global-ai-settings.d.ts +14 -0
  20. package/dist/api/admin/global-ai-settings.d.ts.map +1 -0
  21. package/dist/api/admin/global-ai-settings.js +63 -0
  22. package/dist/api/admin/token-packs/[id].d.ts.map +1 -1
  23. package/dist/api/admin/token-packs/[id].js +3 -2
  24. package/dist/api/admin/token-packs.d.ts.map +1 -1
  25. package/dist/api/admin/token-packs.js +3 -2
  26. package/dist/api/admin/user-monthly-details.d.ts +49 -0
  27. package/dist/api/admin/user-monthly-details.d.ts.map +1 -0
  28. package/dist/api/admin/user-monthly-details.js +140 -0
  29. package/dist/api/admin/user-quota.d.ts +21 -0
  30. package/dist/api/admin/user-quota.d.ts.map +1 -0
  31. package/dist/api/admin/user-quota.js +59 -0
  32. package/dist/api/admin/user-token/[id].d.ts.map +1 -1
  33. package/dist/api/admin/user-token/[id].js +2 -1
  34. package/dist/api/admin/user-token.d.ts +5 -2
  35. package/dist/api/admin/user-token.d.ts.map +1 -1
  36. package/dist/api/admin/user-token.js +91 -17
  37. package/dist/api/admin/user-usage-by-model.d.ts +22 -0
  38. package/dist/api/admin/user-usage-by-model.d.ts.map +1 -0
  39. package/dist/api/admin/user-usage-by-model.js +78 -0
  40. package/dist/api/admin/user-wallet-analytics.d.ts +15 -0
  41. package/dist/api/admin/user-wallet-analytics.d.ts.map +1 -0
  42. package/dist/api/admin/user-wallet-analytics.js +67 -0
  43. package/dist/api/admin/wallet-repair/route.d.ts +30 -0
  44. package/dist/api/admin/wallet-repair/route.d.ts.map +1 -0
  45. package/dist/api/admin/wallet-repair/route.js +63 -0
  46. package/dist/api/auth/ai-model-settings.d.ts +21 -0
  47. package/dist/api/auth/ai-model-settings.d.ts.map +1 -0
  48. package/dist/api/auth/ai-model-settings.js +86 -0
  49. package/dist/api/auth/ai-settings.d.ts +17 -0
  50. package/dist/api/auth/ai-settings.d.ts.map +1 -0
  51. package/dist/api/auth/ai-settings.js +87 -0
  52. package/dist/api/auth/api-keys/[id].d.ts +17 -0
  53. package/dist/api/auth/api-keys/[id].d.ts.map +1 -0
  54. package/dist/api/auth/api-keys/[id].js +66 -0
  55. package/dist/api/auth/api-keys.d.ts +19 -0
  56. package/dist/api/auth/api-keys.d.ts.map +1 -0
  57. package/dist/api/auth/api-keys.js +94 -0
  58. package/dist/api/auth/create-checkout.d.ts +1 -1
  59. package/dist/api/auth/create-checkout.d.ts.map +1 -1
  60. package/dist/api/auth/create-checkout.js +8 -6
  61. package/dist/api/auth/generate-image.d.ts +2 -2
  62. package/dist/api/auth/generate-image.d.ts.map +1 -1
  63. package/dist/api/auth/generate-image.js +404 -104
  64. package/dist/api/auth/generate-text.d.ts +3 -2
  65. package/dist/api/auth/generate-text.d.ts.map +1 -1
  66. package/dist/api/auth/generate-text.js +130 -58
  67. package/dist/api/auth/process-ocr.d.ts +9 -0
  68. package/dist/api/auth/process-ocr.d.ts.map +1 -0
  69. package/dist/api/auth/process-ocr.js +43 -0
  70. package/dist/api/auth/prompts/stats.d.ts +14 -0
  71. package/dist/api/auth/prompts/stats.d.ts.map +1 -0
  72. package/dist/api/auth/prompts/stats.js +46 -0
  73. package/dist/api/auth/prompts.d.ts +26 -0
  74. package/dist/api/auth/prompts.d.ts.map +1 -0
  75. package/dist/api/auth/prompts.js +175 -0
  76. package/dist/api/auth/token-balance.d.ts +26 -0
  77. package/dist/api/auth/token-balance.d.ts.map +1 -0
  78. package/dist/api/auth/token-balance.js +47 -0
  79. package/dist/api/auth/token-checkout.d.ts.map +1 -1
  80. package/dist/api/auth/token-checkout.js +22 -4
  81. package/dist/api/auth/token-packs.d.ts.map +1 -1
  82. package/dist/api/auth/token-packs.js +2 -1
  83. package/dist/api/auth/usage-by-model.d.ts +25 -0
  84. package/dist/api/auth/usage-by-model.d.ts.map +1 -0
  85. package/dist/api/auth/usage-by-model.js +95 -0
  86. package/dist/api/auth/usage.d.ts +26 -0
  87. package/dist/api/auth/usage.d.ts.map +1 -0
  88. package/dist/api/auth/usage.js +127 -0
  89. package/dist/api/auth/user-tokens.d.ts.map +1 -1
  90. package/dist/api/auth/user-tokens.js +36 -2
  91. package/dist/api/auth/wallet/route.d.ts +17 -0
  92. package/dist/api/auth/wallet/route.d.ts.map +1 -0
  93. package/dist/api/auth/wallet/route.js +68 -0
  94. package/dist/api/auth/wallet.d.ts +16 -0
  95. package/dist/api/auth/wallet.d.ts.map +1 -0
  96. package/dist/api/auth/wallet.js +71 -0
  97. package/dist/api/public/gateway-models.d.ts +25 -0
  98. package/dist/api/public/gateway-models.d.ts.map +1 -0
  99. package/dist/api/public/gateway-models.js +49 -0
  100. package/dist/api/public/pricing-summary.d.ts +46 -0
  101. package/dist/api/public/pricing-summary.d.ts.map +1 -0
  102. package/dist/api/public/pricing-summary.js +70 -0
  103. package/dist/api/public/prompts/[id]/stats.d.ts +15 -0
  104. package/dist/api/public/prompts/[id]/stats.d.ts.map +1 -0
  105. package/dist/api/public/prompts/[id]/stats.js +37 -0
  106. package/dist/api/public/prompts/[id]/view.d.ts +15 -0
  107. package/dist/api/public/prompts/[id]/view.d.ts.map +1 -0
  108. package/dist/api/public/prompts/[id]/view.js +41 -0
  109. package/dist/api/public/prompts/slug/[slug].d.ts +15 -0
  110. package/dist/api/public/prompts/slug/[slug].d.ts.map +1 -0
  111. package/dist/api/public/prompts/slug/[slug].js +40 -0
  112. package/dist/api/public/prompts/user/[username].d.ts +17 -0
  113. package/dist/api/public/prompts/user/[username].d.ts.map +1 -0
  114. package/dist/api/public/prompts/user/[username].js +51 -0
  115. package/dist/api/public/prompts.d.ts +15 -0
  116. package/dist/api/public/prompts.d.ts.map +1 -0
  117. package/dist/api/public/prompts.js +45 -0
  118. package/dist/api/public/token-packs.d.ts +11 -0
  119. package/dist/api/public/token-packs.d.ts.map +1 -0
  120. package/dist/api/public/token-packs.js +25 -0
  121. package/dist/api/public/token-pricing.d.ts +44 -0
  122. package/dist/api/public/token-pricing.d.ts.map +1 -0
  123. package/dist/api/public/token-pricing.js +168 -0
  124. package/dist/api/public/v1/_lib/artifacts.d.ts +52 -0
  125. package/dist/api/public/v1/_lib/artifacts.d.ts.map +1 -0
  126. package/dist/api/public/v1/_lib/artifacts.js +217 -0
  127. package/dist/api/public/v1/_lib/auth.d.ts +43 -0
  128. package/dist/api/public/v1/_lib/auth.d.ts.map +1 -0
  129. package/dist/api/public/v1/_lib/auth.js +108 -0
  130. package/dist/api/public/v1/_lib/errors.d.ts +17 -0
  131. package/dist/api/public/v1/_lib/errors.d.ts.map +1 -0
  132. package/dist/api/public/v1/_lib/errors.js +16 -0
  133. package/dist/api/public/v1/_lib/log.d.ts +29 -0
  134. package/dist/api/public/v1/_lib/log.d.ts.map +1 -0
  135. package/dist/api/public/v1/_lib/log.js +68 -0
  136. package/dist/api/public/v1/_lib/quota.d.ts +24 -0
  137. package/dist/api/public/v1/_lib/quota.d.ts.map +1 -0
  138. package/dist/api/public/v1/_lib/quota.js +118 -0
  139. package/dist/api/public/v1/_lib/router.d.ts +54 -0
  140. package/dist/api/public/v1/_lib/router.d.ts.map +1 -0
  141. package/dist/api/public/v1/_lib/router.js +119 -0
  142. package/dist/api/public/v1/connect.d.ts +20 -0
  143. package/dist/api/public/v1/connect.d.ts.map +1 -0
  144. package/dist/api/public/v1/connect.js +119 -0
  145. package/dist/api/public/v1/doc.d.ts +239 -0
  146. package/dist/api/public/v1/doc.d.ts.map +1 -0
  147. package/dist/api/public/v1/doc.js +253 -0
  148. package/dist/api/public/v1/history/[id].d.ts +92 -0
  149. package/dist/api/public/v1/history/[id].d.ts.map +1 -0
  150. package/dist/api/public/v1/history/[id].js +176 -0
  151. package/dist/api/public/v1/history.d.ts +30 -0
  152. package/dist/api/public/v1/history.d.ts.map +1 -0
  153. package/dist/api/public/v1/history.js +142 -0
  154. package/dist/api/public/v1/image-ai.d.ts +24 -0
  155. package/dist/api/public/v1/image-ai.d.ts.map +1 -0
  156. package/dist/api/public/v1/image-ai.js +233 -0
  157. package/dist/api/public/v1/prompts.d.ts +19 -0
  158. package/dist/api/public/v1/prompts.d.ts.map +1 -0
  159. package/dist/api/public/v1/prompts.js +107 -0
  160. package/dist/api/public/v1/provider.d.ts +16 -0
  161. package/dist/api/public/v1/provider.d.ts.map +1 -0
  162. package/dist/api/public/v1/provider.js +130 -0
  163. package/dist/api/public/v1/purchase.d.ts +11 -0
  164. package/dist/api/public/v1/purchase.d.ts.map +1 -0
  165. package/dist/api/public/v1/purchase.js +18 -0
  166. package/dist/api/public/v1/status.d.ts +35 -0
  167. package/dist/api/public/v1/status.d.ts.map +1 -0
  168. package/dist/api/public/v1/status.js +163 -0
  169. package/dist/api/public/v1/text-ai.d.ts +26 -0
  170. package/dist/api/public/v1/text-ai.d.ts.map +1 -0
  171. package/dist/api/public/v1/text-ai.js +239 -0
  172. package/dist/api/public/webhook.d.ts.map +1 -1
  173. package/dist/api/public/webhook.js +50 -39
  174. package/dist/api/track-usage.d.ts +12 -0
  175. package/dist/api/track-usage.d.ts.map +1 -0
  176. package/dist/api/track-usage.js +37 -0
  177. package/dist/components/Doc.d.ts.map +1 -1
  178. package/dist/components/Doc.js +1 -1
  179. package/dist/components/DocUsageCustom.js +6 -6
  180. package/dist/components/admin/UserTokenTab.d.ts.map +1 -1
  181. package/dist/components/admin/UserTokenTab.js +170 -23
  182. package/dist/components/auth/AuthDashboardAi.d.ts +2 -0
  183. package/dist/components/auth/AuthDashboardAi.d.ts.map +1 -0
  184. package/dist/components/auth/AuthDashboardAi.js +53 -0
  185. package/dist/docs/REFACTORING_BILLING_GUIDE.d.ts +277 -0
  186. package/dist/docs/REFACTORING_BILLING_GUIDE.d.ts.map +1 -0
  187. package/dist/docs/REFACTORING_BILLING_GUIDE.js +276 -0
  188. package/dist/index.d.ts +15 -1
  189. package/dist/index.d.ts.map +1 -1
  190. package/dist/index.js +18 -1
  191. package/dist/scripts/migrate-tokens-to-wallet.d.ts +13 -0
  192. package/dist/scripts/migrate-tokens-to-wallet.d.ts.map +1 -0
  193. package/dist/scripts/migrate-tokens-to-wallet.js +165 -0
  194. package/dist/server/__tests__/billing.test.d.ts +5 -0
  195. package/dist/server/__tests__/billing.test.d.ts.map +1 -0
  196. package/dist/server/__tests__/billing.test.js +223 -0
  197. package/dist/server/ai-client.d.ts +59 -0
  198. package/dist/server/ai-client.d.ts.map +1 -0
  199. package/dist/server/ai-client.js +111 -0
  200. package/dist/server/ai-generation-service.d.ts +66 -0
  201. package/dist/server/ai-generation-service.d.ts.map +1 -0
  202. package/dist/server/ai-generation-service.js +274 -0
  203. package/dist/server/billing.d.ts +200 -0
  204. package/dist/server/billing.d.ts.map +1 -0
  205. package/dist/server/billing.js +488 -0
  206. package/dist/server/gateway-service.d.ts +13 -0
  207. package/dist/server/gateway-service.d.ts.map +1 -0
  208. package/dist/server/gateway-service.js +161 -0
  209. package/dist/server/global-settings.d.ts +16 -0
  210. package/dist/server/global-settings.d.ts.map +1 -0
  211. package/dist/server/global-settings.js +42 -0
  212. package/dist/server/model-filter.d.ts +25 -0
  213. package/dist/server/model-filter.d.ts.map +1 -0
  214. package/dist/server/model-filter.js +240 -0
  215. package/dist/server/ocr.d.ts +39 -0
  216. package/dist/server/ocr.d.ts.map +1 -0
  217. package/dist/server/ocr.js +280 -0
  218. package/dist/server/openai-client.d.ts +19 -0
  219. package/dist/server/openai-client.d.ts.map +1 -0
  220. package/dist/server/openai-client.js +26 -0
  221. package/dist/server/pricing-config.d.ts +18 -0
  222. package/dist/server/pricing-config.d.ts.map +1 -0
  223. package/dist/server/pricing-config.js +94 -0
  224. package/dist/server/pricing-validator.d.ts +41 -0
  225. package/dist/server/pricing-validator.d.ts.map +1 -0
  226. package/dist/server/pricing-validator.js +113 -0
  227. package/dist/server/pricing.d.ts +121 -0
  228. package/dist/server/pricing.d.ts.map +1 -0
  229. package/dist/server/pricing.js +225 -0
  230. package/dist/server/quota.d.ts +66 -0
  231. package/dist/server/quota.d.ts.map +1 -0
  232. package/dist/server/quota.js +538 -0
  233. package/dist/server/wallet-repair.d.ts +32 -0
  234. package/dist/server/wallet-repair.d.ts.map +1 -0
  235. package/dist/server/wallet-repair.js +189 -0
  236. package/dist/server.d.ts +13 -1
  237. package/dist/server.d.ts.map +1 -1
  238. package/dist/server.js +87 -16
  239. package/dist/sitemap/handlers/prompts.d.ts +6 -0
  240. package/dist/sitemap/handlers/prompts.d.ts.map +1 -0
  241. package/dist/sitemap/handlers/prompts.js +72 -0
  242. package/dist/sitemap/handlers/users.d.ts +6 -0
  243. package/dist/sitemap/handlers/users.d.ts.map +1 -0
  244. package/dist/sitemap/handlers/users.js +80 -0
  245. package/dist/sitemap/manifest.d.ts +8 -0
  246. package/dist/sitemap/manifest.d.ts.map +1 -0
  247. package/dist/sitemap/manifest.js +27 -0
  248. package/dist/types/gateway.d.ts +40 -0
  249. package/dist/types/gateway.d.ts.map +1 -0
  250. package/dist/types/gateway.js +4 -0
  251. package/dist/types/quota.d.ts +74 -0
  252. package/dist/types/quota.d.ts.map +1 -0
  253. package/dist/types/quota.js +11 -0
  254. package/dist/utils/date.d.ts +7 -0
  255. package/dist/utils/date.d.ts.map +1 -0
  256. package/dist/utils/date.js +17 -0
  257. package/dist/web/admin/AdminTokenPacksPage.js +1 -1
  258. package/dist/web/admin/BillingAnalyticsPage.d.ts +2 -0
  259. package/dist/web/admin/BillingAnalyticsPage.d.ts.map +1 -0
  260. package/dist/web/admin/BillingAnalyticsPage.js +141 -0
  261. package/dist/web/admin/GlobalAISettingsPage.d.ts +2 -0
  262. package/dist/web/admin/GlobalAISettingsPage.d.ts.map +1 -0
  263. package/dist/web/admin/GlobalAISettingsPage.js +93 -0
  264. package/dist/web/admin/UserTokenPage.d.ts.map +1 -1
  265. package/dist/web/admin/UserTokenPage.js +20 -7
  266. package/dist/web/auth/AISettingsPage.d.ts +2 -0
  267. package/dist/web/auth/AISettingsPage.d.ts.map +1 -0
  268. package/dist/web/auth/AISettingsPage.js +258 -0
  269. package/dist/web/auth/APIKeysPage.d.ts +2 -0
  270. package/dist/web/auth/APIKeysPage.d.ts.map +1 -0
  271. package/dist/web/auth/APIKeysPage.js +154 -0
  272. package/dist/web/auth/HistoryPage.d.ts +2 -0
  273. package/dist/web/auth/HistoryPage.d.ts.map +1 -0
  274. package/dist/web/auth/HistoryPage.js +279 -0
  275. package/dist/web/auth/PromptsPage.d.ts +5 -0
  276. package/dist/web/auth/PromptsPage.d.ts.map +1 -0
  277. package/dist/web/auth/PromptsPage.js +137 -0
  278. package/dist/web/auth/TokenPage.d.ts.map +1 -1
  279. package/dist/web/auth/TokenPage.js +88 -31
  280. package/dist/web/auth/UsageAndTokensPage.d.ts +2 -0
  281. package/dist/web/auth/UsageAndTokensPage.d.ts.map +1 -0
  282. package/dist/web/auth/UsageAndTokensPage.js +157 -0
  283. package/dist/web/auth/UsagePage.d.ts +2 -0
  284. package/dist/web/auth/UsagePage.d.ts.map +1 -0
  285. package/dist/web/auth/UsagePage.js +62 -0
  286. package/dist/web/auth/components/ApiKeyFilterSelect.d.ts +13 -0
  287. package/dist/web/auth/components/ApiKeyFilterSelect.d.ts.map +1 -0
  288. package/dist/web/auth/components/ApiKeyFilterSelect.js +16 -0
  289. package/dist/web/auth/components/ModelUsageTable.d.ts +19 -0
  290. package/dist/web/auth/components/ModelUsageTable.d.ts.map +1 -0
  291. package/dist/web/auth/components/ModelUsageTable.js +37 -0
  292. package/dist/web/auth/components/PurchaseButton.d.ts +7 -0
  293. package/dist/web/auth/components/PurchaseButton.d.ts.map +1 -0
  294. package/dist/web/auth/components/PurchaseButton.js +13 -0
  295. package/dist/web/auth/components/TokenHistoryCard.d.ts +20 -0
  296. package/dist/web/auth/components/TokenHistoryCard.d.ts.map +1 -0
  297. package/dist/web/auth/components/TokenHistoryCard.js +76 -0
  298. package/dist/web/auth/components/TokenKpiGrid.d.ts +24 -0
  299. package/dist/web/auth/components/TokenKpiGrid.d.ts.map +1 -0
  300. package/dist/web/auth/components/TokenKpiGrid.js +38 -0
  301. package/dist/web/auth/components/UsageByDayChart.d.ts +11 -0
  302. package/dist/web/auth/components/UsageByDayChart.d.ts.map +1 -0
  303. package/dist/web/auth/components/UsageByDayChart.js +32 -0
  304. package/dist/web/auth/components/UsageByModelBarChart.d.ts +12 -0
  305. package/dist/web/auth/components/UsageByModelBarChart.d.ts.map +1 -0
  306. package/dist/web/auth/components/UsageByModelBarChart.js +32 -0
  307. package/dist/web/auth/components/WalletStatusCard.d.ts +9 -0
  308. package/dist/web/auth/components/WalletStatusCard.d.ts.map +1 -0
  309. package/dist/web/auth/components/WalletStatusCard.js +50 -0
  310. package/dist/web/components/ImageGenerative.d.ts +3 -1
  311. package/dist/web/components/ImageGenerative.d.ts.map +1 -1
  312. package/dist/web/components/ImageGenerative.js +139 -52
  313. package/dist/web/components/TextareaGenerative.d.ts +3 -1
  314. package/dist/web/components/TextareaGenerative.d.ts.map +1 -1
  315. package/dist/web/components/TextareaGenerative.js +10 -5
  316. package/dist/web/public/PromptDetailPage.d.ts +25 -0
  317. package/dist/web/public/PromptDetailPage.d.ts.map +1 -0
  318. package/dist/web/public/PromptDetailPage.js +71 -0
  319. package/dist/web/public/PromptDetailPageServer.d.ts +15 -0
  320. package/dist/web/public/PromptDetailPageServer.d.ts.map +1 -0
  321. package/dist/web/public/PromptDetailPageServer.js +68 -0
  322. package/dist/web/public/PublicPromptsPage.d.ts +5 -0
  323. package/dist/web/public/PublicPromptsPage.d.ts.map +1 -0
  324. package/dist/web/public/PublicPromptsPage.js +110 -0
  325. package/dist/web/public/PurchaseTokensPage.d.ts +2 -0
  326. package/dist/web/public/PurchaseTokensPage.d.ts.map +1 -0
  327. package/dist/web/public/PurchaseTokensPage.js +98 -0
  328. package/dist/web/public/UserAvatar.d.ts +13 -0
  329. package/dist/web/public/UserAvatar.d.ts.map +1 -0
  330. package/dist/web/public/UserAvatar.js +13 -0
  331. package/dist/web/public/UserDetailPageServer.d.ts +15 -0
  332. package/dist/web/public/UserDetailPageServer.d.ts.map +1 -0
  333. package/dist/web/public/UserDetailPageServer.js +31 -0
  334. package/dist/web/public/UserPromptsPage.d.ts +9 -0
  335. package/dist/web/public/UserPromptsPage.d.ts.map +1 -0
  336. package/dist/web/public/UserPromptsPage.js +112 -0
  337. package/dist/web/public/UserPromptsPageServer.d.ts +15 -0
  338. package/dist/web/public/UserPromptsPageServer.d.ts.map +1 -0
  339. package/dist/web/public/UserPromptsPageServer.js +31 -0
  340. package/package.json +18 -9
  341. package/supabase/migrations/20251125000000_ai_tokens.sql +7 -0
  342. package/supabase/migrations/20260123100002_user_token_quota_monthly copy.sql +173 -0
  343. package/supabase/migrations/20260128100003_update_and_add_table.sql +368 -0
  344. package/supabase/migrations/20260128120000_seed_providers_models.sql +78 -0
  345. package/supabase/migrations/20260128131405_add_api_key_id_to_ledgers.sql +41 -0
  346. package/supabase/migrations/20260128140000_ai_artifacts_storage.sql +99 -0
  347. package/supabase/migrations/20260128140002_ai_user_settings.sql +57 -0
  348. package/supabase/migrations/20260128150000_drop_ai_user_settings.sql +21 -0
  349. package/supabase/migrations/20260128160000_wallet_billing_system.sql +192 -0
  350. package/supabase/migrations/20260128160001_wallet_rpc_functions.sql +165 -0
  351. package/supabase/migrations/20260128170000_add_pack_coef_to_token_packs.sql +30 -0
  352. package/supabase/migrations/20260129120000_wallet_view_rpc.sql +41 -0
  353. package/supabase/migrations/20260129220003_update_pack_margins.sql +31 -0
  354. package/supabase/migrations/20260129330004_ai_user_prompts.sql +151 -0
  355. package/supabase/migrations/20260129330005_ai_prompts_ip_tracking.sql +92 -0
  356. package/supabase/migrations/20260129330006_ai_prompts_slug.sql +64 -0
  357. package/supabase/migrations/20260129330007_ai_prompts_view_slug.sql +26 -0
  358. package/supabase/migrations/20260129440000_ai_prompts_view_username.sql +33 -0
  359. package/supabase/migrations/20260129450000_ai_prompts_add_lang.sql +40 -0
  360. package/supabase/migrations/20260131000000_extract_model_prompt_in_ledger.sql +92 -0
  361. package/supabase/migrations/20260131140000_fix_duplicate_purchases.sql +64 -0
  362. package/supabase/migrations/20260201120000_module-ai_default_models.sql +63 -0
  363. package/supabase/migrations/20260201130000_module-ai_remove_provider_tables.sql +17 -0
  364. package/supabase/migrations-down/20251217120000_user_token_quota_monthly.sql +34 -0
  365. package/supabase/migrations-down/20260128131405_add_api_key_id_to_ledgers.sql +25 -0
@@ -0,0 +1,168 @@
1
+ /**
2
+ * GET /api/ai/public/token-pricing
3
+ * Returns token pricing information for all models
4
+ * Single source of truth for client-side pricing display
5
+ */
6
+ import { NextResponse } from "next/server";
7
+ import { logger } from "@lastbrain/core";
8
+ import { getSupabaseServiceClient } from "@lastbrain/core/server";
9
+ import { fetchGatewayModels } from "../../server/gateway-service";
10
+ import { computeProviderCostUsd, estimateDebitTokens, } from "../../server/billing";
11
+ export async function GET(request) {
12
+ try {
13
+ // Get pack ID from query params (optional)
14
+ const url = new URL(request.url);
15
+ const packId = url.searchParams.get("packId");
16
+ // Get pack info from database
17
+ const supabase = await getSupabaseServiceClient();
18
+ let packInfo;
19
+ if (packId) {
20
+ const { data: pack } = await supabase
21
+ .from("token_packs")
22
+ .select("tokens, price_cents, currency, pack_coef")
23
+ .eq("id", packId)
24
+ .eq("is_active", true)
25
+ .single();
26
+ if (pack) {
27
+ packInfo = {
28
+ tokens: pack.tokens,
29
+ price_usd: pack.price_cents / 100,
30
+ coef: pack.pack_coef || 1.35, // Use DB value or fallback
31
+ };
32
+ }
33
+ else {
34
+ // Fallback to best pack
35
+ packInfo = await getBestPack(supabase);
36
+ }
37
+ }
38
+ else {
39
+ // Use best pack (lowest $/token)
40
+ packInfo = await getBestPack(supabase);
41
+ }
42
+ // Fetch models from gateway (internal, with pricing)
43
+ const gatewayResponse = await fetchGatewayModels();
44
+ // Calculate pricing for all models
45
+ const providersWithPricing = gatewayResponse.providers.map((provider) => {
46
+ const modelsWithPricing = provider.models.map((model) => {
47
+ // Map Gateway types to our internal types
48
+ const normalizedType = model.type === "language" ? "text" : model.type;
49
+ // Only calculate pricing for supported types
50
+ if (normalizedType !== "text" && normalizedType !== "image") {
51
+ return {
52
+ modelId: model.id,
53
+ modelName: model.name,
54
+ type: model.type,
55
+ provider: model.provider,
56
+ estimatedTokens: null,
57
+ displayPrice: "N/A",
58
+ available: false,
59
+ };
60
+ }
61
+ const providerPricing = {
62
+ // Gateway prices are in $/token (not $/M)
63
+ inputUsdPerToken: model.pricing?.input
64
+ ? parseFloat(model.pricing.input.toString())
65
+ : undefined,
66
+ outputUsdPerToken: model.pricing?.output
67
+ ? parseFloat(model.pricing.output.toString())
68
+ : undefined,
69
+ imageUsdPerImage: model.pricing?.image
70
+ ? parseFloat(model.pricing.image.toString())
71
+ : undefined,
72
+ };
73
+ let estimatedTokens = null;
74
+ let displayPrice = "N/A";
75
+ let inputSellUsdPerMToken;
76
+ let outputSellUsdPerMToken;
77
+ if (normalizedType === "text") {
78
+ // Estimate for 500K input + 500K output
79
+ const usage = {
80
+ inputTokens: 500_000,
81
+ outputTokens: 500_000,
82
+ };
83
+ const providerCost = computeProviderCostUsd(usage, providerPricing);
84
+ if (providerCost > 0) {
85
+ estimatedTokens = estimateDebitTokens(providerCost, packInfo.coef, packInfo.tokens, packInfo.price_usd);
86
+ displayPrice = `~${estimatedTokens.toLocaleString()} tokens/1M`;
87
+ // Sell prices with markup
88
+ if (providerPricing.inputUsdPerToken) {
89
+ inputSellUsdPerMToken =
90
+ providerPricing.inputUsdPerToken * 1_000_000 * packInfo.coef;
91
+ }
92
+ if (providerPricing.outputUsdPerToken) {
93
+ outputSellUsdPerMToken =
94
+ providerPricing.outputUsdPerToken * 1_000_000 * packInfo.coef;
95
+ }
96
+ }
97
+ }
98
+ else if (normalizedType === "image") {
99
+ // Estimate for 1 image
100
+ const usage = { images: 1 };
101
+ const providerCost = computeProviderCostUsd(usage, providerPricing);
102
+ if (providerCost > 0) {
103
+ estimatedTokens = estimateDebitTokens(providerCost, packInfo.coef, packInfo.tokens, packInfo.price_usd);
104
+ displayPrice = `${estimatedTokens.toLocaleString()} tokens/img`;
105
+ }
106
+ }
107
+ return {
108
+ modelId: model.id,
109
+ modelName: model.name,
110
+ type: model.type,
111
+ provider: model.provider,
112
+ estimatedTokens,
113
+ displayPrice,
114
+ available: estimatedTokens !== null,
115
+ inputSellUsdPerMToken,
116
+ outputSellUsdPerMToken,
117
+ imageSellUsd: providerPricing.imageUsdPerImage
118
+ ? providerPricing.imageUsdPerImage * packInfo.coef
119
+ : undefined,
120
+ };
121
+ });
122
+ return {
123
+ name: provider.name,
124
+ display_name: provider.display_name,
125
+ models: modelsWithPricing,
126
+ };
127
+ });
128
+ return NextResponse.json({
129
+ config: {
130
+ packTokens: packInfo.tokens,
131
+ packPriceUsd: packInfo.price_usd,
132
+ tokenPriceUsd: packInfo.price_usd / packInfo.tokens,
133
+ markupCoef: packInfo.coef,
134
+ },
135
+ providers: providersWithPricing,
136
+ });
137
+ }
138
+ catch (error) {
139
+ logger.error("[public/token-pricing] Error:", error);
140
+ return NextResponse.json({ error: error.message || "Internal server error" }, { status: 500 });
141
+ }
142
+ }
143
+ // Helper to get best pack (lowest $/token)
144
+ async function getBestPack(supabase) {
145
+ const { data: packs } = await supabase
146
+ .from("token_packs")
147
+ .select("tokens, price_cents, pack_coef")
148
+ .eq("is_active", true);
149
+ if (!packs || packs.length === 0) {
150
+ // Fallback
151
+ return { tokens: 2_000_000, price_usd: 50, coef: 1.35 };
152
+ }
153
+ // Find pack with lowest $/token
154
+ let bestPack = packs[0];
155
+ let bestRate = packs[0].price_cents / 100 / packs[0].tokens;
156
+ for (const pack of packs) {
157
+ const rate = pack.price_cents / 100 / pack.tokens;
158
+ if (rate < bestRate) {
159
+ bestRate = rate;
160
+ bestPack = pack;
161
+ }
162
+ }
163
+ return {
164
+ tokens: bestPack.tokens,
165
+ price_usd: bestPack.price_cents / 100,
166
+ coef: bestPack.pack_coef || 1.35, // Use DB value or fallback
167
+ };
168
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Helper functions for AI artifacts storage
3
+ */
4
+ interface ApiKey {
5
+ id: string;
6
+ owner_id: string;
7
+ name: string;
8
+ }
9
+ interface ArtifactOptions {
10
+ apiKey: ApiKey;
11
+ kind: "text" | "image" | "pdf" | "file";
12
+ endpoint: string;
13
+ provider?: string | null;
14
+ model?: string | null;
15
+ tokensTotal?: number | null;
16
+ statusCode?: number;
17
+ textContent?: string;
18
+ fileBuffer?: Buffer;
19
+ mimeType?: string;
20
+ ext?: string;
21
+ meta?: Record<string, any>;
22
+ }
23
+ interface StorageResult {
24
+ success: boolean;
25
+ artifactId?: string;
26
+ storagePath?: string;
27
+ error?: string;
28
+ }
29
+ /**
30
+ * Check if user has store_outputs enabled
31
+ */
32
+ export declare function shouldStoreOutputs(ownerId: string, overrideStoreOutputs?: boolean): Promise<boolean>;
33
+ /**
34
+ * Create artifact record in database
35
+ */
36
+ export declare function createArtifact(options: ArtifactOptions): Promise<StorageResult>;
37
+ /**
38
+ * Generate signed URL for file download
39
+ */
40
+ export declare function getSignedDownloadUrl(storagePath: string, expiresIn?: number): Promise<{
41
+ url: string | null;
42
+ error?: string;
43
+ }>;
44
+ /**
45
+ * Delete artifact and its storage file
46
+ */
47
+ export declare function deleteArtifact(artifactId: string, ownerId: string): Promise<{
48
+ success: boolean;
49
+ error?: string;
50
+ }>;
51
+ export {};
52
+ //# sourceMappingURL=artifacts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifacts.d.ts","sourceRoot":"","sources":["../../../../../src/api/public/v1/_lib/artifacts.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,UAAU,MAAM;IACd,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,eAAe;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC5B;AAED,UAAU,aAAa;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAuBD;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,EACf,oBAAoB,CAAC,EAAE,OAAO,GAC7B,OAAO,CAAC,OAAO,CAAC,CA8BlB;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,aAAa,CAAC,CA+GxB;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,WAAW,EAAE,MAAM,EACnB,SAAS,SAAK,GACb,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAqBjD;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAgD/C"}
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Helper functions for AI artifacts storage
3
+ */
4
+ import { logger } from "@lastbrain/core";
5
+ import { getSupabaseServiceClient } from "@lastbrain/core/server";
6
+ /**
7
+ * Sanitize API key name for storage path
8
+ */
9
+ function sanitizeApiKeyName(name) {
10
+ return name
11
+ .toLowerCase()
12
+ .replace(/[^a-z0-9-]/g, "-")
13
+ .replace(/-+/g, "-")
14
+ .substring(0, 50);
15
+ }
16
+ /**
17
+ * Get month-year folder (mm-yyyy)
18
+ */
19
+ function getMonthYearFolder() {
20
+ const now = new Date();
21
+ const month = String(now.getMonth() + 1).padStart(2, "0");
22
+ const year = now.getFullYear();
23
+ return `${month}-${year}`;
24
+ }
25
+ /**
26
+ * Check if user has store_outputs enabled
27
+ */
28
+ export async function shouldStoreOutputs(ownerId, overrideStoreOutputs) {
29
+ // If explicitly provided in request, use that value
30
+ if (overrideStoreOutputs !== undefined) {
31
+ return overrideStoreOutputs;
32
+ }
33
+ try {
34
+ const supabase = getSupabaseServiceClient();
35
+ const { data, error } = await supabase
36
+ .from("user_ai_settings")
37
+ .select("store_outputs")
38
+ .eq("owner_id", ownerId)
39
+ .single();
40
+ if (error || !data) {
41
+ logger.debug("[artifacts] No settings found for user, defaulting to false", {
42
+ ownerId,
43
+ });
44
+ return false;
45
+ }
46
+ return data.store_outputs === true;
47
+ }
48
+ catch (error) {
49
+ logger.error("[artifacts] Error checking store_outputs", error);
50
+ return false;
51
+ }
52
+ }
53
+ /**
54
+ * Create artifact record in database
55
+ */
56
+ export async function createArtifact(options) {
57
+ const { apiKey, kind, endpoint, provider, model, tokensTotal, statusCode = 200, textContent, fileBuffer, mimeType, ext, meta = {}, } = options;
58
+ try {
59
+ const supabase = getSupabaseServiceClient();
60
+ const artifactId = crypto.randomUUID();
61
+ // Sanitize API key name for path
62
+ const apiKeyName = apiKey.name
63
+ ? sanitizeApiKeyName(apiKey.name)
64
+ : "default";
65
+ const monthYear = getMonthYearFolder();
66
+ let storagePath = null;
67
+ let sizeBytes = null;
68
+ // Handle file storage (image, pdf, file)
69
+ if (kind !== "text" && fileBuffer) {
70
+ const extension = ext || "bin";
71
+ storagePath = `${apiKey.owner_id}/${apiKeyName}/${monthYear}/${artifactId}.${extension}`;
72
+ try {
73
+ const { error: uploadError } = await supabase.storage
74
+ .from("app")
75
+ .upload(storagePath, fileBuffer, {
76
+ contentType: mimeType || "application/octet-stream",
77
+ upsert: false,
78
+ });
79
+ if (uploadError) {
80
+ logger.error("[artifacts] Storage upload failed", {
81
+ error: uploadError,
82
+ path: storagePath,
83
+ });
84
+ // Continue anyway, will store metadata without file
85
+ storagePath = null;
86
+ }
87
+ else {
88
+ sizeBytes = fileBuffer.length;
89
+ }
90
+ }
91
+ catch (uploadError) {
92
+ logger.error("[artifacts] Storage upload exception", uploadError);
93
+ storagePath = null;
94
+ }
95
+ }
96
+ // Insert artifact record
97
+ const { data, error } = await supabase
98
+ .from("ai_artifacts")
99
+ .insert({
100
+ id: artifactId,
101
+ owner_id: apiKey.owner_id,
102
+ api_key_id: apiKey.id,
103
+ api_key_name: apiKeyName,
104
+ kind,
105
+ provider,
106
+ model,
107
+ endpoint,
108
+ tokens_total: tokensTotal,
109
+ status_code: statusCode,
110
+ text_content: kind === "text" ? textContent : null,
111
+ storage_bucket: storagePath ? "app" : null,
112
+ storage_path: storagePath,
113
+ mime_type: mimeType || null,
114
+ size_bytes: sizeBytes,
115
+ meta,
116
+ })
117
+ .select("id, storage_path")
118
+ .single();
119
+ if (error) {
120
+ logger.error("[artifacts] Failed to insert artifact", {
121
+ error,
122
+ artifactId,
123
+ });
124
+ return {
125
+ success: false,
126
+ error: error.message,
127
+ };
128
+ }
129
+ logger.info("[artifacts] Artifact created", {
130
+ artifactId,
131
+ kind,
132
+ storagePath,
133
+ });
134
+ return {
135
+ success: true,
136
+ artifactId: data.id,
137
+ storagePath: data.storage_path || undefined,
138
+ };
139
+ }
140
+ catch (error) {
141
+ logger.error("[artifacts] Unexpected error creating artifact", error);
142
+ return {
143
+ success: false,
144
+ error: error.message,
145
+ };
146
+ }
147
+ }
148
+ /**
149
+ * Generate signed URL for file download
150
+ */
151
+ export async function getSignedDownloadUrl(storagePath, expiresIn = 60) {
152
+ try {
153
+ const supabase = getSupabaseServiceClient();
154
+ const { data, error } = await supabase.storage
155
+ .from("app")
156
+ .createSignedUrl(storagePath, expiresIn);
157
+ if (error) {
158
+ logger.error("[artifacts] Failed to generate signed URL", {
159
+ error,
160
+ path: storagePath,
161
+ });
162
+ return { url: null, error: error.message };
163
+ }
164
+ return { url: data.signedUrl };
165
+ }
166
+ catch (error) {
167
+ logger.error("[artifacts] Exception generating signed URL", error);
168
+ return { url: null, error: error.message };
169
+ }
170
+ }
171
+ /**
172
+ * Delete artifact and its storage file
173
+ */
174
+ export async function deleteArtifact(artifactId, ownerId) {
175
+ try {
176
+ const supabase = getSupabaseServiceClient();
177
+ // Get artifact first to check ownership and get storage path
178
+ const { data: artifact, error: fetchError } = await supabase
179
+ .from("ai_artifacts")
180
+ .select("owner_id, storage_path, storage_bucket")
181
+ .eq("id", artifactId)
182
+ .single();
183
+ if (fetchError || !artifact) {
184
+ return { success: false, error: "Artifact not found" };
185
+ }
186
+ if (artifact.owner_id !== ownerId) {
187
+ return { success: false, error: "Unauthorized" };
188
+ }
189
+ // Delete file from storage if exists
190
+ if (artifact.storage_path && artifact.storage_bucket) {
191
+ try {
192
+ await supabase.storage
193
+ .from(artifact.storage_bucket)
194
+ .remove([artifact.storage_path]);
195
+ }
196
+ catch (storageError) {
197
+ logger.error("[artifacts] Failed to delete storage file", storageError);
198
+ // Continue anyway to delete DB record
199
+ }
200
+ }
201
+ // Delete database record
202
+ const { error: deleteError } = await supabase
203
+ .from("ai_artifacts")
204
+ .delete()
205
+ .eq("id", artifactId);
206
+ if (deleteError) {
207
+ logger.error("[artifacts] Failed to delete artifact record", deleteError);
208
+ return { success: false, error: deleteError.message };
209
+ }
210
+ logger.info("[artifacts] Artifact deleted", { artifactId });
211
+ return { success: true };
212
+ }
213
+ catch (error) {
214
+ logger.error("[artifacts] Exception deleting artifact", error);
215
+ return { success: false, error: error.message };
216
+ }
217
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Authentication and authorization helpers for public API v1
3
+ */
4
+ export interface ApiKeyData {
5
+ id: string;
6
+ owner_id: string;
7
+ name: string;
8
+ prefix: string;
9
+ env: string;
10
+ scopes: string[];
11
+ rate_limit_rpm: number;
12
+ daily_token_limit: bigint | null;
13
+ is_active: boolean;
14
+ last_used_at: string | null;
15
+ created_at: string;
16
+ }
17
+ export interface ValidateApiKeyResult {
18
+ success: boolean;
19
+ error?: string;
20
+ errorCode?: string;
21
+ apiKey?: ApiKeyData;
22
+ }
23
+ /**
24
+ * Hash API key for lookup
25
+ */
26
+ export declare function hashApiKey(apiKey: string): string;
27
+ /**
28
+ * Validate API key from Authorization header
29
+ */
30
+ export declare function validateApiKey(authHeader: string | null): Promise<ValidateApiKeyResult>;
31
+ /**
32
+ * Check if API key has required scope
33
+ */
34
+ export declare function hasScope(apiKey: ApiKeyData, requiredScope: string): boolean;
35
+ /**
36
+ * Verify API key has required scope, returns error if not
37
+ */
38
+ export declare function verifyScope(apiKey: ApiKeyData, requiredScope: string): {
39
+ success: boolean;
40
+ error?: string;
41
+ errorCode?: string;
42
+ };
43
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../../../src/api/public/v1/_lib/auth.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,UAAU,EAAE,MAAM,GAAG,IAAI,GACxB,OAAO,CAAC,oBAAoB,CAAC,CAgF/B;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAE3E;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,UAAU,EAClB,aAAa,EAAE,MAAM,GACpB;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAS1D"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Authentication and authorization helpers for public API v1
3
+ */
4
+ import { getSupabaseServiceClient } from "@lastbrain/core/server";
5
+ import { logger } from "@lastbrain/core";
6
+ import { createHash } from "crypto";
7
+ /**
8
+ * Hash API key for lookup
9
+ */
10
+ export function hashApiKey(apiKey) {
11
+ return createHash("sha256").update(apiKey).digest("hex");
12
+ }
13
+ /**
14
+ * Validate API key from Authorization header
15
+ */
16
+ export async function validateApiKey(authHeader) {
17
+ if (!authHeader) {
18
+ return {
19
+ success: false,
20
+ error: "Missing Authorization header",
21
+ errorCode: "MISSING_API_KEY",
22
+ };
23
+ }
24
+ const parts = authHeader.split(" ");
25
+ if (parts.length !== 2 || parts[0] !== "Bearer") {
26
+ return {
27
+ success: false,
28
+ error: "Invalid Authorization header format. Expected: Bearer <api_key>",
29
+ errorCode: "INVALID_API_KEY",
30
+ };
31
+ }
32
+ const rawKey = parts[1];
33
+ if (!rawKey || rawKey.length < 32) {
34
+ return {
35
+ success: false,
36
+ error: "Invalid API key format",
37
+ errorCode: "INVALID_API_KEY",
38
+ };
39
+ }
40
+ try {
41
+ const keyHash = hashApiKey(rawKey);
42
+ const supabase = await getSupabaseServiceClient();
43
+ const { data: apiKey, error } = await supabase
44
+ .from("api_keys")
45
+ .select("*")
46
+ .eq("key_hash", keyHash)
47
+ .single();
48
+ if (error || !apiKey) {
49
+ logger.warn("[validateApiKey] API key not found", { keyHash });
50
+ return {
51
+ success: false,
52
+ error: "Invalid API key",
53
+ errorCode: "INVALID_API_KEY",
54
+ };
55
+ }
56
+ if (!apiKey.is_active) {
57
+ logger.warn("[validateApiKey] API key is inactive", {
58
+ id: apiKey.id,
59
+ owner_id: apiKey.owner_id,
60
+ });
61
+ return {
62
+ success: false,
63
+ error: "API key is inactive",
64
+ errorCode: "INACTIVE_API_KEY",
65
+ };
66
+ }
67
+ // Parse scopes from JSONB
68
+ const scopes = Array.isArray(apiKey.scopes)
69
+ ? apiKey.scopes
70
+ : typeof apiKey.scopes === "string"
71
+ ? JSON.parse(apiKey.scopes)
72
+ : [];
73
+ return {
74
+ success: true,
75
+ apiKey: {
76
+ ...apiKey,
77
+ scopes,
78
+ },
79
+ };
80
+ }
81
+ catch (error) {
82
+ logger.error("[validateApiKey] Error validating API key", error);
83
+ return {
84
+ success: false,
85
+ error: "Internal error validating API key",
86
+ errorCode: "INTERNAL_ERROR",
87
+ };
88
+ }
89
+ }
90
+ /**
91
+ * Check if API key has required scope
92
+ */
93
+ export function hasScope(apiKey, requiredScope) {
94
+ return apiKey.scopes.includes(requiredScope);
95
+ }
96
+ /**
97
+ * Verify API key has required scope, returns error if not
98
+ */
99
+ export function verifyScope(apiKey, requiredScope) {
100
+ if (!hasScope(apiKey, requiredScope)) {
101
+ return {
102
+ success: false,
103
+ error: `Missing required scope: ${requiredScope}`,
104
+ errorCode: "INSUFFICIENT_SCOPE",
105
+ };
106
+ }
107
+ return { success: true };
108
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Standard error codes and JSON error responses for public API v1
3
+ */
4
+ export type ErrorCode = "MISSING_API_KEY" | "INVALID_API_KEY" | "INACTIVE_API_KEY" | "INSUFFICIENT_SCOPE" | "INSUFFICIENT_TOKENS" | "RATE_LIMIT_EXCEEDED" | "DAILY_LIMIT_EXCEEDED" | "VALIDATION_ERROR" | "PROVIDER_ERROR" | "INTERNAL_ERROR" | "INVALID_CREDENTIALS" | "USER_NOT_FOUND";
5
+ export interface ApiError {
6
+ error: {
7
+ code: ErrorCode;
8
+ message: string;
9
+ request_id: string;
10
+ action_url?: string;
11
+ };
12
+ }
13
+ export declare function createErrorResponse(code: ErrorCode, message: string, requestId: string, actionUrl?: string, status?: number): {
14
+ response: ApiError;
15
+ status: number;
16
+ };
17
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../../../src/api/public/v1/_lib/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,SAAS,GACjB,iBAAiB,GACjB,iBAAiB,GACjB,kBAAkB,GAClB,oBAAoB,GACpB,qBAAqB,GACrB,qBAAqB,GACrB,sBAAsB,GACtB,kBAAkB,GAClB,gBAAgB,GAChB,gBAAgB,GAChB,qBAAqB,GACrB,gBAAgB,CAAC;AAErB,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE;QACL,IAAI,EAAE,SAAS,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;CACH;AAED,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,EAClB,MAAM,GAAE,MAAY,GACnB;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAYxC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Standard error codes and JSON error responses for public API v1
3
+ */
4
+ export function createErrorResponse(code, message, requestId, actionUrl, status = 400) {
5
+ return {
6
+ response: {
7
+ error: {
8
+ code,
9
+ message,
10
+ request_id: requestId,
11
+ ...(actionUrl && { action_url: actionUrl }),
12
+ },
13
+ },
14
+ status,
15
+ };
16
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Logging utilities for public API v1
3
+ * Handles ai_call_log inserts and api_keys.last_used_at updates
4
+ */
5
+ import type { ApiKeyData } from "./auth";
6
+ export interface LogCallParams {
7
+ apiKeyId: string;
8
+ ownerId: string;
9
+ endpoint: string;
10
+ provider: string | null;
11
+ model: string | null;
12
+ tokensIn: number | null;
13
+ tokensOut: number | null;
14
+ tokensTotal: number | null;
15
+ latencyMs: number | null;
16
+ statusCode: number;
17
+ errorCode: string | null;
18
+ requestId: string;
19
+ meta?: Record<string, unknown>;
20
+ }
21
+ /**
22
+ * Insert a log entry in ai_call_log
23
+ */
24
+ export declare function logApiCall(params: LogCallParams): Promise<void>;
25
+ /**
26
+ * Update api_keys.last_used_at
27
+ */
28
+ export declare function updateApiKeyLastUsed(apiKey: ApiKeyData): Promise<void>;
29
+ //# sourceMappingURL=log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../../../../src/api/public/v1/_lib/log.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEzC,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAqCrE;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAkB5E"}