libre-webui 0.25.0 → 0.26.0

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 (510) hide show
  1. package/README.md +32 -22
  2. package/backend/dist/index.d.ts.map +1 -1
  3. package/backend/dist/index.js +26 -4
  4. package/backend/dist/index.js.map +1 -1
  5. package/backend/dist/middleware/auth.d.ts +7 -0
  6. package/backend/dist/middleware/auth.d.ts.map +1 -1
  7. package/backend/dist/middleware/auth.js +26 -0
  8. package/backend/dist/middleware/auth.js.map +1 -1
  9. package/backend/dist/middleware/index.d.ts +20 -0
  10. package/backend/dist/middleware/index.d.ts.map +1 -1
  11. package/backend/dist/middleware/index.js +105 -2
  12. package/backend/dist/middleware/index.js.map +1 -1
  13. package/backend/dist/models/personaModel.d.ts.map +1 -1
  14. package/backend/dist/models/personaModel.js +2 -0
  15. package/backend/dist/models/personaModel.js.map +1 -1
  16. package/backend/dist/observability/otel.d.ts +40 -0
  17. package/backend/dist/observability/otel.d.ts.map +1 -0
  18. package/backend/dist/observability/otel.js +337 -0
  19. package/backend/dist/observability/otel.js.map +1 -0
  20. package/backend/dist/observability/requestContext.d.ts +14 -0
  21. package/backend/dist/observability/requestContext.d.ts.map +1 -0
  22. package/backend/dist/observability/requestContext.js +21 -0
  23. package/backend/dist/observability/requestContext.js.map +1 -0
  24. package/backend/dist/persistence/chatGenerationTypes.d.ts +9 -0
  25. package/backend/dist/persistence/chatGenerationTypes.d.ts.map +1 -1
  26. package/backend/dist/persistence/extensionTypes.d.ts +13 -0
  27. package/backend/dist/persistence/extensionTypes.d.ts.map +1 -1
  28. package/backend/dist/persistence/extensionTypes.js.map +1 -1
  29. package/backend/dist/persistence/index.d.ts +1 -1
  30. package/backend/dist/persistence/index.d.ts.map +1 -1
  31. package/backend/dist/persistence/index.js.map +1 -1
  32. package/backend/dist/persistence/postgresAgentFoundationMigration.d.ts +9 -0
  33. package/backend/dist/persistence/postgresAgentFoundationMigration.d.ts.map +1 -0
  34. package/backend/dist/persistence/postgresAgentFoundationMigration.js +164 -0
  35. package/backend/dist/persistence/postgresAgentFoundationMigration.js.map +1 -0
  36. package/backend/dist/persistence/postgresExtensionRepositories.d.ts.map +1 -1
  37. package/backend/dist/persistence/postgresExtensionRepositories.js +46 -2
  38. package/backend/dist/persistence/postgresExtensionRepositories.js.map +1 -1
  39. package/backend/dist/persistence/postgresMediaEnterpriseOpsMigration.d.ts +10 -0
  40. package/backend/dist/persistence/postgresMediaEnterpriseOpsMigration.d.ts.map +1 -0
  41. package/backend/dist/persistence/postgresMediaEnterpriseOpsMigration.js +148 -0
  42. package/backend/dist/persistence/postgresMediaEnterpriseOpsMigration.js.map +1 -0
  43. package/backend/dist/persistence/postgresMigrationRegistry.d.ts.map +1 -1
  44. package/backend/dist/persistence/postgresMigrationRegistry.js +10 -0
  45. package/backend/dist/persistence/postgresMigrationRegistry.js.map +1 -1
  46. package/backend/dist/persistence/postgresNotesV2Migration.d.ts +4 -0
  47. package/backend/dist/persistence/postgresNotesV2Migration.d.ts.map +1 -0
  48. package/backend/dist/persistence/postgresNotesV2Migration.js +57 -0
  49. package/backend/dist/persistence/postgresNotesV2Migration.js.map +1 -0
  50. package/backend/dist/persistence/postgresResourceRepositories.d.ts.map +1 -1
  51. package/backend/dist/persistence/postgresResourceRepositories.js +1646 -11
  52. package/backend/dist/persistence/postgresResourceRepositories.js.map +1 -1
  53. package/backend/dist/persistence/postgresSkillFilesMigration.d.ts +9 -0
  54. package/backend/dist/persistence/postgresSkillFilesMigration.d.ts.map +1 -0
  55. package/backend/dist/persistence/postgresSkillFilesMigration.js +50 -0
  56. package/backend/dist/persistence/postgresSkillFilesMigration.js.map +1 -0
  57. package/backend/dist/persistence/postgresTeamCollaborationMigration.d.ts +10 -0
  58. package/backend/dist/persistence/postgresTeamCollaborationMigration.d.ts.map +1 -0
  59. package/backend/dist/persistence/postgresTeamCollaborationMigration.js +161 -0
  60. package/backend/dist/persistence/postgresTeamCollaborationMigration.js.map +1 -0
  61. package/backend/dist/persistence/resourceTypes.d.ts +541 -3
  62. package/backend/dist/persistence/resourceTypes.d.ts.map +1 -1
  63. package/backend/dist/persistence/resourceTypes.js.map +1 -1
  64. package/backend/dist/persistence/sqliteExtensionRepositories.d.ts.map +1 -1
  65. package/backend/dist/persistence/sqliteExtensionRepositories.js +26 -4
  66. package/backend/dist/persistence/sqliteExtensionRepositories.js.map +1 -1
  67. package/backend/dist/persistence/sqliteMigrations.d.ts +9 -0
  68. package/backend/dist/persistence/sqliteMigrations.d.ts.map +1 -1
  69. package/backend/dist/persistence/sqliteMigrations.js +1196 -29
  70. package/backend/dist/persistence/sqliteMigrations.js.map +1 -1
  71. package/backend/dist/persistence/sqliteResourceRepositories.d.ts.map +1 -1
  72. package/backend/dist/persistence/sqliteResourceRepositories.js +1523 -134
  73. package/backend/dist/persistence/sqliteResourceRepositories.js.map +1 -1
  74. package/backend/dist/persistence/sqliteToPostgresMigration.d.ts.map +1 -1
  75. package/backend/dist/persistence/sqliteToPostgresMigration.js +257 -3
  76. package/backend/dist/persistence/sqliteToPostgresMigration.js.map +1 -1
  77. package/backend/dist/platform/jobs/domainJobContracts.d.ts +8 -0
  78. package/backend/dist/platform/jobs/domainJobContracts.d.ts.map +1 -1
  79. package/backend/dist/platform/jobs/domainJobContracts.js +8 -0
  80. package/backend/dist/platform/jobs/domainJobContracts.js.map +1 -1
  81. package/backend/dist/platform/jobs/domainJobHandlers.d.ts.map +1 -1
  82. package/backend/dist/platform/jobs/domainJobHandlers.js +310 -3
  83. package/backend/dist/platform/jobs/domainJobHandlers.js.map +1 -1
  84. package/backend/dist/platform/jobs/embeddedDurableJobWorker.d.ts.map +1 -1
  85. package/backend/dist/platform/jobs/embeddedDurableJobWorker.js +4 -2
  86. package/backend/dist/platform/jobs/embeddedDurableJobWorker.js.map +1 -1
  87. package/backend/dist/platform/jobs/postgresDurableJobWorker.d.ts.map +1 -1
  88. package/backend/dist/platform/jobs/postgresDurableJobWorker.js +4 -2
  89. package/backend/dist/platform/jobs/postgresDurableJobWorker.js.map +1 -1
  90. package/backend/dist/platform/storage/pgVectorStore.d.ts +2 -1
  91. package/backend/dist/platform/storage/pgVectorStore.d.ts.map +1 -1
  92. package/backend/dist/platform/storage/pgVectorStore.js +51 -0
  93. package/backend/dist/platform/storage/pgVectorStore.js.map +1 -1
  94. package/backend/dist/platform/storage/platformDomainRepositories.d.ts +8 -2
  95. package/backend/dist/platform/storage/platformDomainRepositories.d.ts.map +1 -1
  96. package/backend/dist/platform/storage/postgresPlatformDomainRepositories.d.ts.map +1 -1
  97. package/backend/dist/platform/storage/postgresPlatformDomainRepositories.js +25 -4
  98. package/backend/dist/platform/storage/postgresPlatformDomainRepositories.js.map +1 -1
  99. package/backend/dist/platform/storage/sqliteEncryptedVectorStore.d.ts +2 -1
  100. package/backend/dist/platform/storage/sqliteEncryptedVectorStore.d.ts.map +1 -1
  101. package/backend/dist/platform/storage/sqliteEncryptedVectorStore.js +35 -0
  102. package/backend/dist/platform/storage/sqliteEncryptedVectorStore.js.map +1 -1
  103. package/backend/dist/platform/storage/sqlitePlatformDomainRepositories.d.ts.map +1 -1
  104. package/backend/dist/platform/storage/sqlitePlatformDomainRepositories.js +26 -3
  105. package/backend/dist/platform/storage/sqlitePlatformDomainRepositories.js.map +1 -1
  106. package/backend/dist/platform/storage/vectorStore.d.ts +14 -0
  107. package/backend/dist/platform/storage/vectorStore.d.ts.map +1 -1
  108. package/backend/dist/platform/storage/vectorStore.js.map +1 -1
  109. package/backend/dist/routes/access.d.ts.map +1 -1
  110. package/backend/dist/routes/access.js +84 -3
  111. package/backend/dist/routes/access.js.map +1 -1
  112. package/backend/dist/routes/calendar.d.ts.map +1 -1
  113. package/backend/dist/routes/calendar.js +125 -22
  114. package/backend/dist/routes/calendar.js.map +1 -1
  115. package/backend/dist/routes/channels.d.ts +3 -0
  116. package/backend/dist/routes/channels.d.ts.map +1 -0
  117. package/backend/dist/routes/channels.js +447 -0
  118. package/backend/dist/routes/channels.js.map +1 -0
  119. package/backend/dist/routes/chat.d.ts.map +1 -1
  120. package/backend/dist/routes/chat.js +195 -7
  121. package/backend/dist/routes/chat.js.map +1 -1
  122. package/backend/dist/routes/costs.d.ts +3 -0
  123. package/backend/dist/routes/costs.d.ts.map +1 -0
  124. package/backend/dist/routes/costs.js +191 -0
  125. package/backend/dist/routes/costs.js.map +1 -0
  126. package/backend/dist/routes/documents.d.ts.map +1 -1
  127. package/backend/dist/routes/documents.js +64 -21
  128. package/backend/dist/routes/documents.js.map +1 -1
  129. package/backend/dist/routes/evaluations.d.ts +3 -0
  130. package/backend/dist/routes/evaluations.d.ts.map +1 -0
  131. package/backend/dist/routes/evaluations.js +352 -0
  132. package/backend/dist/routes/evaluations.js.map +1 -0
  133. package/backend/dist/routes/imageGen.d.ts.map +1 -1
  134. package/backend/dist/routes/imageGen.js +2 -1
  135. package/backend/dist/routes/imageGen.js.map +1 -1
  136. package/backend/dist/routes/media.d.ts.map +1 -1
  137. package/backend/dist/routes/media.js +228 -7
  138. package/backend/dist/routes/media.js.map +1 -1
  139. package/backend/dist/routes/notes.d.ts.map +1 -1
  140. package/backend/dist/routes/notes.js +149 -36
  141. package/backend/dist/routes/notes.js.map +1 -1
  142. package/backend/dist/routes/notifications.d.ts +3 -0
  143. package/backend/dist/routes/notifications.d.ts.map +1 -0
  144. package/backend/dist/routes/notifications.js +213 -0
  145. package/backend/dist/routes/notifications.js.map +1 -0
  146. package/backend/dist/routes/openaiCompat.d.ts +3 -0
  147. package/backend/dist/routes/openaiCompat.d.ts.map +1 -0
  148. package/backend/dist/routes/openaiCompat.js +314 -0
  149. package/backend/dist/routes/openaiCompat.js.map +1 -0
  150. package/backend/dist/routes/personas.js +1 -1
  151. package/backend/dist/routes/personas.js.map +1 -1
  152. package/backend/dist/routes/prompts.d.ts +3 -0
  153. package/backend/dist/routes/prompts.d.ts.map +1 -0
  154. package/backend/dist/routes/prompts.js +173 -0
  155. package/backend/dist/routes/prompts.js.map +1 -0
  156. package/backend/dist/routes/search.d.ts.map +1 -1
  157. package/backend/dist/routes/search.js +29 -1
  158. package/backend/dist/routes/search.js.map +1 -1
  159. package/backend/dist/routes/skills.d.ts +3 -0
  160. package/backend/dist/routes/skills.d.ts.map +1 -0
  161. package/backend/dist/routes/skills.js +246 -0
  162. package/backend/dist/routes/skills.js.map +1 -0
  163. package/backend/dist/routes/stt.d.ts.map +1 -1
  164. package/backend/dist/routes/stt.js +2 -1
  165. package/backend/dist/routes/stt.js.map +1 -1
  166. package/backend/dist/routes/tools.d.ts +3 -0
  167. package/backend/dist/routes/tools.d.ts.map +1 -0
  168. package/backend/dist/routes/tools.js +359 -0
  169. package/backend/dist/routes/tools.js.map +1 -0
  170. package/backend/dist/routes/tts.d.ts.map +1 -1
  171. package/backend/dist/routes/tts.js +63 -8
  172. package/backend/dist/routes/tts.js.map +1 -1
  173. package/backend/dist/routes/voiceAccess.d.ts +3 -0
  174. package/backend/dist/routes/voiceAccess.d.ts.map +1 -0
  175. package/backend/dist/routes/voiceAccess.js +102 -0
  176. package/backend/dist/routes/voiceAccess.js.map +1 -0
  177. package/backend/dist/services/apiTokenService.d.ts +2 -2
  178. package/backend/dist/services/apiTokenService.d.ts.map +1 -1
  179. package/backend/dist/services/apiTokenService.js +7 -0
  180. package/backend/dist/services/apiTokenService.js.map +1 -1
  181. package/backend/dist/services/authorizationService.d.ts +8 -4
  182. package/backend/dist/services/authorizationService.d.ts.map +1 -1
  183. package/backend/dist/services/authorizationService.js +31 -1
  184. package/backend/dist/services/authorizationService.js.map +1 -1
  185. package/backend/dist/services/automationSchedulerService.d.ts.map +1 -1
  186. package/backend/dist/services/automationSchedulerService.js +33 -3
  187. package/backend/dist/services/automationSchedulerService.js.map +1 -1
  188. package/backend/dist/services/automationService.d.ts +4 -1
  189. package/backend/dist/services/automationService.d.ts.map +1 -1
  190. package/backend/dist/services/automationService.js +25 -2
  191. package/backend/dist/services/automationService.js.map +1 -1
  192. package/backend/dist/services/builtinToolsService.d.ts +20 -0
  193. package/backend/dist/services/builtinToolsService.d.ts.map +1 -0
  194. package/backend/dist/services/builtinToolsService.js +661 -0
  195. package/backend/dist/services/builtinToolsService.js.map +1 -0
  196. package/backend/dist/services/calendarService.d.ts +74 -0
  197. package/backend/dist/services/calendarService.d.ts.map +1 -0
  198. package/backend/dist/services/calendarService.js +339 -0
  199. package/backend/dist/services/calendarService.js.map +1 -0
  200. package/backend/dist/services/channelService.d.ts +195 -0
  201. package/backend/dist/services/channelService.d.ts.map +1 -0
  202. package/backend/dist/services/channelService.js +1057 -0
  203. package/backend/dist/services/channelService.js.map +1 -0
  204. package/backend/dist/services/chatRequestService.d.ts.map +1 -1
  205. package/backend/dist/services/chatRequestService.js +29 -1
  206. package/backend/dist/services/chatRequestService.js.map +1 -1
  207. package/backend/dist/services/chatService.d.ts +61 -1
  208. package/backend/dist/services/chatService.d.ts.map +1 -1
  209. package/backend/dist/services/chatService.js +218 -1
  210. package/backend/dist/services/chatService.js.map +1 -1
  211. package/backend/dist/services/chatToolRuntimeService.d.ts +55 -0
  212. package/backend/dist/services/chatToolRuntimeService.d.ts.map +1 -0
  213. package/backend/dist/services/chatToolRuntimeService.js +389 -0
  214. package/backend/dist/services/chatToolRuntimeService.js.map +1 -0
  215. package/backend/dist/services/costGovernanceService.d.ts +102 -0
  216. package/backend/dist/services/costGovernanceService.d.ts.map +1 -0
  217. package/backend/dist/services/costGovernanceService.js +463 -0
  218. package/backend/dist/services/costGovernanceService.js.map +1 -0
  219. package/backend/dist/services/dataArchiveService.d.ts +6 -0
  220. package/backend/dist/services/dataArchiveService.d.ts.map +1 -1
  221. package/backend/dist/services/dataArchiveService.js +23 -4
  222. package/backend/dist/services/dataArchiveService.js.map +1 -1
  223. package/backend/dist/services/documentService.d.ts +58 -1
  224. package/backend/dist/services/documentService.d.ts.map +1 -1
  225. package/backend/dist/services/documentService.js +289 -84
  226. package/backend/dist/services/documentService.js.map +1 -1
  227. package/backend/dist/services/durableChatGenerationService.d.ts +9 -0
  228. package/backend/dist/services/durableChatGenerationService.d.ts.map +1 -1
  229. package/backend/dist/services/durableChatGenerationService.js +152 -11
  230. package/backend/dist/services/durableChatGenerationService.js.map +1 -1
  231. package/backend/dist/services/evaluationService.d.ts +111 -0
  232. package/backend/dist/services/evaluationService.d.ts.map +1 -0
  233. package/backend/dist/services/evaluationService.js +403 -0
  234. package/backend/dist/services/evaluationService.js.map +1 -0
  235. package/backend/dist/services/galleryService.d.ts +6 -0
  236. package/backend/dist/services/galleryService.d.ts.map +1 -1
  237. package/backend/dist/services/galleryService.js +26 -0
  238. package/backend/dist/services/galleryService.js.map +1 -1
  239. package/backend/dist/services/mcpClientService.d.ts +29 -0
  240. package/backend/dist/services/mcpClientService.d.ts.map +1 -0
  241. package/backend/dist/services/mcpClientService.js +189 -0
  242. package/backend/dist/services/mcpClientService.js.map +1 -0
  243. package/backend/dist/services/noteService.d.ts +71 -0
  244. package/backend/dist/services/noteService.d.ts.map +1 -0
  245. package/backend/dist/services/noteService.js +397 -0
  246. package/backend/dist/services/noteService.js.map +1 -0
  247. package/backend/dist/services/notificationService.d.ts +55 -0
  248. package/backend/dist/services/notificationService.d.ts.map +1 -0
  249. package/backend/dist/services/notificationService.js +308 -0
  250. package/backend/dist/services/notificationService.js.map +1 -0
  251. package/backend/dist/services/openApiToolService.d.ts +38 -0
  252. package/backend/dist/services/openApiToolService.d.ts.map +1 -0
  253. package/backend/dist/services/openApiToolService.js +254 -0
  254. package/backend/dist/services/openApiToolService.js.map +1 -0
  255. package/backend/dist/services/personaService.d.ts +18 -2
  256. package/backend/dist/services/personaService.d.ts.map +1 -1
  257. package/backend/dist/services/personaService.js +113 -4
  258. package/backend/dist/services/personaService.js.map +1 -1
  259. package/backend/dist/services/pluginImageGenerationService.d.ts +19 -0
  260. package/backend/dist/services/pluginImageGenerationService.d.ts.map +1 -1
  261. package/backend/dist/services/pluginImageGenerationService.js +121 -0
  262. package/backend/dist/services/pluginImageGenerationService.js.map +1 -1
  263. package/backend/dist/services/pluginService.d.ts +7 -0
  264. package/backend/dist/services/pluginService.d.ts.map +1 -1
  265. package/backend/dist/services/pluginService.js +8 -2
  266. package/backend/dist/services/pluginService.js.map +1 -1
  267. package/backend/dist/services/promptService.d.ts +74 -0
  268. package/backend/dist/services/promptService.d.ts.map +1 -0
  269. package/backend/dist/services/promptService.js +513 -0
  270. package/backend/dist/services/promptService.js.map +1 -0
  271. package/backend/dist/services/resourceGrantService.d.ts.map +1 -1
  272. package/backend/dist/services/resourceGrantService.js +62 -17
  273. package/backend/dist/services/resourceGrantService.js.map +1 -1
  274. package/backend/dist/services/searchService.d.ts +29 -0
  275. package/backend/dist/services/searchService.d.ts.map +1 -0
  276. package/backend/dist/services/searchService.js +0 -0
  277. package/backend/dist/services/searchService.js.map +1 -0
  278. package/backend/dist/services/securityAuditService.d.ts.map +1 -1
  279. package/backend/dist/services/securityAuditService.js +2 -1
  280. package/backend/dist/services/securityAuditService.js.map +1 -1
  281. package/backend/dist/services/sharedResourceAccess.d.ts +25 -0
  282. package/backend/dist/services/sharedResourceAccess.d.ts.map +1 -0
  283. package/backend/dist/services/sharedResourceAccess.js +59 -0
  284. package/backend/dist/services/sharedResourceAccess.js.map +1 -0
  285. package/backend/dist/services/skillService.d.ts +137 -0
  286. package/backend/dist/services/skillService.d.ts.map +1 -0
  287. package/backend/dist/services/skillService.js +852 -0
  288. package/backend/dist/services/skillService.js.map +1 -0
  289. package/backend/dist/services/toolAccessService.d.ts +9 -0
  290. package/backend/dist/services/toolAccessService.d.ts.map +1 -0
  291. package/backend/dist/services/toolAccessService.js +60 -0
  292. package/backend/dist/services/toolAccessService.js.map +1 -0
  293. package/backend/dist/services/toolApprovalService.d.ts +34 -0
  294. package/backend/dist/services/toolApprovalService.d.ts.map +1 -0
  295. package/backend/dist/services/toolApprovalService.js +188 -0
  296. package/backend/dist/services/toolApprovalService.js.map +1 -0
  297. package/backend/dist/services/toolGatewayService.d.ts +53 -0
  298. package/backend/dist/services/toolGatewayService.d.ts.map +1 -0
  299. package/backend/dist/services/toolGatewayService.js +249 -0
  300. package/backend/dist/services/toolGatewayService.js.map +1 -0
  301. package/backend/dist/services/toolServerService.d.ts +57 -0
  302. package/backend/dist/services/toolServerService.d.ts.map +1 -0
  303. package/backend/dist/services/toolServerService.js +521 -0
  304. package/backend/dist/services/toolServerService.js.map +1 -0
  305. package/backend/dist/services/voiceAccessService.d.ts +10 -0
  306. package/backend/dist/services/voiceAccessService.d.ts.map +1 -0
  307. package/backend/dist/services/voiceAccessService.js +74 -0
  308. package/backend/dist/services/voiceAccessService.js.map +1 -0
  309. package/backend/dist/services/voiceProfileService.d.ts +20 -0
  310. package/backend/dist/services/voiceProfileService.d.ts.map +1 -1
  311. package/backend/dist/services/voiceProfileService.js +60 -0
  312. package/backend/dist/services/voiceProfileService.js.map +1 -1
  313. package/backend/dist/storage.d.ts +28 -1
  314. package/backend/dist/storage.d.ts.map +1 -1
  315. package/backend/dist/storage.js +100 -1
  316. package/backend/dist/storage.js.map +1 -1
  317. package/backend/dist/storageMappers.d.ts +2 -2
  318. package/backend/dist/storageMappers.d.ts.map +1 -1
  319. package/backend/dist/storageMappers.js.map +1 -1
  320. package/backend/dist/types/index.d.ts +197 -2
  321. package/backend/dist/types/index.d.ts.map +1 -1
  322. package/backend/dist/types/index.js.map +1 -1
  323. package/backend/dist/types/tools.d.ts +137 -0
  324. package/backend/dist/types/tools.d.ts.map +1 -0
  325. package/backend/dist/types/tools.js +25 -0
  326. package/backend/dist/types/tools.js.map +1 -0
  327. package/backend/dist/utils/chatDocumentContext.d.ts +26 -1
  328. package/backend/dist/utils/chatDocumentContext.d.ts.map +1 -1
  329. package/backend/dist/utils/chatDocumentContext.js +87 -18
  330. package/backend/dist/utils/chatDocumentContext.js.map +1 -1
  331. package/backend/dist/utils/documentExtraction.d.ts +69 -0
  332. package/backend/dist/utils/documentExtraction.d.ts.map +1 -0
  333. package/backend/dist/utils/documentExtraction.js +545 -0
  334. package/backend/dist/utils/documentExtraction.js.map +1 -0
  335. package/backend/dist/utils/hybridRetrieval.d.ts +34 -0
  336. package/backend/dist/utils/hybridRetrieval.d.ts.map +1 -0
  337. package/backend/dist/utils/hybridRetrieval.js +119 -0
  338. package/backend/dist/utils/hybridRetrieval.js.map +1 -0
  339. package/backend/dist/utils/icsCalendar.d.ts +27 -0
  340. package/backend/dist/utils/icsCalendar.d.ts.map +1 -0
  341. package/backend/dist/utils/icsCalendar.js +313 -0
  342. package/backend/dist/utils/icsCalendar.js.map +1 -0
  343. package/backend/dist/utils/imageEditUpload.d.ts +29 -0
  344. package/backend/dist/utils/imageEditUpload.d.ts.map +1 -0
  345. package/backend/dist/utils/imageEditUpload.js +82 -0
  346. package/backend/dist/utils/imageEditUpload.js.map +1 -0
  347. package/backend/dist/utils/logRedaction.d.ts +2 -0
  348. package/backend/dist/utils/logRedaction.d.ts.map +1 -0
  349. package/backend/dist/utils/logRedaction.js +66 -0
  350. package/backend/dist/utils/logRedaction.js.map +1 -0
  351. package/backend/dist/utils/logger.d.ts +3 -0
  352. package/backend/dist/utils/logger.d.ts.map +1 -1
  353. package/backend/dist/utils/logger.js +63 -20
  354. package/backend/dist/utils/logger.js.map +1 -1
  355. package/backend/dist/utils/pluginChatAdapter.d.ts +5 -1
  356. package/backend/dist/utils/pluginChatAdapter.d.ts.map +1 -1
  357. package/backend/dist/utils/pluginChatAdapter.js +83 -0
  358. package/backend/dist/utils/pluginChatAdapter.js.map +1 -1
  359. package/backend/dist/utils/pluginDefinitionTrust.js +1 -1
  360. package/backend/dist/utils/resourceLimits.d.ts +61 -2
  361. package/backend/dist/utils/resourceLimits.d.ts.map +1 -1
  362. package/backend/dist/utils/resourceLimits.js +59 -0
  363. package/backend/dist/utils/resourceLimits.js.map +1 -1
  364. package/backend/dist/utils/toolEgress.d.ts +22 -0
  365. package/backend/dist/utils/toolEgress.d.ts.map +1 -0
  366. package/backend/dist/utils/toolEgress.js +157 -0
  367. package/backend/dist/utils/toolEgress.js.map +1 -0
  368. package/backend/dist/utils/websocketMessages.d.ts +6 -0
  369. package/backend/dist/utils/websocketMessages.d.ts.map +1 -1
  370. package/backend/dist/utils/websocketMessages.js +8 -0
  371. package/backend/dist/utils/websocketMessages.js.map +1 -1
  372. package/backend/dist/utils/zipArchive.d.ts +32 -0
  373. package/backend/dist/utils/zipArchive.d.ts.map +1 -0
  374. package/backend/dist/utils/zipArchive.js +171 -0
  375. package/backend/dist/utils/zipArchive.js.map +1 -0
  376. package/backend/dist/websocketServer.d.ts.map +1 -1
  377. package/backend/dist/websocketServer.js +142 -35
  378. package/backend/dist/websocketServer.js.map +1 -1
  379. package/backend/package.json +1 -1
  380. package/frontend/dist/artifact-runtime/.build-fingerprint +1 -1
  381. package/frontend/dist/assets/index-BPsH9FII.js +80 -0
  382. package/frontend/dist/assets/index-BtXF0Oxa.css +1 -0
  383. package/frontend/dist/index.html +18 -18
  384. package/frontend/dist/js/{ArtifactContainer-CpUi6ekF.js → ArtifactContainer-CIoFoMv8.js} +1 -1
  385. package/frontend/dist/js/{ArtifactDemoPage-BeNs3gHp.js → ArtifactDemoPage-D0JEIAbZ.js} +1 -1
  386. package/frontend/dist/js/{ArtifactSandboxFrame-qsj_Y3LV.js → ArtifactSandboxFrame-REazyIrx.js} +2 -2
  387. package/frontend/dist/js/ArtifactSlideOutPanel-A-i94ghF.js +1 -0
  388. package/frontend/dist/js/AutomationsPage-Dow_lbIf.js +1 -0
  389. package/frontend/dist/js/{Button-BPf_pOxH.js → Button-C0r3J9G_.js} +1 -1
  390. package/frontend/dist/js/CalendarPage-DLjasZOd.js +1 -0
  391. package/frontend/dist/js/ChannelsPage-C8hvTD7J.js +1 -0
  392. package/frontend/dist/js/ChatPage-BAgBHUES.js +16 -0
  393. package/frontend/dist/js/EvaluationsPage-Bm8FRoeX.js +2 -0
  394. package/frontend/dist/js/{FirstTimeSetup-Df0YJni3.js → FirstTimeSetup-BxJapNul.js} +1 -1
  395. package/frontend/dist/js/GalleryPage-DHz9DkK7.js +1 -0
  396. package/frontend/dist/js/HomePage-BqNy-FWX.js +1 -0
  397. package/frontend/dist/js/LibreClawPage-BDzDILjY.js +1 -0
  398. package/frontend/dist/js/{ModelManagerModals-DcLGYN3l.js → ModelManagerModals-DX9eT5r2.js} +1 -1
  399. package/frontend/dist/js/ModelSelector-ol8mK2ja.js +1 -0
  400. package/frontend/dist/js/NotesPage-DzHEx2O4.js +3 -0
  401. package/frontend/dist/js/{OptimizedSyntaxHighlighter-Dz7D1LPB.js → OptimizedSyntaxHighlighter-CZNPwN0i.js} +1 -1
  402. package/frontend/dist/js/PersonasPage-M_WKH2a9.js +13 -0
  403. package/frontend/dist/js/PluginUsagePage-Ctoka6JZ.js +1 -0
  404. package/frontend/dist/js/RichMessageContent-BqgafH4p.js +3 -0
  405. package/frontend/dist/js/SettingsModal-R9EDadAV.js +19 -0
  406. package/frontend/dist/js/{SettingsToggle-woG_WebF.js → SettingsToggle-BIVyfGjl.js} +1 -1
  407. package/frontend/dist/js/SystemPage-aOkrSuY7.js +1 -0
  408. package/frontend/dist/js/UserManagementPage-BOuaX3ay.js +1 -0
  409. package/frontend/dist/js/WorkPage-DxZISerQ.js +6 -0
  410. package/frontend/dist/js/api-6bSMtSNh.js +14 -0
  411. package/frontend/dist/js/{appStore-0JnCUlEq.js → appStore-CdObhQBb.js} +2 -2
  412. package/frontend/dist/js/ar-DT_VLBO3.js +2 -0
  413. package/frontend/dist/js/{authStore-DbMdvNgJ.js → authStore-DbqqrfZv.js} +3 -3
  414. package/frontend/dist/js/{babel-BsYoxD-7.js → babel-DSqqk5kt.js} +1 -1
  415. package/frontend/dist/js/bn-BrvEqhGA.js +2 -0
  416. package/frontend/dist/js/chatStore-DbZPJNsu.js +3 -0
  417. package/frontend/dist/js/{config-DSOYQ9a3.js → config-8KdgjtEx.js} +1 -1
  418. package/frontend/dist/js/cs-BthnM_YI.js +2 -0
  419. package/frontend/dist/js/da-DCth-6_j.js +2 -0
  420. package/frontend/dist/js/de-BBWt0gJ_.js +2 -0
  421. package/frontend/dist/js/es-DuslOPqM.js +2 -0
  422. package/frontend/dist/js/{estree-BcnBcOAx.js → estree-BU5-LV_1.js} +3 -3
  423. package/frontend/dist/js/evaluationsApi-CwZJsgeG.js +1 -0
  424. package/frontend/dist/js/fr-DZAeg46S.js +2 -0
  425. package/frontend/dist/js/hi-ijDSiY77.js +2 -0
  426. package/frontend/dist/js/{html-DkY55Jys.js → html-DeoAy2bo.js} +2 -2
  427. package/frontend/dist/js/id-BS6qS0oq.js +2 -0
  428. package/frontend/dist/js/is-CO2PMDte.js +2 -0
  429. package/frontend/dist/js/it-ChhxT4hs.js +2 -0
  430. package/frontend/dist/js/ja-cyeqUeJN.js +2 -0
  431. package/frontend/dist/js/ko-CfqHTLXj.js +2 -0
  432. package/frontend/dist/js/{markdown-IZSI9DXw.js → markdown-DOLsYkBY.js} +3 -3
  433. package/frontend/dist/js/markdown-core-BK7WVOb7.js +32 -0
  434. package/frontend/dist/js/{markdown-math-DK846gXU.js → markdown-math-e1ByS3KY.js} +5 -5
  435. package/frontend/dist/js/{messageCodeStyles-BFNR9Wv6.js → messageCodeStyles-DhVgizF-.js} +2 -2
  436. package/frontend/dist/js/ms-DZ5pACe1.js +2 -0
  437. package/frontend/dist/js/nl-BsRsnOVp.js +2 -0
  438. package/frontend/dist/js/pl-DXSTAW4M.js +2 -0
  439. package/frontend/dist/js/{postcss-BJ0okz7n.js → postcss-Bg15EcFE.js} +1 -1
  440. package/frontend/dist/js/pt-DmDx0JxB.js +2 -0
  441. package/frontend/dist/js/{react-vendor-C5yShNlM.js → react-vendor-BG_HPV6X.js} +1 -1
  442. package/frontend/dist/js/router-vendor-CbexJIsv.js +1 -0
  443. package/frontend/dist/js/ru-d2g8EWfY.js +2 -0
  444. package/frontend/dist/js/{standalone-Csw3jfJ_.js → standalone-BO5pzI8_.js} +3 -3
  445. package/frontend/dist/js/sv-CIhYUTVQ.js +2 -0
  446. package/frontend/dist/js/{syntax-highlight-3EEX6CGL.js → syntax-highlight-DcR9Vhb7.js} +3 -3
  447. package/frontend/dist/js/th-B3wSxanB.js +2 -0
  448. package/frontend/dist/js/tr-R4UGgPOY.js +2 -0
  449. package/frontend/dist/js/{typescript-CnF4UVx7.js → typescript-6GJ8APmn.js} +1 -1
  450. package/frontend/dist/js/ui-CV0LYmkM.js +2 -0
  451. package/frontend/dist/js/ui-vendor-CWjIBW-z.js +177 -0
  452. package/frontend/dist/js/uk-BzUF4Vkt.js +2 -0
  453. package/frontend/dist/js/{useTranslation-Bho4HCyg.js → useTranslation-5b5UhAc9.js} +1 -1
  454. package/frontend/dist/js/{utils-D9W1Lnkn.js → utils-BGrdX9ie.js} +1 -1
  455. package/frontend/dist/js/{utils-vendor-DWh39_3x.js → utils-vendor-9bQ62WeD.js} +1 -1
  456. package/frontend/dist/js/vi-CKc6Fg4h.js +2 -0
  457. package/frontend/dist/js/{workApi-C3uy9EDT.js → workApi-BruWTlHM.js} +3 -3
  458. package/frontend/dist/js/{yaml-DzT5hBSH.js → yaml-DUeWcOOH.js} +2 -2
  459. package/frontend/dist/js/zh-CXL-kDe8.js +2 -0
  460. package/package.json +2 -2
  461. package/plugins/openai.json +52 -9
  462. package/frontend/dist/assets/index-6o-Hc2wS.js +0 -41
  463. package/frontend/dist/assets/index-D-pM957b.css +0 -1
  464. package/frontend/dist/js/ArtifactSlideOutPanel-CSFRluWA.js +0 -1
  465. package/frontend/dist/js/AutomationsPage-KUeWoZNv.js +0 -1
  466. package/frontend/dist/js/CalendarPage-D9I972w8.js +0 -1
  467. package/frontend/dist/js/ChatPage-CmBx4oAL.js +0 -16
  468. package/frontend/dist/js/GalleryPage-bLCrdJN_.js +0 -1
  469. package/frontend/dist/js/HomePage-Uc6PEg-U.js +0 -1
  470. package/frontend/dist/js/LibreClawPage-CG63DHg1.js +0 -1
  471. package/frontend/dist/js/ModelSelector-yxJC64rM.js +0 -1
  472. package/frontend/dist/js/ModelsPage-1R_TxgmJ.js +0 -2
  473. package/frontend/dist/js/NotesPage-L65j5HoW.js +0 -1
  474. package/frontend/dist/js/PersonasPage-CKeBpHIH.js +0 -13
  475. package/frontend/dist/js/PluginUsagePage-BX1SKMbW.js +0 -1
  476. package/frontend/dist/js/RichMessageContent-DiySVYGB.js +0 -3
  477. package/frontend/dist/js/SettingsModal-68SG1uZ2.js +0 -1
  478. package/frontend/dist/js/SystemPage-CytpZiQV.js +0 -1
  479. package/frontend/dist/js/UserManagementPage-BzJBvZiS.js +0 -1
  480. package/frontend/dist/js/WorkPage-C8gr0QJe.js +0 -6
  481. package/frontend/dist/js/api-BDtqBtqw.js +0 -14
  482. package/frontend/dist/js/ar-CSpi37Gr.js +0 -2
  483. package/frontend/dist/js/bn-CyH1BeDD.js +0 -2
  484. package/frontend/dist/js/chatStore-CHQ2uINS.js +0 -3
  485. package/frontend/dist/js/cs-Cv2NfRK5.js +0 -2
  486. package/frontend/dist/js/da-D6D6IvPK.js +0 -2
  487. package/frontend/dist/js/de-BNoJJTrT.js +0 -2
  488. package/frontend/dist/js/es-kzo9C7q_.js +0 -2
  489. package/frontend/dist/js/fr-CjHYvJRp.js +0 -2
  490. package/frontend/dist/js/hi-BU98AdCQ.js +0 -2
  491. package/frontend/dist/js/id-CpdedejX.js +0 -2
  492. package/frontend/dist/js/is-Cp5dDWBH.js +0 -2
  493. package/frontend/dist/js/it-CuH_cK4U.js +0 -2
  494. package/frontend/dist/js/ja-BWXXXiHn.js +0 -2
  495. package/frontend/dist/js/ko-DQgv2M1t.js +0 -2
  496. package/frontend/dist/js/markdown-core-BmPPZiuD.js +0 -31
  497. package/frontend/dist/js/ms-DCcRoIcX.js +0 -2
  498. package/frontend/dist/js/nl-CbKR1_EU.js +0 -2
  499. package/frontend/dist/js/pl-CRyJZccn.js +0 -2
  500. package/frontend/dist/js/pt-BXYmHFGH.js +0 -2
  501. package/frontend/dist/js/router-vendor-DGFGM3EP.js +0 -1
  502. package/frontend/dist/js/ru-M9SyZG4b.js +0 -2
  503. package/frontend/dist/js/sv-DJ486LTU.js +0 -2
  504. package/frontend/dist/js/th-Ch7gKtZv.js +0 -2
  505. package/frontend/dist/js/tr-ClehWhSC.js +0 -2
  506. package/frontend/dist/js/ui-CC-K19wO.js +0 -2
  507. package/frontend/dist/js/ui-vendor-B1UgO3vd.js +0 -177
  508. package/frontend/dist/js/uk-BqAd5zHF.js +0 -2
  509. package/frontend/dist/js/vi-D9gjlDVB.js +0 -2
  510. package/frontend/dist/js/zh-DKTyHkiR.js +0 -2
@@ -172,6 +172,11 @@ class SQLiteKnowledgeCollectionRepository {
172
172
  .prepare('SELECT * FROM knowledge_collections WHERE user_id = ? ORDER BY name COLLATE NOCASE ASC')
173
173
  .all(userId);
174
174
  }
175
+ async findById(collectionId) {
176
+ return (this.database
177
+ .prepare('SELECT * FROM knowledge_collections WHERE id = ?')
178
+ .get(collectionId) ?? null);
179
+ }
175
180
  async replace(collection) {
176
181
  ensureSameOwner(this.database, 'knowledge_collections', collection.id, collection.user_id);
177
182
  this.database
@@ -204,7 +209,7 @@ class SQLiteNoteRepository {
204
209
  return this.database
205
210
  .prepare(`SELECT * FROM notes
206
211
  WHERE user_id = ?
207
- ORDER BY updated_at DESC
212
+ ORDER BY pinned DESC, updated_at DESC
208
213
  LIMIT ?`)
209
214
  .all(userId, maximum);
210
215
  }
@@ -226,14 +231,15 @@ class SQLiteNoteRepository {
226
231
  }
227
232
  this.database
228
233
  .prepare(`INSERT INTO notes
229
- (id, user_id, title, content, created_at, updated_at)
230
- VALUES (?, ?, ?, ?, ?, ?)
234
+ (id, user_id, title, content, pinned, created_at, updated_at)
235
+ VALUES (?, ?, ?, ?, ?, ?, ?)
231
236
  ON CONFLICT(id) DO UPDATE SET
232
237
  title = excluded.title,
233
238
  content = excluded.content,
239
+ pinned = excluded.pinned,
234
240
  updated_at = excluded.updated_at
235
241
  WHERE notes.user_id = excluded.user_id`)
236
- .run(note.id, note.user_id, note.title, note.content, note.created_at, note.updated_at);
242
+ .run(note.id, note.user_id, note.title, note.content, note.pinned, note.created_at, note.updated_at);
237
243
  });
238
244
  replace();
239
245
  }
@@ -249,6 +255,10 @@ class SQLiteNoteRepository {
249
255
  assignments.push('content = ?');
250
256
  values.push(patch.content);
251
257
  }
258
+ if (patch.pinned !== undefined) {
259
+ assignments.push('pinned = ?');
260
+ values.push(patch.pinned);
261
+ }
252
262
  assignments.push('updated_at = ?');
253
263
  values.push(patch.updated_at);
254
264
  const result = this.database
@@ -268,6 +278,110 @@ class SQLiteNoteRepository {
268
278
  .prepare('DELETE FROM notes WHERE id = ? AND user_id = ?')
269
279
  .run(noteId, userId).changes > 0);
270
280
  }
281
+ async findById(noteId) {
282
+ return (this.database.prepare('SELECT * FROM notes WHERE id = ?').get(noteId) ?? null);
283
+ }
284
+ async patchById(noteId, patch) {
285
+ const update = this.database.transaction(() => {
286
+ const assignments = [];
287
+ const values = [];
288
+ if (patch.title !== undefined) {
289
+ assignments.push('title = ?');
290
+ values.push(patch.title);
291
+ }
292
+ if (patch.content !== undefined) {
293
+ assignments.push('content = ?');
294
+ values.push(patch.content);
295
+ }
296
+ if (patch.pinned !== undefined) {
297
+ assignments.push('pinned = ?');
298
+ values.push(patch.pinned);
299
+ }
300
+ assignments.push('updated_at = ?');
301
+ values.push(patch.updated_at);
302
+ const result = this.database
303
+ .prepare(`UPDATE notes SET ${assignments.join(', ')} WHERE id = ?`)
304
+ .run(...values, noteId);
305
+ if (result.changes !== 1)
306
+ return null;
307
+ return this.database
308
+ .prepare('SELECT * FROM notes WHERE id = ?')
309
+ .get(noteId);
310
+ });
311
+ return update.immediate();
312
+ }
313
+ async listRevisions(noteId, maximum) {
314
+ return this.database
315
+ .prepare(`SELECT * FROM note_revisions
316
+ WHERE note_id = ?
317
+ ORDER BY created_at DESC, id DESC
318
+ LIMIT ?`)
319
+ .all(noteId, maximum);
320
+ }
321
+ async findRevision(revisionId) {
322
+ return (this.database
323
+ .prepare('SELECT * FROM note_revisions WHERE id = ?')
324
+ .get(revisionId) ?? null);
325
+ }
326
+ async insertRevision(revision, maximum) {
327
+ const insert = this.database.transaction(() => {
328
+ // Strictly increasing per note so same-millisecond snapshots keep a
329
+ // deterministic order for listing, restore, and pruning.
330
+ const latest = this.database
331
+ .prepare('SELECT COALESCE(MAX(created_at), 0) AS latest FROM note_revisions WHERE note_id = ?')
332
+ .get(revision.note_id);
333
+ const createdAt = Math.max(revision.created_at, latest.latest + 1);
334
+ this.database
335
+ .prepare(`INSERT INTO note_revisions
336
+ (id, note_id, title, content, created_at)
337
+ VALUES (?, ?, ?, ?, ?)`)
338
+ .run(revision.id, revision.note_id, revision.title, revision.content, createdAt);
339
+ this.database
340
+ .prepare(`DELETE FROM note_revisions
341
+ WHERE note_id = ?
342
+ AND id NOT IN (
343
+ SELECT id FROM note_revisions
344
+ WHERE note_id = ?
345
+ ORDER BY created_at DESC, id DESC
346
+ LIMIT ?
347
+ )`)
348
+ .run(revision.note_id, revision.note_id, maximum);
349
+ });
350
+ insert();
351
+ }
352
+ async listAttachments(noteId) {
353
+ return this.database
354
+ .prepare(`SELECT * FROM note_attachments
355
+ WHERE note_id = ?
356
+ ORDER BY created_at ASC, id ASC`)
357
+ .all(noteId);
358
+ }
359
+ async findAttachment(attachmentId) {
360
+ return (this.database
361
+ .prepare('SELECT * FROM note_attachments WHERE id = ?')
362
+ .get(attachmentId) ?? null);
363
+ }
364
+ async insertAttachmentWithLimit(attachment, maximum) {
365
+ const insert = this.database.transaction(() => {
366
+ const row = this.database
367
+ .prepare('SELECT COUNT(*) AS count FROM note_attachments WHERE note_id = ?')
368
+ .get(attachment.note_id);
369
+ if (row.count >= maximum) {
370
+ throw new PersistenceResourceLimitError('note-attachment', maximum);
371
+ }
372
+ this.database
373
+ .prepare(`INSERT INTO note_attachments
374
+ (id, note_id, blob_id, filename, content_type, size, created_at)
375
+ VALUES (?, ?, ?, ?, ?, ?, ?)`)
376
+ .run(attachment.id, attachment.note_id, attachment.blob_id, attachment.filename, attachment.content_type, attachment.size, attachment.created_at);
377
+ });
378
+ insert();
379
+ }
380
+ async deleteAttachment(attachmentId) {
381
+ return (this.database
382
+ .prepare('DELETE FROM note_attachments WHERE id = ?')
383
+ .run(attachmentId).changes > 0);
384
+ }
271
385
  }
272
386
  class SQLiteCalendarEventRepository {
273
387
  constructor(database) {
@@ -289,11 +403,57 @@ class SQLiteCalendarEventRepository {
289
403
  LIMIT ?`)
290
404
  .all(userId, maximum);
291
405
  }
406
+ async listByCalendarsBetween(calendarIds, from, to, maximum) {
407
+ if (calendarIds.length === 0)
408
+ return [];
409
+ const placeholders = calendarIds.map(() => '?').join(', ');
410
+ return this.database
411
+ .prepare(`SELECT * FROM calendar_events
412
+ WHERE calendar_id IN (${placeholders})
413
+ AND start_at >= ? AND start_at < ?
414
+ ORDER BY start_at ASC
415
+ LIMIT ?`)
416
+ .all(...calendarIds, from, to, maximum);
417
+ }
418
+ async listRecurringByCalendars(calendarIds, maximum) {
419
+ if (calendarIds.length === 0)
420
+ return [];
421
+ const placeholders = calendarIds.map(() => '?').join(', ');
422
+ return this.database
423
+ .prepare(`SELECT * FROM calendar_events
424
+ WHERE calendar_id IN (${placeholders})
425
+ AND recurrence IS NOT NULL
426
+ ORDER BY start_at ASC
427
+ LIMIT ?`)
428
+ .all(...calendarIds, maximum);
429
+ }
292
430
  async findByOwner(eventId, userId) {
293
431
  return (this.database
294
432
  .prepare('SELECT * FROM calendar_events WHERE id = ? AND user_id = ?')
295
433
  .get(eventId, userId) ?? null);
296
434
  }
435
+ async findById(eventId) {
436
+ return (this.database
437
+ .prepare('SELECT * FROM calendar_events WHERE id = ?')
438
+ .get(eventId) ?? null);
439
+ }
440
+ async listWithReminders(maximum) {
441
+ return this.database
442
+ .prepare(`SELECT * FROM calendar_events
443
+ WHERE reminder_minutes IS NOT NULL
444
+ ORDER BY start_at ASC
445
+ LIMIT ?`)
446
+ .all(maximum);
447
+ }
448
+ async markReminded(eventId, occurrenceStart) {
449
+ return (this.database
450
+ .prepare(`UPDATE calendar_events
451
+ SET last_reminded_occurrence = ?
452
+ WHERE id = ?
453
+ AND (last_reminded_occurrence IS NULL
454
+ OR last_reminded_occurrence < ?)`)
455
+ .run(occurrenceStart, eventId, occurrenceStart).changes > 0);
456
+ }
297
457
  async replaceWithLimit(event, maximum) {
298
458
  const replace = this.database.transaction(() => {
299
459
  const exists = ensureSameOwner(this.database, 'calendar_events', event.id, event.user_id);
@@ -308,8 +468,9 @@ class SQLiteCalendarEventRepository {
308
468
  this.database
309
469
  .prepare(`INSERT INTO calendar_events
310
470
  (id, user_id, title, notes, start_at, end_at, all_day,
311
- recurrence, created_at, updated_at)
312
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
471
+ recurrence, calendar_id, reminder_minutes,
472
+ last_reminded_occurrence, created_at, updated_at)
473
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
313
474
  ON CONFLICT(id) DO UPDATE SET
314
475
  title = excluded.title,
315
476
  notes = excluded.notes,
@@ -317,186 +478,1175 @@ class SQLiteCalendarEventRepository {
317
478
  end_at = excluded.end_at,
318
479
  all_day = excluded.all_day,
319
480
  recurrence = excluded.recurrence,
481
+ calendar_id = excluded.calendar_id,
482
+ reminder_minutes = excluded.reminder_minutes,
483
+ last_reminded_occurrence = excluded.last_reminded_occurrence,
320
484
  updated_at = excluded.updated_at
321
485
  WHERE calendar_events.user_id = excluded.user_id`)
322
- .run(event.id, event.user_id, event.title, event.notes, event.start_at, event.end_at, event.all_day, event.recurrence, event.created_at, event.updated_at);
486
+ .run(event.id, event.user_id, event.title, event.notes, event.start_at, event.end_at, event.all_day, event.recurrence, event.calendar_id, event.reminder_minutes, event.last_reminded_occurrence, event.created_at, event.updated_at);
487
+ });
488
+ replace();
489
+ }
490
+ async deleteByOwner(eventId, userId) {
491
+ return (this.database
492
+ .prepare('DELETE FROM calendar_events WHERE id = ? AND user_id = ?')
493
+ .run(eventId, userId).changes > 0);
494
+ }
495
+ }
496
+ class SQLiteCalendarRepository {
497
+ constructor(database) {
498
+ this.database = database;
499
+ }
500
+ async listByOwner(userId, maximum) {
501
+ return this.database
502
+ .prepare(`SELECT * FROM calendars
503
+ WHERE user_id = ?
504
+ ORDER BY created_at ASC
505
+ LIMIT ?`)
506
+ .all(userId, maximum);
507
+ }
508
+ async findByOwner(calendarId, userId) {
509
+ return (this.database
510
+ .prepare('SELECT * FROM calendars WHERE id = ? AND user_id = ?')
511
+ .get(calendarId, userId) ?? null);
512
+ }
513
+ async findById(calendarId) {
514
+ return (this.database
515
+ .prepare('SELECT * FROM calendars WHERE id = ?')
516
+ .get(calendarId) ?? null);
517
+ }
518
+ async replaceWithLimit(calendar, maximum) {
519
+ const replace = this.database.transaction(() => {
520
+ const exists = ensureSameOwner(this.database, 'calendars', calendar.id, calendar.user_id);
521
+ if (!exists) {
522
+ const row = this.database
523
+ .prepare('SELECT COUNT(*) AS count FROM calendars WHERE user_id = ?')
524
+ .get(calendar.user_id);
525
+ if (row.count >= maximum) {
526
+ throw new PersistenceResourceLimitError('calendar', maximum);
527
+ }
528
+ }
529
+ this.database
530
+ .prepare(`INSERT INTO calendars
531
+ (id, user_id, name, color, created_at, updated_at)
532
+ VALUES (?, ?, ?, ?, ?, ?)
533
+ ON CONFLICT(id) DO UPDATE SET
534
+ name = excluded.name,
535
+ color = excluded.color,
536
+ updated_at = excluded.updated_at
537
+ WHERE calendars.user_id = excluded.user_id`)
538
+ .run(calendar.id, calendar.user_id, calendar.name, calendar.color, calendar.created_at, calendar.updated_at);
539
+ });
540
+ replace();
541
+ }
542
+ async deleteAndDetach(calendarId, userId) {
543
+ const remove = this.database.transaction(() => {
544
+ const deleted = this.database
545
+ .prepare('DELETE FROM calendars WHERE id = ? AND user_id = ?')
546
+ .run(calendarId, userId).changes > 0;
547
+ if (deleted) {
548
+ this.database
549
+ .prepare(`UPDATE calendar_events
550
+ SET calendar_id = NULL
551
+ WHERE calendar_id = ? AND user_id = ?`)
552
+ .run(calendarId, userId);
553
+ }
554
+ return deleted;
555
+ });
556
+ return remove();
557
+ }
558
+ }
559
+ class SQLiteChannelRepository {
560
+ constructor(database) {
561
+ this.database = database;
562
+ }
563
+ async insertWithOwner(channel, owner, maximumPerUser) {
564
+ const insert = this.database.transaction(() => {
565
+ const count = this.database
566
+ .prepare(`SELECT COUNT(*) AS count
567
+ FROM channels
568
+ WHERE created_by = ?`)
569
+ .get(owner.user_id);
570
+ if (count.count >= maximumPerUser) {
571
+ throw new PersistenceResourceLimitError('channel', maximumPerUser);
572
+ }
573
+ this.database
574
+ .prepare(`INSERT INTO channels
575
+ (id, type, name, description, dm_key, created_by,
576
+ created_at, updated_at, archived_at)
577
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)
578
+ .run(channel.id, channel.type, channel.name, channel.description, channel.dm_key, channel.created_by, channel.created_at, channel.updated_at, channel.archived_at);
579
+ this.database
580
+ .prepare(`INSERT INTO channel_members
581
+ (channel_id, user_id, role, joined_at, last_read_at)
582
+ VALUES (?, ?, ?, ?, ?)`)
583
+ .run(owner.channel_id, owner.user_id, owner.role, owner.joined_at, owner.last_read_at);
584
+ });
585
+ insert();
586
+ }
587
+ async findById(channelId) {
588
+ return (this.database
589
+ .prepare('SELECT * FROM channels WHERE id = ?')
590
+ .get(channelId) ?? null);
591
+ }
592
+ async findByDmKey(dmKey) {
593
+ return (this.database
594
+ .prepare('SELECT * FROM channels WHERE dm_key = ?')
595
+ .get(dmKey) ?? null);
596
+ }
597
+ async listForUser(userId, maximum) {
598
+ const rows = this.database
599
+ .prepare(`SELECT
600
+ c.id, c.type, c.name, c.description, c.dm_key, c.created_by,
601
+ c.created_at, c.updated_at, c.archived_at,
602
+ m.channel_id AS member_channel_id, m.user_id AS member_user_id,
603
+ m.role AS member_role, m.joined_at AS member_joined_at,
604
+ m.last_read_at AS member_last_read_at
605
+ FROM channel_members m
606
+ JOIN channels c ON c.id = m.channel_id
607
+ WHERE m.user_id = ?
608
+ ORDER BY c.updated_at DESC
609
+ LIMIT ?`)
610
+ .all(userId, maximum);
611
+ return rows.map(row => ({
612
+ channel: {
613
+ id: row.id,
614
+ type: row.type,
615
+ name: row.name,
616
+ description: row.description,
617
+ dm_key: row.dm_key,
618
+ created_by: row.created_by,
619
+ created_at: row.created_at,
620
+ updated_at: row.updated_at,
621
+ archived_at: row.archived_at,
622
+ },
623
+ member: {
624
+ channel_id: row.member_channel_id,
625
+ user_id: row.member_user_id,
626
+ role: row.member_role,
627
+ joined_at: row.member_joined_at,
628
+ last_read_at: row.member_last_read_at,
629
+ },
630
+ }));
631
+ }
632
+ async listPublic(maximum) {
633
+ return this.database
634
+ .prepare(`SELECT * FROM channels
635
+ WHERE type = 'public' AND archived_at IS NULL
636
+ ORDER BY updated_at DESC
637
+ LIMIT ?`)
638
+ .all(maximum);
639
+ }
640
+ async update(channel) {
641
+ this.database
642
+ .prepare(`UPDATE channels SET
643
+ name = ?,
644
+ description = ?,
645
+ updated_at = ?,
646
+ archived_at = ?
647
+ WHERE id = ?`)
648
+ .run(channel.name, channel.description, channel.updated_at, channel.archived_at, channel.id);
649
+ }
650
+ async delete(channelId) {
651
+ return (this.database.prepare('DELETE FROM channels WHERE id = ?').run(channelId)
652
+ .changes > 0);
653
+ }
654
+ async upsertMember(member, maximumMembers) {
655
+ const upsert = this.database.transaction(() => {
656
+ const existing = this.database
657
+ .prepare('SELECT 1 FROM channel_members WHERE channel_id = ? AND user_id = ?')
658
+ .get(member.channel_id, member.user_id);
659
+ if (!existing) {
660
+ const count = this.database
661
+ .prepare('SELECT COUNT(*) AS count FROM channel_members WHERE channel_id = ?')
662
+ .get(member.channel_id);
663
+ if (count.count >= maximumMembers) {
664
+ throw new PersistenceResourceLimitError('channel-member', maximumMembers);
665
+ }
666
+ }
667
+ this.database
668
+ .prepare(`INSERT INTO channel_members
669
+ (channel_id, user_id, role, joined_at, last_read_at)
670
+ VALUES (?, ?, ?, ?, ?)
671
+ ON CONFLICT(channel_id, user_id) DO UPDATE SET
672
+ role = excluded.role`)
673
+ .run(member.channel_id, member.user_id, member.role, member.joined_at, member.last_read_at);
674
+ });
675
+ upsert();
676
+ }
677
+ async findMember(channelId, userId) {
678
+ return (this.database
679
+ .prepare('SELECT * FROM channel_members WHERE channel_id = ? AND user_id = ?')
680
+ .get(channelId, userId) ??
681
+ null);
682
+ }
683
+ async listMembers(channelId, maximum) {
684
+ return this.database
685
+ .prepare(`SELECT * FROM channel_members
686
+ WHERE channel_id = ?
687
+ ORDER BY joined_at ASC
688
+ LIMIT ?`)
689
+ .all(channelId, maximum);
690
+ }
691
+ async removeMember(channelId, userId) {
692
+ return (this.database
693
+ .prepare('DELETE FROM channel_members WHERE channel_id = ? AND user_id = ?')
694
+ .run(channelId, userId).changes > 0);
695
+ }
696
+ async advanceLastRead(channelId, userId, lastReadAt) {
697
+ return (this.database
698
+ .prepare(`UPDATE channel_members
699
+ SET last_read_at = ?
700
+ WHERE channel_id = ? AND user_id = ? AND last_read_at < ?`)
701
+ .run(lastReadAt, channelId, userId, lastReadAt).changes > 0);
702
+ }
703
+ async unreadSummaryForUser(userId) {
704
+ return this.database
705
+ .prepare(`SELECT
706
+ m.channel_id,
707
+ COUNT(msg.id) AS unread_count,
708
+ MAX(msg.created_at) AS latest_message_at
709
+ FROM channel_members m
710
+ LEFT JOIN channel_messages msg
711
+ ON msg.channel_id = m.channel_id
712
+ AND msg.created_at > m.last_read_at
713
+ AND msg.deleted_at IS NULL
714
+ AND (msg.user_id IS NULL OR msg.user_id != m.user_id
715
+ OR msg.author_kind != 'user')
716
+ WHERE m.user_id = ?
717
+ GROUP BY m.channel_id`)
718
+ .all(userId);
719
+ }
720
+ }
721
+ class SQLiteChannelMessageRepository {
722
+ constructor(database) {
723
+ this.database = database;
724
+ }
725
+ insertMessageRow(message) {
726
+ this.database
727
+ .prepare(`INSERT INTO channel_messages
728
+ (id, channel_id, user_id, parent_id, author_kind, model, content,
729
+ metadata, created_at, updated_at, edited_at, deleted_at,
730
+ pinned_at, pinned_by)
731
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
732
+ .run(message.id, message.channel_id, message.user_id, message.parent_id, message.author_kind, message.model, message.content, message.metadata, message.created_at, message.updated_at, message.edited_at, message.deleted_at, message.pinned_at, message.pinned_by);
733
+ }
734
+ async insertIfAbsent(message, attachments, maximumPerChannel) {
735
+ const insert = this.database.transaction(() => {
736
+ const existing = this.database
737
+ .prepare('SELECT * FROM channel_messages WHERE id = ?')
738
+ .get(message.id);
739
+ if (existing) {
740
+ return { stored: existing, inserted: false };
741
+ }
742
+ const count = this.database
743
+ .prepare('SELECT COUNT(*) AS count FROM channel_messages WHERE channel_id = ?')
744
+ .get(message.channel_id);
745
+ if (count.count >= maximumPerChannel) {
746
+ throw new PersistenceResourceLimitError('channel-message', maximumPerChannel);
747
+ }
748
+ this.insertMessageRow(message);
749
+ const insertAttachment = this.database.prepare(`INSERT INTO channel_attachments
750
+ (id, message_id, channel_id, blob_id, filename, content_type,
751
+ size, created_by, created_at)
752
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`);
753
+ for (const attachment of attachments) {
754
+ if (attachment.message_id !== message.id) {
755
+ throw new Error('A channel attachment does not belong to its message');
756
+ }
757
+ insertAttachment.run(attachment.id, attachment.message_id, attachment.channel_id, attachment.blob_id, attachment.filename, attachment.content_type, attachment.size, attachment.created_by, attachment.created_at);
758
+ }
759
+ this.database
760
+ .prepare('UPDATE channels SET updated_at = ? WHERE id = ?')
761
+ .run(message.created_at, message.channel_id);
762
+ return { stored: message, inserted: true };
763
+ });
764
+ return insert();
765
+ }
766
+ async findById(messageId) {
767
+ return (this.database
768
+ .prepare('SELECT * FROM channel_messages WHERE id = ?')
769
+ .get(messageId) ?? null);
770
+ }
771
+ async listPage(channelId, options) {
772
+ const clauses = ['channel_id = ?'];
773
+ const parameters = [channelId];
774
+ if (options.parentId === null) {
775
+ clauses.push('parent_id IS NULL');
776
+ }
777
+ else if (options.parentId !== undefined) {
778
+ clauses.push('parent_id = ?');
779
+ parameters.push(options.parentId);
780
+ }
781
+ if (options.before) {
782
+ clauses.push('(created_at < ? OR (created_at = ? AND id < ?))');
783
+ parameters.push(options.before.created_at, options.before.created_at, options.before.id);
784
+ }
785
+ if (options.after) {
786
+ clauses.push('(created_at > ? OR (created_at = ? AND id > ?))');
787
+ parameters.push(options.after.created_at, options.after.created_at, options.after.id);
788
+ }
789
+ const direction = options.after ? 'ASC' : 'DESC';
790
+ parameters.push(options.limit);
791
+ return this.database
792
+ .prepare(`SELECT * FROM channel_messages
793
+ WHERE ${clauses.join(' AND ')}
794
+ ORDER BY created_at ${direction}, id ${direction}
795
+ LIMIT ?`)
796
+ .all(...parameters);
797
+ }
798
+ async listThread(parentId, maximum) {
799
+ return this.database
800
+ .prepare(`SELECT * FROM channel_messages
801
+ WHERE parent_id = ?
802
+ ORDER BY created_at ASC, id ASC
803
+ LIMIT ?`)
804
+ .all(parentId, maximum);
805
+ }
806
+ async countThreadReplies(parentIds) {
807
+ if (parentIds.length === 0)
808
+ return {};
809
+ const placeholders = parentIds.map(() => '?').join(', ');
810
+ const rows = this.database
811
+ .prepare(`SELECT parent_id, COUNT(*) AS count
812
+ FROM channel_messages
813
+ WHERE parent_id IN (${placeholders}) AND deleted_at IS NULL
814
+ GROUP BY parent_id`)
815
+ .all(...parentIds);
816
+ const counts = {};
817
+ for (const row of rows)
818
+ counts[row.parent_id] = row.count;
819
+ return counts;
820
+ }
821
+ async update(message) {
822
+ this.database
823
+ .prepare(`UPDATE channel_messages SET
824
+ content = ?,
825
+ metadata = ?,
826
+ updated_at = ?,
827
+ edited_at = ?,
828
+ deleted_at = ?,
829
+ pinned_at = ?,
830
+ pinned_by = ?
831
+ WHERE id = ?`)
832
+ .run(message.content, message.metadata, message.updated_at, message.edited_at, message.deleted_at, message.pinned_at, message.pinned_by, message.id);
833
+ }
834
+ async listPinned(channelId, maximum) {
835
+ return this.database
836
+ .prepare(`SELECT * FROM channel_messages
837
+ WHERE channel_id = ? AND pinned_at IS NOT NULL AND deleted_at IS NULL
838
+ ORDER BY pinned_at DESC
839
+ LIMIT ?`)
840
+ .all(channelId, maximum);
841
+ }
842
+ async listRecentForChannels(channelIds, maximum) {
843
+ if (channelIds.length === 0)
844
+ return [];
845
+ const placeholders = channelIds.map(() => '?').join(', ');
846
+ return this.database
847
+ .prepare(`SELECT * FROM channel_messages
848
+ WHERE channel_id IN (${placeholders}) AND deleted_at IS NULL
849
+ ORDER BY created_at DESC
850
+ LIMIT ?`)
851
+ .all(...channelIds, maximum);
852
+ }
853
+ async addReaction(reaction, maximumPerMessage) {
854
+ const add = this.database.transaction(() => {
855
+ const existing = this.database
856
+ .prepare(`SELECT 1 FROM channel_reactions
857
+ WHERE message_id = ? AND user_id = ? AND emoji = ?`)
858
+ .get(reaction.message_id, reaction.user_id, reaction.emoji);
859
+ if (existing)
860
+ return false;
861
+ const count = this.database
862
+ .prepare('SELECT COUNT(*) AS count FROM channel_reactions WHERE message_id = ?')
863
+ .get(reaction.message_id);
864
+ if (count.count >= maximumPerMessage) {
865
+ throw new PersistenceResourceLimitError('channel-reaction', maximumPerMessage);
866
+ }
867
+ this.database
868
+ .prepare(`INSERT INTO channel_reactions
869
+ (id, message_id, user_id, emoji, created_at)
870
+ VALUES (?, ?, ?, ?, ?)`)
871
+ .run(reaction.id, reaction.message_id, reaction.user_id, reaction.emoji, reaction.created_at);
872
+ return true;
873
+ });
874
+ return add();
875
+ }
876
+ async removeReaction(messageId, userId, emoji) {
877
+ return (this.database
878
+ .prepare(`DELETE FROM channel_reactions
879
+ WHERE message_id = ? AND user_id = ? AND emoji = ?`)
880
+ .run(messageId, userId, emoji).changes > 0);
881
+ }
882
+ async listReactionsForMessages(messageIds) {
883
+ if (messageIds.length === 0)
884
+ return [];
885
+ const placeholders = messageIds.map(() => '?').join(', ');
886
+ return this.database
887
+ .prepare(`SELECT * FROM channel_reactions
888
+ WHERE message_id IN (${placeholders})
889
+ ORDER BY created_at ASC`)
890
+ .all(...messageIds);
891
+ }
892
+ async findAttachment(attachmentId) {
893
+ return (this.database
894
+ .prepare('SELECT * FROM channel_attachments WHERE id = ?')
895
+ .get(attachmentId) ?? null);
896
+ }
897
+ async listAttachmentsForMessages(messageIds) {
898
+ if (messageIds.length === 0)
899
+ return [];
900
+ const placeholders = messageIds.map(() => '?').join(', ');
901
+ return this.database
902
+ .prepare(`SELECT * FROM channel_attachments
903
+ WHERE message_id IN (${placeholders})
904
+ ORDER BY created_at ASC`)
905
+ .all(...messageIds);
906
+ }
907
+ async listAttachmentBlobIds(channelId) {
908
+ return this.database
909
+ .prepare('SELECT blob_id, created_by FROM channel_attachments WHERE channel_id = ?')
910
+ .all(channelId);
911
+ }
912
+ }
913
+ class SQLiteNotificationRepository {
914
+ constructor(database) {
915
+ this.database = database;
916
+ }
917
+ async insertWithLimit(notification, maximumPerUser) {
918
+ const insert = this.database.transaction(() => {
919
+ const result = this.database
920
+ .prepare(`INSERT INTO notifications
921
+ (id, user_id, type, title, body, href, source_key,
922
+ created_at, read_at)
923
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
924
+ ON CONFLICT(user_id, source_key)
925
+ WHERE source_key IS NOT NULL
926
+ DO NOTHING`)
927
+ .run(notification.id, notification.user_id, notification.type, notification.title, notification.body, notification.href, notification.source_key, notification.created_at, notification.read_at);
928
+ if (result.changes === 0)
929
+ return false;
930
+ this.database
931
+ .prepare(`DELETE FROM notifications
932
+ WHERE user_id = ?
933
+ AND id NOT IN (
934
+ SELECT id FROM notifications
935
+ WHERE user_id = ?
936
+ ORDER BY created_at DESC, id DESC
937
+ LIMIT ?
938
+ )`)
939
+ .run(notification.user_id, notification.user_id, maximumPerUser);
940
+ return true;
941
+ });
942
+ return insert();
943
+ }
944
+ async listByOwner(userId, options) {
945
+ const clauses = ['user_id = ?'];
946
+ const parameters = [userId];
947
+ if (options.before !== undefined) {
948
+ clauses.push('created_at < ?');
949
+ parameters.push(options.before);
950
+ }
951
+ if (options.unreadOnly) {
952
+ clauses.push('read_at IS NULL');
953
+ }
954
+ parameters.push(options.limit);
955
+ return this.database
956
+ .prepare(`SELECT * FROM notifications
957
+ WHERE ${clauses.join(' AND ')}
958
+ ORDER BY created_at DESC, id DESC
959
+ LIMIT ?`)
960
+ .all(...parameters);
961
+ }
962
+ async countUnread(userId) {
963
+ const row = this.database
964
+ .prepare(`SELECT COUNT(*) AS count FROM notifications
965
+ WHERE user_id = ? AND read_at IS NULL`)
966
+ .get(userId);
967
+ return row.count;
968
+ }
969
+ async markRead(notificationId, userId, readAt) {
970
+ return (this.database
971
+ .prepare(`UPDATE notifications SET read_at = ?
972
+ WHERE id = ? AND user_id = ? AND read_at IS NULL`)
973
+ .run(readAt, notificationId, userId).changes > 0);
974
+ }
975
+ async markAllRead(userId, readAt) {
976
+ return this.database
977
+ .prepare(`UPDATE notifications SET read_at = ?
978
+ WHERE user_id = ? AND read_at IS NULL`)
979
+ .run(readAt, userId).changes;
980
+ }
981
+ async deleteByOwner(notificationId, userId) {
982
+ return (this.database
983
+ .prepare('DELETE FROM notifications WHERE id = ? AND user_id = ?')
984
+ .run(notificationId, userId).changes > 0);
985
+ }
986
+ }
987
+ class SQLiteWebhookTargetRepository {
988
+ constructor(database) {
989
+ this.database = database;
990
+ }
991
+ async list(maximum) {
992
+ return this.database
993
+ .prepare('SELECT * FROM webhook_targets ORDER BY created_at ASC LIMIT ?')
994
+ .all(maximum);
995
+ }
996
+ async findById(targetId) {
997
+ return (this.database
998
+ .prepare('SELECT * FROM webhook_targets WHERE id = ?')
999
+ .get(targetId) ?? null);
1000
+ }
1001
+ async replaceWithLimit(target, maximum) {
1002
+ const replace = this.database.transaction(() => {
1003
+ const existing = this.database
1004
+ .prepare('SELECT 1 FROM webhook_targets WHERE id = ?')
1005
+ .get(target.id);
1006
+ if (!existing) {
1007
+ const row = this.database
1008
+ .prepare('SELECT COUNT(*) AS count FROM webhook_targets')
1009
+ .get();
1010
+ if (row.count >= maximum) {
1011
+ throw new PersistenceResourceLimitError('webhook-target', maximum);
1012
+ }
1013
+ }
1014
+ this.database
1015
+ .prepare(`INSERT INTO webhook_targets
1016
+ (id, name, url, secret, events, enabled, created_by,
1017
+ created_at, updated_at)
1018
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
1019
+ ON CONFLICT(id) DO UPDATE SET
1020
+ name = excluded.name,
1021
+ url = excluded.url,
1022
+ secret = excluded.secret,
1023
+ events = excluded.events,
1024
+ enabled = excluded.enabled,
1025
+ updated_at = excluded.updated_at`)
1026
+ .run(target.id, target.name, target.url, target.secret, target.events, target.enabled, target.created_by, target.created_at, target.updated_at);
323
1027
  });
324
1028
  replace();
325
1029
  }
326
- async deleteByOwner(eventId, userId) {
1030
+ async delete(targetId) {
1031
+ return (this.database
1032
+ .prepare('DELETE FROM webhook_targets WHERE id = ?')
1033
+ .run(targetId).changes > 0);
1034
+ }
1035
+ }
1036
+ class SQLiteAutomationRepository {
1037
+ constructor(database) {
1038
+ this.database = database;
1039
+ }
1040
+ async listByOwner(userId, maximum) {
1041
+ return this.database
1042
+ .prepare(`SELECT * FROM automations
1043
+ WHERE user_id = ?
1044
+ ORDER BY updated_at DESC
1045
+ LIMIT ?`)
1046
+ .all(userId, maximum);
1047
+ }
1048
+ async findByOwner(automationId, userId) {
1049
+ return (this.database
1050
+ .prepare('SELECT * FROM automations WHERE id = ? AND user_id = ?')
1051
+ .get(automationId, userId) ??
1052
+ null);
1053
+ }
1054
+ async findById(automationId) {
1055
+ return (this.database
1056
+ .prepare('SELECT * FROM automations WHERE id = ?')
1057
+ .get(automationId) ?? null);
1058
+ }
1059
+ async replaceWithLimit(automation, maximum) {
1060
+ const replace = this.database.transaction(() => {
1061
+ const exists = ensureSameOwner(this.database, 'automations', automation.id, automation.user_id);
1062
+ if (!exists) {
1063
+ const row = this.database
1064
+ .prepare('SELECT COUNT(*) AS count FROM automations WHERE user_id = ?')
1065
+ .get(automation.user_id);
1066
+ if (row.count >= maximum) {
1067
+ throw new PersistenceResourceLimitError('automation', maximum);
1068
+ }
1069
+ }
1070
+ this.database
1071
+ .prepare(`INSERT INTO automations
1072
+ (id, user_id, name, instructions, triggers, provider, model,
1073
+ notify, status, next_run_at, last_run_at, created_at, updated_at)
1074
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1075
+ ON CONFLICT(id) DO UPDATE SET
1076
+ name = excluded.name,
1077
+ instructions = excluded.instructions,
1078
+ triggers = excluded.triggers,
1079
+ provider = excluded.provider,
1080
+ model = excluded.model,
1081
+ notify = excluded.notify,
1082
+ status = excluded.status,
1083
+ next_run_at = excluded.next_run_at,
1084
+ last_run_at = excluded.last_run_at,
1085
+ updated_at = excluded.updated_at
1086
+ WHERE automations.user_id = excluded.user_id`)
1087
+ .run(automation.id, automation.user_id, automation.name, automation.instructions, automation.triggers, automation.provider, automation.model, automation.notify, automation.status, automation.next_run_at, automation.last_run_at, automation.created_at, automation.updated_at);
1088
+ });
1089
+ replace();
1090
+ }
1091
+ async listDue(now, maximum) {
1092
+ return this.database
1093
+ .prepare(`SELECT * FROM automations
1094
+ WHERE status = 'active'
1095
+ AND next_run_at IS NOT NULL
1096
+ AND next_run_at <= ?
1097
+ ORDER BY next_run_at ASC
1098
+ LIMIT ?`)
1099
+ .all(now, maximum);
1100
+ }
1101
+ async advanceNextRun(automationId, observedNextRunAt, nextRunAt, lastRunAt) {
1102
+ return (this.database
1103
+ .prepare(`UPDATE automations
1104
+ SET next_run_at = ?, last_run_at = ?
1105
+ WHERE id = ? AND next_run_at = ?`)
1106
+ .run(nextRunAt, lastRunAt, automationId, observedNextRunAt).changes > 0);
1107
+ }
1108
+ async setStatus(automationId, userId, status, nextRunAt, updatedAt) {
1109
+ return (this.database
1110
+ .prepare(`UPDATE automations
1111
+ SET status = ?, next_run_at = ?, updated_at = ?
1112
+ WHERE id = ? AND user_id = ?`)
1113
+ .run(status, nextRunAt, updatedAt, automationId, userId).changes > 0);
1114
+ }
1115
+ async deleteByOwner(automationId, userId) {
1116
+ return (this.database
1117
+ .prepare('DELETE FROM automations WHERE id = ? AND user_id = ?')
1118
+ .run(automationId, userId).changes > 0);
1119
+ }
1120
+ }
1121
+ class SQLiteAutomationRunRepository {
1122
+ constructor(database) {
1123
+ this.database = database;
1124
+ }
1125
+ async insert(run) {
1126
+ this.database
1127
+ .prepare(`INSERT INTO automation_runs
1128
+ (id, automation_id, user_id, scheduled_for, started_at,
1129
+ finished_at, status, session_id, assistant_message_id, error,
1130
+ seen_at, created_at)
1131
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
1132
+ .run(run.id, run.automation_id, run.user_id, run.scheduled_for, run.started_at, run.finished_at, run.status, run.session_id, run.assistant_message_id, run.error, run.seen_at, run.created_at);
1133
+ }
1134
+ async findByOwner(runId, userId) {
1135
+ return (this.database
1136
+ .prepare('SELECT * FROM automation_runs WHERE id = ? AND user_id = ?')
1137
+ .get(runId, userId) ?? null);
1138
+ }
1139
+ async listByOwner(userId, options) {
1140
+ const clauses = ['user_id = ?'];
1141
+ const values = [userId];
1142
+ if (options.automationId !== undefined) {
1143
+ clauses.push('automation_id = ?');
1144
+ values.push(options.automationId);
1145
+ }
1146
+ if (options.from !== undefined) {
1147
+ clauses.push('scheduled_for >= ?');
1148
+ values.push(options.from);
1149
+ }
1150
+ if (options.to !== undefined) {
1151
+ clauses.push('scheduled_for < ?');
1152
+ values.push(options.to);
1153
+ }
1154
+ values.push(options.maximum);
1155
+ return this.database
1156
+ .prepare(`SELECT * FROM automation_runs
1157
+ WHERE ${clauses.join(' AND ')}
1158
+ ORDER BY scheduled_for DESC
1159
+ LIMIT ?`)
1160
+ .all(...values);
1161
+ }
1162
+ async listUnfinished(maximum) {
1163
+ return this.database
1164
+ .prepare(`SELECT * FROM automation_runs
1165
+ WHERE status IN ('queued', 'running')
1166
+ ORDER BY scheduled_for ASC
1167
+ LIMIT ?`)
1168
+ .all(maximum);
1169
+ }
1170
+ async markStarted(runId, sessionId, assistantMessageId, startedAt) {
1171
+ return (this.database
1172
+ .prepare(`UPDATE automation_runs
1173
+ SET session_id = ?, assistant_message_id = ?, started_at = ?,
1174
+ status = 'running'
1175
+ WHERE id = ? AND status = 'queued'`)
1176
+ .run(sessionId, assistantMessageId, startedAt, runId).changes > 0);
1177
+ }
1178
+ async finalize(runId, status, finishedAt, error) {
1179
+ return (this.database
1180
+ .prepare(`UPDATE automation_runs
1181
+ SET status = ?, finished_at = ?, error = ?
1182
+ WHERE id = ? AND status IN ('queued', 'running')`)
1183
+ .run(status, finishedAt, error, runId).changes > 0);
1184
+ }
1185
+ async countUnseenFinished(userId) {
1186
+ const row = this.database
1187
+ .prepare(`SELECT COUNT(*) AS count FROM automation_runs
1188
+ WHERE user_id = ?
1189
+ AND status IN ('succeeded', 'failed')
1190
+ AND seen_at IS NULL`)
1191
+ .get(userId);
1192
+ return row.count;
1193
+ }
1194
+ async markSeenBefore(userId, seenAt) {
1195
+ return this.database
1196
+ .prepare(`UPDATE automation_runs
1197
+ SET seen_at = ?
1198
+ WHERE user_id = ?
1199
+ AND status IN ('succeeded', 'failed')
1200
+ AND seen_at IS NULL
1201
+ AND finished_at IS NOT NULL
1202
+ AND finished_at <= ?`)
1203
+ .run(seenAt, userId, seenAt).changes;
1204
+ }
1205
+ }
1206
+ class SQLiteToolServerRepository {
1207
+ constructor(database) {
1208
+ this.database = database;
1209
+ }
1210
+ async list(maximum) {
1211
+ return this.database
1212
+ .prepare(`SELECT * FROM tool_servers
1213
+ ORDER BY updated_at DESC
1214
+ LIMIT ?`)
1215
+ .all(maximum);
1216
+ }
1217
+ async findById(serverId) {
1218
+ return (this.database
1219
+ .prepare('SELECT * FROM tool_servers WHERE id = ?')
1220
+ .get(serverId) ?? null);
1221
+ }
1222
+ async replaceWithLimit(server, maximum) {
1223
+ const replace = this.database.transaction(() => {
1224
+ const exists = this.database
1225
+ .prepare('SELECT id FROM tool_servers WHERE id = ?')
1226
+ .get(server.id);
1227
+ if (!exists) {
1228
+ const row = this.database
1229
+ .prepare('SELECT COUNT(*) AS count FROM tool_servers')
1230
+ .get();
1231
+ if (row.count >= maximum) {
1232
+ throw new PersistenceResourceLimitError('tool-server', maximum);
1233
+ }
1234
+ }
1235
+ this.database
1236
+ .prepare(`INSERT INTO tool_servers
1237
+ (id, user_id, name, description, kind, base_url, spec,
1238
+ spec_digest, spec_revision, auth_mode, auth_header, access_mode,
1239
+ enabled, timeout_ms, max_response_bytes, created_at, updated_at)
1240
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1241
+ ON CONFLICT(id) DO UPDATE SET
1242
+ name = excluded.name,
1243
+ description = excluded.description,
1244
+ base_url = excluded.base_url,
1245
+ spec = excluded.spec,
1246
+ spec_digest = excluded.spec_digest,
1247
+ spec_revision = excluded.spec_revision,
1248
+ auth_mode = excluded.auth_mode,
1249
+ auth_header = excluded.auth_header,
1250
+ access_mode = excluded.access_mode,
1251
+ enabled = excluded.enabled,
1252
+ timeout_ms = excluded.timeout_ms,
1253
+ max_response_bytes = excluded.max_response_bytes,
1254
+ updated_at = excluded.updated_at`)
1255
+ .run(server.id, server.user_id, server.name, server.description, server.kind, server.base_url, server.spec, server.spec_digest, server.spec_revision, server.auth_mode, server.auth_header, server.access_mode, server.enabled, server.timeout_ms, server.max_response_bytes, server.created_at, server.updated_at);
1256
+ });
1257
+ replace();
1258
+ }
1259
+ async delete(serverId) {
1260
+ return (this.database
1261
+ .prepare('DELETE FROM tool_servers WHERE id = ?')
1262
+ .run(serverId).changes > 0);
1263
+ }
1264
+ }
1265
+ class SQLiteToolServerToolRepository {
1266
+ constructor(database) {
1267
+ this.database = database;
1268
+ }
1269
+ async listByServer(serverId) {
1270
+ return this.database
1271
+ .prepare(`SELECT * FROM tool_server_tools
1272
+ WHERE server_id = ?
1273
+ ORDER BY name ASC`)
1274
+ .all(serverId);
1275
+ }
1276
+ async replaceAllForServer(serverId, tools) {
1277
+ const replace = this.database.transaction(() => {
1278
+ const previous = new Map(this.database
1279
+ .prepare('SELECT name, enabled, side_effect FROM tool_server_tools WHERE server_id = ?')
1280
+ .all(serverId).map(row => [row.name, row]));
1281
+ this.database
1282
+ .prepare('DELETE FROM tool_server_tools WHERE server_id = ?')
1283
+ .run(serverId);
1284
+ const insert = this.database.prepare(`INSERT INTO tool_server_tools
1285
+ (id, server_id, name, description, params_schema, detail,
1286
+ side_effect, enabled, created_at, updated_at)
1287
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
1288
+ for (const tool of tools) {
1289
+ if (tool.server_id !== serverId) {
1290
+ throw new Error('A tool row does not belong to its server');
1291
+ }
1292
+ const kept = previous.get(tool.name);
1293
+ insert.run(tool.id, tool.server_id, tool.name, tool.description, tool.params_schema, tool.detail, kept ? kept.side_effect : tool.side_effect, kept ? kept.enabled : tool.enabled, tool.created_at, tool.updated_at);
1294
+ }
1295
+ });
1296
+ replace();
1297
+ }
1298
+ async updateOverrides(serverId, toolName, overrides, updatedAt) {
1299
+ const assignments = ['updated_at = ?'];
1300
+ const values = [updatedAt];
1301
+ if (overrides.enabled !== undefined) {
1302
+ assignments.push('enabled = ?');
1303
+ values.push(overrides.enabled);
1304
+ }
1305
+ if (overrides.side_effect !== undefined) {
1306
+ assignments.push('side_effect = ?');
1307
+ values.push(overrides.side_effect);
1308
+ }
1309
+ const changed = this.database
1310
+ .prepare(`UPDATE tool_server_tools
1311
+ SET ${assignments.join(', ')}
1312
+ WHERE server_id = ? AND name = ?`)
1313
+ .run(...values, serverId, toolName).changes;
1314
+ if (changed === 0)
1315
+ return null;
1316
+ return (this.database
1317
+ .prepare('SELECT * FROM tool_server_tools WHERE server_id = ? AND name = ?')
1318
+ .get(serverId, toolName) ??
1319
+ null);
1320
+ }
1321
+ }
1322
+ class SQLiteToolServerCredentialRepository {
1323
+ constructor(database) {
1324
+ this.database = database;
1325
+ }
1326
+ async find(serverId, userId) {
1327
+ return (this.database
1328
+ .prepare('SELECT * FROM tool_server_credentials WHERE server_id = ? AND user_id = ?')
1329
+ .get(serverId, userId) ?? null);
1330
+ }
1331
+ async upsert(credential) {
1332
+ this.database
1333
+ .prepare(`INSERT INTO tool_server_credentials
1334
+ (id, server_id, user_id, secret, created_at, updated_at)
1335
+ VALUES (?, ?, ?, ?, ?, ?)
1336
+ ON CONFLICT(server_id, user_id) DO UPDATE SET
1337
+ secret = excluded.secret,
1338
+ updated_at = excluded.updated_at`)
1339
+ .run(credential.id, credential.server_id, credential.user_id, credential.secret, credential.created_at, credential.updated_at);
1340
+ }
1341
+ async delete(serverId, userId) {
1342
+ return (this.database
1343
+ .prepare('DELETE FROM tool_server_credentials WHERE server_id = ? AND user_id = ?')
1344
+ .run(serverId, userId).changes > 0);
1345
+ }
1346
+ }
1347
+ class SQLiteToolApprovalRepository {
1348
+ constructor(database) {
1349
+ this.database = database;
1350
+ }
1351
+ async insert(approval) {
1352
+ this.database
1353
+ .prepare(`INSERT INTO tool_approvals
1354
+ (id, user_id, session_id, server_id, tool_name, call_id,
1355
+ arguments_digest, scope, status, created_at, resolved_at,
1356
+ expires_at)
1357
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
1358
+ .run(approval.id, approval.user_id, approval.session_id, approval.server_id, approval.tool_name, approval.call_id, approval.arguments_digest, approval.scope, approval.status, approval.created_at, approval.resolved_at, approval.expires_at);
1359
+ }
1360
+ async findByOwner(approvalId, userId) {
1361
+ return (this.database
1362
+ .prepare('SELECT * FROM tool_approvals WHERE id = ? AND user_id = ?')
1363
+ .get(approvalId, userId) ??
1364
+ null);
1365
+ }
1366
+ async findStanding(userId, serverId, toolName, sessionId) {
1367
+ return (this.database
1368
+ .prepare(`SELECT * FROM tool_approvals
1369
+ WHERE user_id = ?
1370
+ AND status = 'approved'
1371
+ AND tool_name = ?
1372
+ AND ((server_id IS NULL AND ? IS NULL) OR server_id = ?)
1373
+ AND (scope = 'always'
1374
+ OR (scope = 'session' AND session_id IS NOT NULL AND session_id = ?))
1375
+ ORDER BY resolved_at DESC
1376
+ LIMIT 1`)
1377
+ .get(userId, toolName, serverId, serverId, sessionId) ?? null);
1378
+ }
1379
+ async resolvePending(approvalId, userId, status, scope, resolvedAt) {
1380
+ const changed = this.database
1381
+ .prepare(`UPDATE tool_approvals
1382
+ SET status = ?, scope = ?, resolved_at = ?
1383
+ WHERE id = ? AND user_id = ? AND status = 'pending'
1384
+ AND (expires_at IS NULL OR expires_at > ?)`)
1385
+ .run(status, scope, resolvedAt, approvalId, userId, resolvedAt).changes;
1386
+ if (changed === 0)
1387
+ return null;
1388
+ return this.findByOwner(approvalId, userId);
1389
+ }
1390
+ async listPendingByOwner(userId, maximum) {
1391
+ return this.database
1392
+ .prepare(`SELECT * FROM tool_approvals
1393
+ WHERE user_id = ? AND status = 'pending'
1394
+ ORDER BY created_at ASC
1395
+ LIMIT ?`)
1396
+ .all(userId, maximum);
1397
+ }
1398
+ async listStandingByOwner(userId, maximum) {
1399
+ return this.database
1400
+ .prepare(`SELECT * FROM tool_approvals
1401
+ WHERE user_id = ? AND status = 'approved'
1402
+ AND scope IN ('session', 'always')
1403
+ ORDER BY resolved_at DESC
1404
+ LIMIT ?`)
1405
+ .all(userId, maximum);
1406
+ }
1407
+ async expirePending(now) {
1408
+ return this.database
1409
+ .prepare(`UPDATE tool_approvals
1410
+ SET status = 'expired', resolved_at = ?
1411
+ WHERE status = 'pending' AND expires_at IS NOT NULL AND expires_at <= ?`)
1412
+ .run(now, now).changes;
1413
+ }
1414
+ async deleteByOwner(approvalId, userId) {
1415
+ return (this.database
1416
+ .prepare('DELETE FROM tool_approvals WHERE id = ? AND user_id = ?')
1417
+ .run(approvalId, userId).changes > 0);
1418
+ }
1419
+ }
1420
+ class SQLitePromptRepository {
1421
+ constructor(database) {
1422
+ this.database = database;
1423
+ }
1424
+ async listByOwner(userId, maximum) {
1425
+ return this.database
1426
+ .prepare(`SELECT * FROM prompts
1427
+ WHERE user_id = ?
1428
+ ORDER BY updated_at DESC
1429
+ LIMIT ?`)
1430
+ .all(userId, maximum);
1431
+ }
1432
+ async findByOwner(promptId, userId) {
1433
+ return (this.database
1434
+ .prepare('SELECT * FROM prompts WHERE id = ? AND user_id = ?')
1435
+ .get(promptId, userId) ?? null);
1436
+ }
1437
+ async findById(promptId) {
1438
+ return (this.database
1439
+ .prepare('SELECT * FROM prompts WHERE id = ?')
1440
+ .get(promptId) ?? null);
1441
+ }
1442
+ async findBySlug(userId, slug) {
1443
+ return (this.database
1444
+ .prepare('SELECT * FROM prompts WHERE user_id = ? AND slug = ?')
1445
+ .get(userId, slug) ?? null);
1446
+ }
1447
+ async replaceWithLimit(prompt, maximum, archivedVersion) {
1448
+ const replace = this.database.transaction(() => {
1449
+ const exists = ensureSameOwner(this.database, 'prompts', prompt.id, prompt.user_id);
1450
+ if (!exists) {
1451
+ const row = this.database
1452
+ .prepare('SELECT COUNT(*) AS count FROM prompts WHERE user_id = ?')
1453
+ .get(prompt.user_id);
1454
+ if (row.count >= maximum) {
1455
+ throw new PersistenceResourceLimitError('prompt', maximum);
1456
+ }
1457
+ }
1458
+ this.database
1459
+ .prepare(`INSERT INTO prompts
1460
+ (id, user_id, slug, title, description, content, variables,
1461
+ tags, version, created_at, updated_at)
1462
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1463
+ ON CONFLICT(id) DO UPDATE SET
1464
+ slug = excluded.slug,
1465
+ title = excluded.title,
1466
+ description = excluded.description,
1467
+ content = excluded.content,
1468
+ variables = excluded.variables,
1469
+ tags = excluded.tags,
1470
+ version = excluded.version,
1471
+ updated_at = excluded.updated_at
1472
+ WHERE prompts.user_id = excluded.user_id`)
1473
+ .run(prompt.id, prompt.user_id, prompt.slug, prompt.title, prompt.description, prompt.content, prompt.variables, prompt.tags, prompt.version, prompt.created_at, prompt.updated_at);
1474
+ if (archivedVersion) {
1475
+ this.database
1476
+ .prepare(`INSERT INTO prompt_versions
1477
+ (id, prompt_id, version, content, variables, created_at)
1478
+ VALUES (?, ?, ?, ?, ?, ?)
1479
+ ON CONFLICT(prompt_id, version) DO NOTHING`)
1480
+ .run(archivedVersion.id, archivedVersion.prompt_id, archivedVersion.version, archivedVersion.content, archivedVersion.variables, archivedVersion.created_at);
1481
+ }
1482
+ });
1483
+ replace();
1484
+ }
1485
+ async listVersions(promptId, maximum) {
1486
+ return this.database
1487
+ .prepare(`SELECT * FROM prompt_versions
1488
+ WHERE prompt_id = ?
1489
+ ORDER BY version DESC
1490
+ LIMIT ?`)
1491
+ .all(promptId, maximum);
1492
+ }
1493
+ async findVersion(promptId, version) {
1494
+ return (this.database
1495
+ .prepare('SELECT * FROM prompt_versions WHERE prompt_id = ? AND version = ?')
1496
+ .get(promptId, version) ??
1497
+ null);
1498
+ }
1499
+ async deleteByOwner(promptId, userId) {
327
1500
  return (this.database
328
- .prepare('DELETE FROM calendar_events WHERE id = ? AND user_id = ?')
329
- .run(eventId, userId).changes > 0);
1501
+ .prepare('DELETE FROM prompts WHERE id = ? AND user_id = ?')
1502
+ .run(promptId, userId).changes > 0);
330
1503
  }
331
1504
  }
332
- class SQLiteAutomationRepository {
1505
+ class SQLiteSkillRepository {
333
1506
  constructor(database) {
334
1507
  this.database = database;
335
1508
  }
336
1509
  async listByOwner(userId, maximum) {
337
1510
  return this.database
338
- .prepare(`SELECT * FROM automations
1511
+ .prepare(`SELECT * FROM skills
339
1512
  WHERE user_id = ?
340
1513
  ORDER BY updated_at DESC
341
1514
  LIMIT ?`)
342
1515
  .all(userId, maximum);
343
1516
  }
344
- async findByOwner(automationId, userId) {
1517
+ async findByOwner(skillId, userId) {
345
1518
  return (this.database
346
- .prepare('SELECT * FROM automations WHERE id = ? AND user_id = ?')
347
- .get(automationId, userId) ??
348
- null);
1519
+ .prepare('SELECT * FROM skills WHERE id = ? AND user_id = ?')
1520
+ .get(skillId, userId) ?? null);
349
1521
  }
350
- async findById(automationId) {
1522
+ async findById(skillId) {
351
1523
  return (this.database
352
- .prepare('SELECT * FROM automations WHERE id = ?')
353
- .get(automationId) ?? null);
1524
+ .prepare('SELECT * FROM skills WHERE id = ?')
1525
+ .get(skillId) ?? null);
354
1526
  }
355
- async replaceWithLimit(automation, maximum) {
1527
+ async findBySlug(userId, slug) {
1528
+ return (this.database
1529
+ .prepare('SELECT * FROM skills WHERE user_id = ? AND slug = ?')
1530
+ .get(userId, slug) ?? null);
1531
+ }
1532
+ async replaceWithLimit(skill, maximum, archivedVersion) {
356
1533
  const replace = this.database.transaction(() => {
357
- const exists = ensureSameOwner(this.database, 'automations', automation.id, automation.user_id);
1534
+ const exists = ensureSameOwner(this.database, 'skills', skill.id, skill.user_id);
358
1535
  if (!exists) {
359
1536
  const row = this.database
360
- .prepare('SELECT COUNT(*) AS count FROM automations WHERE user_id = ?')
361
- .get(automation.user_id);
1537
+ .prepare('SELECT COUNT(*) AS count FROM skills WHERE user_id = ?')
1538
+ .get(skill.user_id);
362
1539
  if (row.count >= maximum) {
363
- throw new PersistenceResourceLimitError('automation', maximum);
1540
+ throw new PersistenceResourceLimitError('skill', maximum);
364
1541
  }
365
1542
  }
366
1543
  this.database
367
- .prepare(`INSERT INTO automations
368
- (id, user_id, name, instructions, triggers, provider, model,
369
- notify, status, next_run_at, last_run_at, created_at, updated_at)
370
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1544
+ .prepare(`INSERT INTO skills
1545
+ (id, user_id, slug, name, description, instructions, enabled,
1546
+ version, created_at, updated_at)
1547
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
371
1548
  ON CONFLICT(id) DO UPDATE SET
1549
+ slug = excluded.slug,
372
1550
  name = excluded.name,
1551
+ description = excluded.description,
373
1552
  instructions = excluded.instructions,
374
- triggers = excluded.triggers,
375
- provider = excluded.provider,
376
- model = excluded.model,
377
- notify = excluded.notify,
378
- status = excluded.status,
379
- next_run_at = excluded.next_run_at,
380
- last_run_at = excluded.last_run_at,
1553
+ enabled = excluded.enabled,
1554
+ version = excluded.version,
381
1555
  updated_at = excluded.updated_at
382
- WHERE automations.user_id = excluded.user_id`)
383
- .run(automation.id, automation.user_id, automation.name, automation.instructions, automation.triggers, automation.provider, automation.model, automation.notify, automation.status, automation.next_run_at, automation.last_run_at, automation.created_at, automation.updated_at);
1556
+ WHERE skills.user_id = excluded.user_id`)
1557
+ .run(skill.id, skill.user_id, skill.slug, skill.name, skill.description, skill.instructions, skill.enabled, skill.version, skill.created_at, skill.updated_at);
1558
+ if (archivedVersion) {
1559
+ this.database
1560
+ .prepare(`INSERT INTO skill_versions
1561
+ (id, skill_id, version, instructions, created_at)
1562
+ VALUES (?, ?, ?, ?, ?)
1563
+ ON CONFLICT(skill_id, version) DO NOTHING`)
1564
+ .run(archivedVersion.id, archivedVersion.skill_id, archivedVersion.version, archivedVersion.instructions, archivedVersion.created_at);
1565
+ }
384
1566
  });
385
1567
  replace();
386
1568
  }
387
- async listDue(now, maximum) {
1569
+ async listVersions(skillId, maximum) {
388
1570
  return this.database
389
- .prepare(`SELECT * FROM automations
390
- WHERE status = 'active'
391
- AND next_run_at IS NOT NULL
392
- AND next_run_at <= ?
393
- ORDER BY next_run_at ASC
1571
+ .prepare(`SELECT * FROM skill_versions
1572
+ WHERE skill_id = ?
1573
+ ORDER BY version DESC
394
1574
  LIMIT ?`)
395
- .all(now, maximum);
396
- }
397
- async advanceNextRun(automationId, observedNextRunAt, nextRunAt, lastRunAt) {
398
- return (this.database
399
- .prepare(`UPDATE automations
400
- SET next_run_at = ?, last_run_at = ?
401
- WHERE id = ? AND next_run_at = ?`)
402
- .run(nextRunAt, lastRunAt, automationId, observedNextRunAt).changes > 0);
1575
+ .all(skillId, maximum);
403
1576
  }
404
- async setStatus(automationId, userId, status, nextRunAt, updatedAt) {
1577
+ async findVersion(skillId, version) {
405
1578
  return (this.database
406
- .prepare(`UPDATE automations
407
- SET status = ?, next_run_at = ?, updated_at = ?
408
- WHERE id = ? AND user_id = ?`)
409
- .run(status, nextRunAt, updatedAt, automationId, userId).changes > 0);
1579
+ .prepare('SELECT * FROM skill_versions WHERE skill_id = ? AND version = ?')
1580
+ .get(skillId, version) ?? null);
410
1581
  }
411
- async deleteByOwner(automationId, userId) {
1582
+ async deleteByOwner(skillId, userId) {
412
1583
  return (this.database
413
- .prepare('DELETE FROM automations WHERE id = ? AND user_id = ?')
414
- .run(automationId, userId).changes > 0);
1584
+ .prepare('DELETE FROM skills WHERE id = ? AND user_id = ?')
1585
+ .run(skillId, userId).changes > 0);
415
1586
  }
416
1587
  }
417
- class SQLiteAutomationRunRepository {
1588
+ class SQLiteSkillFileRepository {
418
1589
  constructor(database) {
419
1590
  this.database = database;
420
1591
  }
421
- async insert(run) {
422
- this.database
423
- .prepare(`INSERT INTO automation_runs
424
- (id, automation_id, user_id, scheduled_for, started_at,
425
- finished_at, status, session_id, assistant_message_id, error,
426
- seen_at, created_at)
427
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
428
- .run(run.id, run.automation_id, run.user_id, run.scheduled_for, run.started_at, run.finished_at, run.status, run.session_id, run.assistant_message_id, run.error, run.seen_at, run.created_at);
429
- }
430
- async findByOwner(runId, userId) {
431
- return (this.database
432
- .prepare('SELECT * FROM automation_runs WHERE id = ? AND user_id = ?')
433
- .get(runId, userId) ?? null);
434
- }
435
- async listByOwner(userId, options) {
436
- const clauses = ['user_id = ?'];
437
- const values = [userId];
438
- if (options.automationId !== undefined) {
439
- clauses.push('automation_id = ?');
440
- values.push(options.automationId);
441
- }
442
- if (options.from !== undefined) {
443
- clauses.push('scheduled_for >= ?');
444
- values.push(options.from);
445
- }
446
- if (options.to !== undefined) {
447
- clauses.push('scheduled_for < ?');
448
- values.push(options.to);
449
- }
450
- values.push(options.maximum);
451
- return this.database
452
- .prepare(`SELECT * FROM automation_runs
453
- WHERE ${clauses.join(' AND ')}
454
- ORDER BY scheduled_for DESC
455
- LIMIT ?`)
456
- .all(...values);
457
- }
458
- async listUnfinished(maximum) {
1592
+ async listBySkill(skillId) {
459
1593
  return this.database
460
- .prepare(`SELECT * FROM automation_runs
461
- WHERE status IN ('queued', 'running')
462
- ORDER BY scheduled_for ASC
463
- LIMIT ?`)
464
- .all(maximum);
465
- }
466
- async markStarted(runId, sessionId, assistantMessageId, startedAt) {
467
- return (this.database
468
- .prepare(`UPDATE automation_runs
469
- SET session_id = ?, assistant_message_id = ?, started_at = ?,
470
- status = 'running'
471
- WHERE id = ? AND status = 'queued'`)
472
- .run(sessionId, assistantMessageId, startedAt, runId).changes > 0);
1594
+ .prepare(`SELECT * FROM skill_files
1595
+ WHERE skill_id = ?
1596
+ ORDER BY path ASC`)
1597
+ .all(skillId);
473
1598
  }
474
- async finalize(runId, status, finishedAt, error) {
1599
+ async find(skillId, path) {
475
1600
  return (this.database
476
- .prepare(`UPDATE automation_runs
477
- SET status = ?, finished_at = ?, error = ?
478
- WHERE id = ? AND status IN ('queued', 'running')`)
479
- .run(status, finishedAt, error, runId).changes > 0);
1601
+ .prepare('SELECT * FROM skill_files WHERE skill_id = ? AND path = ?')
1602
+ .get(skillId, path) ?? null);
1603
+ }
1604
+ async upsert(file, maximumPerSkill) {
1605
+ const upsert = this.database.transaction(() => {
1606
+ const exists = this.database
1607
+ .prepare('SELECT id FROM skill_files WHERE skill_id = ? AND path = ?')
1608
+ .get(file.skill_id, file.path);
1609
+ if (!exists) {
1610
+ const row = this.database
1611
+ .prepare('SELECT COUNT(*) AS count FROM skill_files WHERE skill_id = ?')
1612
+ .get(file.skill_id);
1613
+ if (row.count >= maximumPerSkill) {
1614
+ throw new PersistenceResourceLimitError('skill-file', maximumPerSkill);
1615
+ }
1616
+ }
1617
+ this.database
1618
+ .prepare(`INSERT INTO skill_files
1619
+ (id, skill_id, path, content, size, created_at, updated_at)
1620
+ VALUES (?, ?, ?, ?, ?, ?, ?)
1621
+ ON CONFLICT(skill_id, path) DO UPDATE SET
1622
+ content = excluded.content,
1623
+ size = excluded.size,
1624
+ updated_at = excluded.updated_at`)
1625
+ .run(file.id, file.skill_id, file.path, file.content, file.size, file.created_at, file.updated_at);
1626
+ });
1627
+ upsert();
480
1628
  }
481
- async countUnseenFinished(userId) {
482
- const row = this.database
483
- .prepare(`SELECT COUNT(*) AS count FROM automation_runs
484
- WHERE user_id = ?
485
- AND status IN ('succeeded', 'failed')
486
- AND seen_at IS NULL`)
487
- .get(userId);
488
- return row.count;
1629
+ async replaceAllForSkill(skillId, files) {
1630
+ const replace = this.database.transaction(() => {
1631
+ this.database
1632
+ .prepare('DELETE FROM skill_files WHERE skill_id = ?')
1633
+ .run(skillId);
1634
+ const insert = this.database.prepare(`INSERT INTO skill_files
1635
+ (id, skill_id, path, content, size, created_at, updated_at)
1636
+ VALUES (?, ?, ?, ?, ?, ?, ?)`);
1637
+ for (const file of files) {
1638
+ if (file.skill_id !== skillId) {
1639
+ throw new Error('A skill file does not belong to its skill');
1640
+ }
1641
+ insert.run(file.id, file.skill_id, file.path, file.content, file.size, file.created_at, file.updated_at);
1642
+ }
1643
+ });
1644
+ replace();
489
1645
  }
490
- async markSeenBefore(userId, seenAt) {
491
- return this.database
492
- .prepare(`UPDATE automation_runs
493
- SET seen_at = ?
494
- WHERE user_id = ?
495
- AND status IN ('succeeded', 'failed')
496
- AND seen_at IS NULL
497
- AND finished_at IS NOT NULL
498
- AND finished_at <= ?`)
499
- .run(seenAt, userId, seenAt).changes;
1646
+ async delete(skillId, path) {
1647
+ return (this.database
1648
+ .prepare('DELETE FROM skill_files WHERE skill_id = ? AND path = ?')
1649
+ .run(skillId, path).changes > 0);
500
1650
  }
501
1651
  }
502
1652
  class SQLiteSessionFolderRepository {
@@ -670,6 +1820,10 @@ const ARCHIVE_TABLES = {
670
1820
  'knowledge-collection': 'knowledge_collections',
671
1821
  document: 'documents',
672
1822
  persona: 'personas',
1823
+ prompt: 'prompts',
1824
+ skill: 'skills',
1825
+ 'tool-server': 'tool_servers',
1826
+ calendar: 'calendars',
673
1827
  };
674
1828
  class SQLiteDataArchiveRepository {
675
1829
  constructor(database) {
@@ -881,16 +2035,251 @@ class SQLiteDataArchiveRepository {
881
2035
  apply.immediate();
882
2036
  }
883
2037
  }
2038
+ class SQLiteModelTariffRepository {
2039
+ constructor(database) {
2040
+ this.database = database;
2041
+ }
2042
+ async listAll(maximum) {
2043
+ return this.database
2044
+ .prepare(`SELECT * FROM model_tariffs
2045
+ ORDER BY plugin_id ASC, model ASC, effective_from DESC
2046
+ LIMIT ?`)
2047
+ .all(maximum);
2048
+ }
2049
+ async insert(tariff) {
2050
+ this.database
2051
+ .prepare(`INSERT INTO model_tariffs
2052
+ (id, plugin_id, model, input_per_million, output_per_million,
2053
+ unit_price, currency, effective_from, created_by, created_at)
2054
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
2055
+ .run(tariff.id, tariff.plugin_id, tariff.model, tariff.input_per_million, tariff.output_per_million, tariff.unit_price, tariff.currency, tariff.effective_from, tariff.created_by, tariff.created_at);
2056
+ }
2057
+ async deleteById(tariffId) {
2058
+ return (this.database
2059
+ .prepare('DELETE FROM model_tariffs WHERE id = ?')
2060
+ .run(tariffId).changes > 0);
2061
+ }
2062
+ }
2063
+ class SQLiteUsageBudgetRepository {
2064
+ constructor(database) {
2065
+ this.database = database;
2066
+ }
2067
+ async listAll(maximum) {
2068
+ return this.database
2069
+ .prepare(`SELECT * FROM usage_budgets
2070
+ ORDER BY created_at ASC, id ASC
2071
+ LIMIT ?`)
2072
+ .all(maximum);
2073
+ }
2074
+ async findById(budgetId) {
2075
+ return (this.database
2076
+ .prepare('SELECT * FROM usage_budgets WHERE id = ?')
2077
+ .get(budgetId) ?? null);
2078
+ }
2079
+ async replace(budget) {
2080
+ this.database
2081
+ .prepare(`INSERT INTO usage_budgets
2082
+ (id, name, principal_type, principal_id, period, amount_usd,
2083
+ mode, created_by, created_at, updated_at)
2084
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2085
+ ON CONFLICT(id) DO UPDATE SET
2086
+ name = excluded.name,
2087
+ principal_type = excluded.principal_type,
2088
+ principal_id = excluded.principal_id,
2089
+ period = excluded.period,
2090
+ amount_usd = excluded.amount_usd,
2091
+ mode = excluded.mode,
2092
+ updated_at = excluded.updated_at`)
2093
+ .run(budget.id, budget.name, budget.principal_type, budget.principal_id, budget.period, budget.amount_usd, budget.mode, budget.created_by, budget.created_at, budget.updated_at);
2094
+ }
2095
+ async deleteById(budgetId) {
2096
+ return (this.database
2097
+ .prepare('DELETE FROM usage_budgets WHERE id = ?')
2098
+ .run(budgetId).changes > 0);
2099
+ }
2100
+ }
2101
+ class SQLiteMessageFeedbackRepository {
2102
+ constructor(database) {
2103
+ this.database = database;
2104
+ }
2105
+ async upsertByMessage(feedback) {
2106
+ this.database
2107
+ .prepare(`INSERT INTO message_feedback
2108
+ (id, user_id, session_id, message_id, rating, tags, comment,
2109
+ model, plugin_id, snapshot, created_at, updated_at)
2110
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2111
+ ON CONFLICT(user_id, message_id) DO UPDATE SET
2112
+ rating = excluded.rating,
2113
+ tags = excluded.tags,
2114
+ comment = excluded.comment,
2115
+ model = excluded.model,
2116
+ plugin_id = excluded.plugin_id,
2117
+ snapshot = excluded.snapshot,
2118
+ updated_at = excluded.updated_at`)
2119
+ .run(feedback.id, feedback.user_id, feedback.session_id, feedback.message_id, feedback.rating, feedback.tags, feedback.comment, feedback.model, feedback.plugin_id, feedback.snapshot, feedback.created_at, feedback.updated_at);
2120
+ }
2121
+ async deleteByMessage(userId, messageId) {
2122
+ return (this.database
2123
+ .prepare('DELETE FROM message_feedback WHERE user_id = ? AND message_id = ?')
2124
+ .run(userId, messageId).changes > 0);
2125
+ }
2126
+ async listByOwner(userId, maximum) {
2127
+ return this.database
2128
+ .prepare(`SELECT * FROM message_feedback
2129
+ WHERE user_id = ?
2130
+ ORDER BY created_at DESC, id DESC
2131
+ LIMIT ?`)
2132
+ .all(userId, maximum);
2133
+ }
2134
+ async listAll(maximum) {
2135
+ return this.database
2136
+ .prepare(`SELECT * FROM message_feedback
2137
+ ORDER BY created_at DESC, id DESC
2138
+ LIMIT ?`)
2139
+ .all(maximum);
2140
+ }
2141
+ }
2142
+ class SQLiteArenaVoteRepository {
2143
+ constructor(database) {
2144
+ this.database = database;
2145
+ }
2146
+ async insertOnce(vote) {
2147
+ return (this.database
2148
+ .prepare(`INSERT INTO arena_votes
2149
+ (id, user_id, compare_group, model_a, model_b, winner, created_at)
2150
+ VALUES (?, ?, ?, ?, ?, ?, ?)
2151
+ ON CONFLICT(user_id, compare_group) DO NOTHING`)
2152
+ .run(vote.id, vote.user_id, vote.compare_group, vote.model_a, vote.model_b, vote.winner, vote.created_at).changes > 0);
2153
+ }
2154
+ async listAllOrdered(maximum) {
2155
+ return this.database
2156
+ .prepare(`SELECT * FROM arena_votes
2157
+ ORDER BY created_at ASC, id ASC
2158
+ LIMIT ?`)
2159
+ .all(maximum);
2160
+ }
2161
+ }
2162
+ class SQLiteEvalSetRepository {
2163
+ constructor(database) {
2164
+ this.database = database;
2165
+ }
2166
+ async listByOwner(userId, maximum) {
2167
+ return this.database
2168
+ .prepare(`SELECT * FROM eval_sets
2169
+ WHERE user_id = ?
2170
+ ORDER BY updated_at DESC, id DESC
2171
+ LIMIT ?`)
2172
+ .all(userId, maximum);
2173
+ }
2174
+ async findByOwner(setId, userId) {
2175
+ return (this.database
2176
+ .prepare('SELECT * FROM eval_sets WHERE id = ? AND user_id = ?')
2177
+ .get(setId, userId) ?? null);
2178
+ }
2179
+ async replaceWithLimit(set, maximum) {
2180
+ const replace = this.database.transaction(() => {
2181
+ const exists = ensureSameOwner(this.database, 'eval_sets', set.id, set.user_id);
2182
+ if (!exists) {
2183
+ const row = this.database
2184
+ .prepare('SELECT COUNT(*) AS count FROM eval_sets WHERE user_id = ?')
2185
+ .get(set.user_id);
2186
+ if (row.count >= maximum) {
2187
+ throw new PersistenceResourceLimitError('eval-set', maximum);
2188
+ }
2189
+ }
2190
+ this.database
2191
+ .prepare(`INSERT INTO eval_sets
2192
+ (id, user_id, name, description, items, created_at, updated_at)
2193
+ VALUES (?, ?, ?, ?, ?, ?, ?)
2194
+ ON CONFLICT(id) DO UPDATE SET
2195
+ name = excluded.name,
2196
+ description = excluded.description,
2197
+ items = excluded.items,
2198
+ updated_at = excluded.updated_at
2199
+ WHERE eval_sets.user_id = excluded.user_id`)
2200
+ .run(set.id, set.user_id, set.name, set.description, set.items, set.created_at, set.updated_at);
2201
+ });
2202
+ replace();
2203
+ }
2204
+ async deleteByOwner(setId, userId) {
2205
+ return (this.database
2206
+ .prepare('DELETE FROM eval_sets WHERE id = ? AND user_id = ?')
2207
+ .run(setId, userId).changes > 0);
2208
+ }
2209
+ }
2210
+ class SQLiteEvalRunRepository {
2211
+ constructor(database) {
2212
+ this.database = database;
2213
+ }
2214
+ async listByOwner(userId, maximum) {
2215
+ return this.database
2216
+ .prepare(`SELECT * FROM eval_runs
2217
+ WHERE user_id = ?
2218
+ ORDER BY created_at DESC, id DESC
2219
+ LIMIT ?`)
2220
+ .all(userId, maximum);
2221
+ }
2222
+ async listBySet(setId, userId, maximum) {
2223
+ return this.database
2224
+ .prepare(`SELECT * FROM eval_runs
2225
+ WHERE set_id = ? AND user_id = ?
2226
+ ORDER BY created_at DESC, id DESC
2227
+ LIMIT ?`)
2228
+ .all(setId, userId, maximum);
2229
+ }
2230
+ async findByOwner(runId, userId) {
2231
+ return (this.database
2232
+ .prepare('SELECT * FROM eval_runs WHERE id = ? AND user_id = ?')
2233
+ .get(runId, userId) ?? null);
2234
+ }
2235
+ async insert(run) {
2236
+ this.database
2237
+ .prepare(`INSERT INTO eval_runs
2238
+ (id, set_id, user_id, label, plugin_id, model, status, results,
2239
+ error, created_at, updated_at, completed_at)
2240
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
2241
+ .run(run.id, run.set_id, run.user_id, run.label, run.plugin_id, run.model, run.status, run.results, run.error, run.created_at, run.updated_at, run.completed_at);
2242
+ }
2243
+ async update(runId, userId, changes) {
2244
+ return (this.database
2245
+ .prepare(`UPDATE eval_runs SET
2246
+ status = ?,
2247
+ results = COALESCE(?, results),
2248
+ error = ?,
2249
+ updated_at = ?,
2250
+ completed_at = COALESCE(?, completed_at)
2251
+ WHERE id = ? AND user_id = ?`)
2252
+ .run(changes.status, changes.results ?? null, changes.error ?? null, changes.updated_at, changes.completed_at ?? null, runId, userId).changes > 0);
2253
+ }
2254
+ }
884
2255
  export const createSQLiteResourceRepositories = (database) => ({
885
2256
  chatSessions: new SQLiteChatSessionRepository(database),
886
2257
  knowledgeCollections: new SQLiteKnowledgeCollectionRepository(database),
887
2258
  notes: new SQLiteNoteRepository(database),
2259
+ calendars: new SQLiteCalendarRepository(database),
888
2260
  calendarEvents: new SQLiteCalendarEventRepository(database),
2261
+ channels: new SQLiteChannelRepository(database),
2262
+ channelMessages: new SQLiteChannelMessageRepository(database),
2263
+ notifications: new SQLiteNotificationRepository(database),
2264
+ webhookTargets: new SQLiteWebhookTargetRepository(database),
889
2265
  automations: new SQLiteAutomationRepository(database),
890
2266
  automationRuns: new SQLiteAutomationRunRepository(database),
2267
+ toolServers: new SQLiteToolServerRepository(database),
2268
+ toolServerTools: new SQLiteToolServerToolRepository(database),
2269
+ toolServerCredentials: new SQLiteToolServerCredentialRepository(database),
2270
+ toolApprovals: new SQLiteToolApprovalRepository(database),
2271
+ prompts: new SQLitePromptRepository(database),
2272
+ skills: new SQLiteSkillRepository(database),
2273
+ skillFiles: new SQLiteSkillFileRepository(database),
891
2274
  sessionFolders: new SQLiteSessionFolderRepository(database),
892
2275
  preferences: new SQLitePreferenceRepository(database),
893
2276
  systemSettings: new SQLiteSystemSettingRepository(database),
894
2277
  archive: new SQLiteDataArchiveRepository(database),
2278
+ modelTariffs: new SQLiteModelTariffRepository(database),
2279
+ usageBudgets: new SQLiteUsageBudgetRepository(database),
2280
+ messageFeedback: new SQLiteMessageFeedbackRepository(database),
2281
+ arenaVotes: new SQLiteArenaVoteRepository(database),
2282
+ evalSets: new SQLiteEvalSetRepository(database),
2283
+ evalRuns: new SQLiteEvalRunRepository(database),
895
2284
  });
896
2285
  //# sourceMappingURL=sqliteResourceRepositories.js.map