libre-webui 0.24.1 → 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.
- package/README.md +36 -15
- package/backend/dist/index.d.ts.map +1 -1
- package/backend/dist/index.js +35 -4
- package/backend/dist/index.js.map +1 -1
- package/backend/dist/middleware/auth.d.ts +7 -0
- package/backend/dist/middleware/auth.d.ts.map +1 -1
- package/backend/dist/middleware/auth.js +26 -0
- package/backend/dist/middleware/auth.js.map +1 -1
- package/backend/dist/middleware/index.d.ts +20 -0
- package/backend/dist/middleware/index.d.ts.map +1 -1
- package/backend/dist/middleware/index.js +105 -2
- package/backend/dist/middleware/index.js.map +1 -1
- package/backend/dist/models/personaModel.d.ts.map +1 -1
- package/backend/dist/models/personaModel.js +2 -0
- package/backend/dist/models/personaModel.js.map +1 -1
- package/backend/dist/observability/otel.d.ts +40 -0
- package/backend/dist/observability/otel.d.ts.map +1 -0
- package/backend/dist/observability/otel.js +337 -0
- package/backend/dist/observability/otel.js.map +1 -0
- package/backend/dist/observability/requestContext.d.ts +14 -0
- package/backend/dist/observability/requestContext.d.ts.map +1 -0
- package/backend/dist/observability/requestContext.js +21 -0
- package/backend/dist/observability/requestContext.js.map +1 -0
- package/backend/dist/persistence/chatGenerationTypes.d.ts +9 -0
- package/backend/dist/persistence/chatGenerationTypes.d.ts.map +1 -1
- package/backend/dist/persistence/extensionTypes.d.ts +13 -0
- package/backend/dist/persistence/extensionTypes.d.ts.map +1 -1
- package/backend/dist/persistence/extensionTypes.js.map +1 -1
- package/backend/dist/persistence/index.d.ts +1 -1
- package/backend/dist/persistence/index.d.ts.map +1 -1
- package/backend/dist/persistence/index.js.map +1 -1
- package/backend/dist/persistence/postgresAgentFoundationMigration.d.ts +9 -0
- package/backend/dist/persistence/postgresAgentFoundationMigration.d.ts.map +1 -0
- package/backend/dist/persistence/postgresAgentFoundationMigration.js +164 -0
- package/backend/dist/persistence/postgresAgentFoundationMigration.js.map +1 -0
- package/backend/dist/persistence/postgresExtensionRepositories.d.ts.map +1 -1
- package/backend/dist/persistence/postgresExtensionRepositories.js +46 -2
- package/backend/dist/persistence/postgresExtensionRepositories.js.map +1 -1
- package/backend/dist/persistence/postgresMediaEnterpriseOpsMigration.d.ts +10 -0
- package/backend/dist/persistence/postgresMediaEnterpriseOpsMigration.d.ts.map +1 -0
- package/backend/dist/persistence/postgresMediaEnterpriseOpsMigration.js +148 -0
- package/backend/dist/persistence/postgresMediaEnterpriseOpsMigration.js.map +1 -0
- package/backend/dist/persistence/postgresMigrationRegistry.d.ts.map +1 -1
- package/backend/dist/persistence/postgresMigrationRegistry.js +12 -0
- package/backend/dist/persistence/postgresMigrationRegistry.js.map +1 -1
- package/backend/dist/persistence/postgresNotesV2Migration.d.ts +4 -0
- package/backend/dist/persistence/postgresNotesV2Migration.d.ts.map +1 -0
- package/backend/dist/persistence/postgresNotesV2Migration.js +57 -0
- package/backend/dist/persistence/postgresNotesV2Migration.js.map +1 -0
- package/backend/dist/persistence/postgresPersonalAutomationsMigration.d.ts +8 -0
- package/backend/dist/persistence/postgresPersonalAutomationsMigration.d.ts.map +1 -0
- package/backend/dist/persistence/postgresPersonalAutomationsMigration.js +98 -0
- package/backend/dist/persistence/postgresPersonalAutomationsMigration.js.map +1 -0
- package/backend/dist/persistence/postgresResourceRepositories.d.ts.map +1 -1
- package/backend/dist/persistence/postgresResourceRepositories.js +1972 -63
- package/backend/dist/persistence/postgresResourceRepositories.js.map +1 -1
- package/backend/dist/persistence/postgresSkillFilesMigration.d.ts +9 -0
- package/backend/dist/persistence/postgresSkillFilesMigration.d.ts.map +1 -0
- package/backend/dist/persistence/postgresSkillFilesMigration.js +50 -0
- package/backend/dist/persistence/postgresSkillFilesMigration.js.map +1 -0
- package/backend/dist/persistence/postgresTeamCollaborationMigration.d.ts +10 -0
- package/backend/dist/persistence/postgresTeamCollaborationMigration.d.ts.map +1 -0
- package/backend/dist/persistence/postgresTeamCollaborationMigration.js +161 -0
- package/backend/dist/persistence/postgresTeamCollaborationMigration.js.map +1 -0
- package/backend/dist/persistence/resourceTypes.d.ts +622 -3
- package/backend/dist/persistence/resourceTypes.d.ts.map +1 -1
- package/backend/dist/persistence/resourceTypes.js.map +1 -1
- package/backend/dist/persistence/sqliteExtensionRepositories.d.ts.map +1 -1
- package/backend/dist/persistence/sqliteExtensionRepositories.js +26 -4
- package/backend/dist/persistence/sqliteExtensionRepositories.js.map +1 -1
- package/backend/dist/persistence/sqliteMigrations.d.ts +10 -0
- package/backend/dist/persistence/sqliteMigrations.d.ts.map +1 -1
- package/backend/dist/persistence/sqliteMigrations.js +1396 -48
- package/backend/dist/persistence/sqliteMigrations.js.map +1 -1
- package/backend/dist/persistence/sqliteResourceRepositories.d.ts.map +1 -1
- package/backend/dist/persistence/sqliteResourceRepositories.js +1680 -58
- package/backend/dist/persistence/sqliteResourceRepositories.js.map +1 -1
- package/backend/dist/persistence/sqliteToPostgresMigration.d.ts.map +1 -1
- package/backend/dist/persistence/sqliteToPostgresMigration.js +297 -2
- package/backend/dist/persistence/sqliteToPostgresMigration.js.map +1 -1
- package/backend/dist/platform/jobs/domainJobContracts.d.ts +11 -0
- package/backend/dist/platform/jobs/domainJobContracts.d.ts.map +1 -1
- package/backend/dist/platform/jobs/domainJobContracts.js +11 -0
- package/backend/dist/platform/jobs/domainJobContracts.js.map +1 -1
- package/backend/dist/platform/jobs/domainJobHandlers.d.ts.map +1 -1
- package/backend/dist/platform/jobs/domainJobHandlers.js +409 -2
- package/backend/dist/platform/jobs/domainJobHandlers.js.map +1 -1
- package/backend/dist/platform/jobs/embeddedDurableJobWorker.d.ts.map +1 -1
- package/backend/dist/platform/jobs/embeddedDurableJobWorker.js +4 -2
- package/backend/dist/platform/jobs/embeddedDurableJobWorker.js.map +1 -1
- package/backend/dist/platform/jobs/postgresDurableJobWorker.d.ts.map +1 -1
- package/backend/dist/platform/jobs/postgresDurableJobWorker.js +4 -2
- package/backend/dist/platform/jobs/postgresDurableJobWorker.js.map +1 -1
- package/backend/dist/platform/storage/pgVectorStore.d.ts +2 -1
- package/backend/dist/platform/storage/pgVectorStore.d.ts.map +1 -1
- package/backend/dist/platform/storage/pgVectorStore.js +51 -0
- package/backend/dist/platform/storage/pgVectorStore.js.map +1 -1
- package/backend/dist/platform/storage/platformDomainRepositories.d.ts +8 -2
- package/backend/dist/platform/storage/platformDomainRepositories.d.ts.map +1 -1
- package/backend/dist/platform/storage/postgresPlatformDomainRepositories.d.ts.map +1 -1
- package/backend/dist/platform/storage/postgresPlatformDomainRepositories.js +25 -4
- package/backend/dist/platform/storage/postgresPlatformDomainRepositories.js.map +1 -1
- package/backend/dist/platform/storage/sqliteEncryptedVectorStore.d.ts +2 -1
- package/backend/dist/platform/storage/sqliteEncryptedVectorStore.d.ts.map +1 -1
- package/backend/dist/platform/storage/sqliteEncryptedVectorStore.js +35 -0
- package/backend/dist/platform/storage/sqliteEncryptedVectorStore.js.map +1 -1
- package/backend/dist/platform/storage/sqlitePlatformDomainRepositories.d.ts.map +1 -1
- package/backend/dist/platform/storage/sqlitePlatformDomainRepositories.js +26 -3
- package/backend/dist/platform/storage/sqlitePlatformDomainRepositories.js.map +1 -1
- package/backend/dist/platform/storage/vectorStore.d.ts +14 -0
- package/backend/dist/platform/storage/vectorStore.d.ts.map +1 -1
- package/backend/dist/platform/storage/vectorStore.js.map +1 -1
- package/backend/dist/routes/access.d.ts.map +1 -1
- package/backend/dist/routes/access.js +84 -3
- package/backend/dist/routes/access.js.map +1 -1
- package/backend/dist/routes/automations.d.ts +3 -0
- package/backend/dist/routes/automations.d.ts.map +1 -0
- package/backend/dist/routes/automations.js +232 -0
- package/backend/dist/routes/automations.js.map +1 -0
- package/backend/dist/routes/calendar.d.ts +3 -0
- package/backend/dist/routes/calendar.d.ts.map +1 -0
- package/backend/dist/routes/calendar.js +309 -0
- package/backend/dist/routes/calendar.js.map +1 -0
- package/backend/dist/routes/channels.d.ts +3 -0
- package/backend/dist/routes/channels.d.ts.map +1 -0
- package/backend/dist/routes/channels.js +447 -0
- package/backend/dist/routes/channels.js.map +1 -0
- package/backend/dist/routes/chat.d.ts.map +1 -1
- package/backend/dist/routes/chat.js +237 -10
- package/backend/dist/routes/chat.js.map +1 -1
- package/backend/dist/routes/costs.d.ts +3 -0
- package/backend/dist/routes/costs.d.ts.map +1 -0
- package/backend/dist/routes/costs.js +191 -0
- package/backend/dist/routes/costs.js.map +1 -0
- package/backend/dist/routes/documents.d.ts.map +1 -1
- package/backend/dist/routes/documents.js +64 -21
- package/backend/dist/routes/documents.js.map +1 -1
- package/backend/dist/routes/evaluations.d.ts +3 -0
- package/backend/dist/routes/evaluations.d.ts.map +1 -0
- package/backend/dist/routes/evaluations.js +352 -0
- package/backend/dist/routes/evaluations.js.map +1 -0
- package/backend/dist/routes/imageGen.d.ts.map +1 -1
- package/backend/dist/routes/imageGen.js +2 -1
- package/backend/dist/routes/imageGen.js.map +1 -1
- package/backend/dist/routes/media.d.ts.map +1 -1
- package/backend/dist/routes/media.js +228 -7
- package/backend/dist/routes/media.js.map +1 -1
- package/backend/dist/routes/notes.d.ts.map +1 -1
- package/backend/dist/routes/notes.js +149 -36
- package/backend/dist/routes/notes.js.map +1 -1
- package/backend/dist/routes/notifications.d.ts +3 -0
- package/backend/dist/routes/notifications.d.ts.map +1 -0
- package/backend/dist/routes/notifications.js +213 -0
- package/backend/dist/routes/notifications.js.map +1 -0
- package/backend/dist/routes/ollama.d.ts.map +1 -1
- package/backend/dist/routes/ollama.js +4 -1
- package/backend/dist/routes/ollama.js.map +1 -1
- package/backend/dist/routes/openaiCompat.d.ts +3 -0
- package/backend/dist/routes/openaiCompat.d.ts.map +1 -0
- package/backend/dist/routes/openaiCompat.js +314 -0
- package/backend/dist/routes/openaiCompat.js.map +1 -0
- package/backend/dist/routes/personas.js +1 -1
- package/backend/dist/routes/personas.js.map +1 -1
- package/backend/dist/routes/prompts.d.ts +3 -0
- package/backend/dist/routes/prompts.d.ts.map +1 -0
- package/backend/dist/routes/prompts.js +173 -0
- package/backend/dist/routes/prompts.js.map +1 -0
- package/backend/dist/routes/search.d.ts.map +1 -1
- package/backend/dist/routes/search.js +34 -4
- package/backend/dist/routes/search.js.map +1 -1
- package/backend/dist/routes/skills.d.ts +3 -0
- package/backend/dist/routes/skills.d.ts.map +1 -0
- package/backend/dist/routes/skills.js +246 -0
- package/backend/dist/routes/skills.js.map +1 -0
- package/backend/dist/routes/stt.d.ts.map +1 -1
- package/backend/dist/routes/stt.js +2 -1
- package/backend/dist/routes/stt.js.map +1 -1
- package/backend/dist/routes/tools.d.ts +3 -0
- package/backend/dist/routes/tools.d.ts.map +1 -0
- package/backend/dist/routes/tools.js +359 -0
- package/backend/dist/routes/tools.js.map +1 -0
- package/backend/dist/routes/tts.d.ts.map +1 -1
- package/backend/dist/routes/tts.js +63 -8
- package/backend/dist/routes/tts.js.map +1 -1
- package/backend/dist/routes/voiceAccess.d.ts +3 -0
- package/backend/dist/routes/voiceAccess.d.ts.map +1 -0
- package/backend/dist/routes/voiceAccess.js +102 -0
- package/backend/dist/routes/voiceAccess.js.map +1 -0
- package/backend/dist/services/apiTokenService.d.ts +2 -2
- package/backend/dist/services/apiTokenService.d.ts.map +1 -1
- package/backend/dist/services/apiTokenService.js +7 -0
- package/backend/dist/services/apiTokenService.js.map +1 -1
- package/backend/dist/services/authorizationService.d.ts +8 -4
- package/backend/dist/services/authorizationService.d.ts.map +1 -1
- package/backend/dist/services/authorizationService.js +31 -1
- package/backend/dist/services/authorizationService.js.map +1 -1
- package/backend/dist/services/automationSchedulerService.d.ts +28 -0
- package/backend/dist/services/automationSchedulerService.d.ts.map +1 -0
- package/backend/dist/services/automationSchedulerService.js +221 -0
- package/backend/dist/services/automationSchedulerService.js.map +1 -0
- package/backend/dist/services/automationService.d.ts +56 -0
- package/backend/dist/services/automationService.d.ts.map +1 -0
- package/backend/dist/services/automationService.js +247 -0
- package/backend/dist/services/automationService.js.map +1 -0
- package/backend/dist/services/builtinToolsService.d.ts +20 -0
- package/backend/dist/services/builtinToolsService.d.ts.map +1 -0
- package/backend/dist/services/builtinToolsService.js +661 -0
- package/backend/dist/services/builtinToolsService.js.map +1 -0
- package/backend/dist/services/calendarService.d.ts +74 -0
- package/backend/dist/services/calendarService.d.ts.map +1 -0
- package/backend/dist/services/calendarService.js +339 -0
- package/backend/dist/services/calendarService.js.map +1 -0
- package/backend/dist/services/channelService.d.ts +195 -0
- package/backend/dist/services/channelService.d.ts.map +1 -0
- package/backend/dist/services/channelService.js +1057 -0
- package/backend/dist/services/channelService.js.map +1 -0
- package/backend/dist/services/chatGenerationService.d.ts.map +1 -1
- package/backend/dist/services/chatGenerationService.js +7 -1
- package/backend/dist/services/chatGenerationService.js.map +1 -1
- package/backend/dist/services/chatRequestService.d.ts.map +1 -1
- package/backend/dist/services/chatRequestService.js +29 -1
- package/backend/dist/services/chatRequestService.js.map +1 -1
- package/backend/dist/services/chatService.d.ts +62 -1
- package/backend/dist/services/chatService.d.ts.map +1 -1
- package/backend/dist/services/chatService.js +242 -1
- package/backend/dist/services/chatService.js.map +1 -1
- package/backend/dist/services/chatToolRuntimeService.d.ts +55 -0
- package/backend/dist/services/chatToolRuntimeService.d.ts.map +1 -0
- package/backend/dist/services/chatToolRuntimeService.js +389 -0
- package/backend/dist/services/chatToolRuntimeService.js.map +1 -0
- package/backend/dist/services/costGovernanceService.d.ts +102 -0
- package/backend/dist/services/costGovernanceService.d.ts.map +1 -0
- package/backend/dist/services/costGovernanceService.js +463 -0
- package/backend/dist/services/costGovernanceService.js.map +1 -0
- package/backend/dist/services/dataArchiveService.d.ts +6 -0
- package/backend/dist/services/dataArchiveService.d.ts.map +1 -1
- package/backend/dist/services/dataArchiveService.js +23 -4
- package/backend/dist/services/dataArchiveService.js.map +1 -1
- package/backend/dist/services/documentService.d.ts +58 -1
- package/backend/dist/services/documentService.d.ts.map +1 -1
- package/backend/dist/services/documentService.js +289 -84
- package/backend/dist/services/documentService.js.map +1 -1
- package/backend/dist/services/durableChatGenerationService.d.ts +9 -0
- package/backend/dist/services/durableChatGenerationService.d.ts.map +1 -1
- package/backend/dist/services/durableChatGenerationService.js +160 -13
- package/backend/dist/services/durableChatGenerationService.js.map +1 -1
- package/backend/dist/services/evaluationService.d.ts +111 -0
- package/backend/dist/services/evaluationService.d.ts.map +1 -0
- package/backend/dist/services/evaluationService.js +403 -0
- package/backend/dist/services/evaluationService.js.map +1 -0
- package/backend/dist/services/galleryService.d.ts +6 -0
- package/backend/dist/services/galleryService.d.ts.map +1 -1
- package/backend/dist/services/galleryService.js +26 -0
- package/backend/dist/services/galleryService.js.map +1 -1
- package/backend/dist/services/mcpClientService.d.ts +29 -0
- package/backend/dist/services/mcpClientService.d.ts.map +1 -0
- package/backend/dist/services/mcpClientService.js +189 -0
- package/backend/dist/services/mcpClientService.js.map +1 -0
- package/backend/dist/services/noteService.d.ts +71 -0
- package/backend/dist/services/noteService.d.ts.map +1 -0
- package/backend/dist/services/noteService.js +397 -0
- package/backend/dist/services/noteService.js.map +1 -0
- package/backend/dist/services/notificationService.d.ts +55 -0
- package/backend/dist/services/notificationService.d.ts.map +1 -0
- package/backend/dist/services/notificationService.js +308 -0
- package/backend/dist/services/notificationService.js.map +1 -0
- package/backend/dist/services/ollamaService.d.ts.map +1 -1
- package/backend/dist/services/ollamaService.js +3 -1
- package/backend/dist/services/ollamaService.js.map +1 -1
- package/backend/dist/services/openApiToolService.d.ts +38 -0
- package/backend/dist/services/openApiToolService.d.ts.map +1 -0
- package/backend/dist/services/openApiToolService.js +254 -0
- package/backend/dist/services/openApiToolService.js.map +1 -0
- package/backend/dist/services/personaService.d.ts +18 -2
- package/backend/dist/services/personaService.d.ts.map +1 -1
- package/backend/dist/services/personaService.js +113 -4
- package/backend/dist/services/personaService.js.map +1 -1
- package/backend/dist/services/pluginImageGenerationService.d.ts +19 -0
- package/backend/dist/services/pluginImageGenerationService.d.ts.map +1 -1
- package/backend/dist/services/pluginImageGenerationService.js +121 -0
- package/backend/dist/services/pluginImageGenerationService.js.map +1 -1
- package/backend/dist/services/pluginService.d.ts +7 -0
- package/backend/dist/services/pluginService.d.ts.map +1 -1
- package/backend/dist/services/pluginService.js +8 -2
- package/backend/dist/services/pluginService.js.map +1 -1
- package/backend/dist/services/promptService.d.ts +74 -0
- package/backend/dist/services/promptService.d.ts.map +1 -0
- package/backend/dist/services/promptService.js +513 -0
- package/backend/dist/services/promptService.js.map +1 -0
- package/backend/dist/services/resourceGrantService.d.ts.map +1 -1
- package/backend/dist/services/resourceGrantService.js +62 -17
- package/backend/dist/services/resourceGrantService.js.map +1 -1
- package/backend/dist/services/searchService.d.ts +29 -0
- package/backend/dist/services/searchService.d.ts.map +1 -0
- package/backend/dist/services/searchService.js +0 -0
- package/backend/dist/services/searchService.js.map +1 -0
- package/backend/dist/services/securityAuditService.d.ts.map +1 -1
- package/backend/dist/services/securityAuditService.js +2 -1
- package/backend/dist/services/securityAuditService.js.map +1 -1
- package/backend/dist/services/sharedResourceAccess.d.ts +25 -0
- package/backend/dist/services/sharedResourceAccess.d.ts.map +1 -0
- package/backend/dist/services/sharedResourceAccess.js +59 -0
- package/backend/dist/services/sharedResourceAccess.js.map +1 -0
- package/backend/dist/services/skillService.d.ts +137 -0
- package/backend/dist/services/skillService.d.ts.map +1 -0
- package/backend/dist/services/skillService.js +852 -0
- package/backend/dist/services/skillService.js.map +1 -0
- package/backend/dist/services/titleGenerationService.d.ts.map +1 -1
- package/backend/dist/services/titleGenerationService.js +5 -1
- package/backend/dist/services/titleGenerationService.js.map +1 -1
- package/backend/dist/services/toolAccessService.d.ts +9 -0
- package/backend/dist/services/toolAccessService.d.ts.map +1 -0
- package/backend/dist/services/toolAccessService.js +60 -0
- package/backend/dist/services/toolAccessService.js.map +1 -0
- package/backend/dist/services/toolApprovalService.d.ts +34 -0
- package/backend/dist/services/toolApprovalService.d.ts.map +1 -0
- package/backend/dist/services/toolApprovalService.js +188 -0
- package/backend/dist/services/toolApprovalService.js.map +1 -0
- package/backend/dist/services/toolGatewayService.d.ts +53 -0
- package/backend/dist/services/toolGatewayService.d.ts.map +1 -0
- package/backend/dist/services/toolGatewayService.js +249 -0
- package/backend/dist/services/toolGatewayService.js.map +1 -0
- package/backend/dist/services/toolServerService.d.ts +57 -0
- package/backend/dist/services/toolServerService.d.ts.map +1 -0
- package/backend/dist/services/toolServerService.js +521 -0
- package/backend/dist/services/toolServerService.js.map +1 -0
- package/backend/dist/services/voiceAccessService.d.ts +10 -0
- package/backend/dist/services/voiceAccessService.d.ts.map +1 -0
- package/backend/dist/services/voiceAccessService.js +74 -0
- package/backend/dist/services/voiceAccessService.js.map +1 -0
- package/backend/dist/services/voiceProfileService.d.ts +20 -0
- package/backend/dist/services/voiceProfileService.d.ts.map +1 -1
- package/backend/dist/services/voiceProfileService.js +60 -0
- package/backend/dist/services/voiceProfileService.js.map +1 -1
- package/backend/dist/services/webSearchPlanService.d.ts +18 -0
- package/backend/dist/services/webSearchPlanService.d.ts.map +1 -0
- package/backend/dist/services/webSearchPlanService.js +160 -0
- package/backend/dist/services/webSearchPlanService.js.map +1 -0
- package/backend/dist/services/webSearchService.d.ts +9 -3
- package/backend/dist/services/webSearchService.d.ts.map +1 -1
- package/backend/dist/services/webSearchService.js +8 -2
- package/backend/dist/services/webSearchService.js.map +1 -1
- package/backend/dist/storage.d.ts +34 -1
- package/backend/dist/storage.d.ts.map +1 -1
- package/backend/dist/storage.js +164 -1
- package/backend/dist/storage.js.map +1 -1
- package/backend/dist/storageMappers.d.ts +2 -2
- package/backend/dist/storageMappers.d.ts.map +1 -1
- package/backend/dist/storageMappers.js.map +1 -1
- package/backend/dist/types/index.d.ts +269 -2
- package/backend/dist/types/index.d.ts.map +1 -1
- package/backend/dist/types/index.js.map +1 -1
- package/backend/dist/types/tools.d.ts +137 -0
- package/backend/dist/types/tools.d.ts.map +1 -0
- package/backend/dist/types/tools.js +25 -0
- package/backend/dist/types/tools.js.map +1 -0
- package/backend/dist/utils/automationSchedule.d.ts +26 -0
- package/backend/dist/utils/automationSchedule.d.ts.map +1 -0
- package/backend/dist/utils/automationSchedule.js +231 -0
- package/backend/dist/utils/automationSchedule.js.map +1 -0
- package/backend/dist/utils/chatDocumentContext.d.ts +26 -1
- package/backend/dist/utils/chatDocumentContext.d.ts.map +1 -1
- package/backend/dist/utils/chatDocumentContext.js +87 -18
- package/backend/dist/utils/chatDocumentContext.js.map +1 -1
- package/backend/dist/utils/documentExtraction.d.ts +69 -0
- package/backend/dist/utils/documentExtraction.d.ts.map +1 -0
- package/backend/dist/utils/documentExtraction.js +545 -0
- package/backend/dist/utils/documentExtraction.js.map +1 -0
- package/backend/dist/utils/hybridRetrieval.d.ts +34 -0
- package/backend/dist/utils/hybridRetrieval.d.ts.map +1 -0
- package/backend/dist/utils/hybridRetrieval.js +119 -0
- package/backend/dist/utils/hybridRetrieval.js.map +1 -0
- package/backend/dist/utils/icsCalendar.d.ts +27 -0
- package/backend/dist/utils/icsCalendar.d.ts.map +1 -0
- package/backend/dist/utils/icsCalendar.js +313 -0
- package/backend/dist/utils/icsCalendar.js.map +1 -0
- package/backend/dist/utils/imageEditUpload.d.ts +29 -0
- package/backend/dist/utils/imageEditUpload.d.ts.map +1 -0
- package/backend/dist/utils/imageEditUpload.js +82 -0
- package/backend/dist/utils/imageEditUpload.js.map +1 -0
- package/backend/dist/utils/logRedaction.d.ts +2 -0
- package/backend/dist/utils/logRedaction.d.ts.map +1 -0
- package/backend/dist/utils/logRedaction.js +66 -0
- package/backend/dist/utils/logRedaction.js.map +1 -0
- package/backend/dist/utils/logger.d.ts +3 -0
- package/backend/dist/utils/logger.d.ts.map +1 -1
- package/backend/dist/utils/logger.js +63 -20
- package/backend/dist/utils/logger.js.map +1 -1
- package/backend/dist/utils/pluginChatAdapter.d.ts +5 -1
- package/backend/dist/utils/pluginChatAdapter.d.ts.map +1 -1
- package/backend/dist/utils/pluginChatAdapter.js +83 -0
- package/backend/dist/utils/pluginChatAdapter.js.map +1 -1
- package/backend/dist/utils/pluginDefinitionTrust.js +1 -1
- package/backend/dist/utils/resourceLimits.d.ts +68 -2
- package/backend/dist/utils/resourceLimits.d.ts.map +1 -1
- package/backend/dist/utils/resourceLimits.js +66 -0
- package/backend/dist/utils/resourceLimits.js.map +1 -1
- package/backend/dist/utils/toolEgress.d.ts +22 -0
- package/backend/dist/utils/toolEgress.d.ts.map +1 -0
- package/backend/dist/utils/toolEgress.js +157 -0
- package/backend/dist/utils/toolEgress.js.map +1 -0
- package/backend/dist/utils/websocketMessages.d.ts +6 -0
- package/backend/dist/utils/websocketMessages.d.ts.map +1 -1
- package/backend/dist/utils/websocketMessages.js +8 -0
- package/backend/dist/utils/websocketMessages.js.map +1 -1
- package/backend/dist/utils/zipArchive.d.ts +32 -0
- package/backend/dist/utils/zipArchive.d.ts.map +1 -0
- package/backend/dist/utils/zipArchive.js +171 -0
- package/backend/dist/utils/zipArchive.js.map +1 -0
- package/backend/dist/websocketServer.d.ts.map +1 -1
- package/backend/dist/websocketServer.js +150 -37
- package/backend/dist/websocketServer.js.map +1 -1
- package/backend/package.json +1 -1
- package/frontend/dist/artifact-runtime/.build-fingerprint +1 -1
- package/frontend/dist/assets/index-BPsH9FII.js +80 -0
- package/frontend/dist/assets/index-BtXF0Oxa.css +1 -0
- package/frontend/dist/index.html +18 -18
- package/frontend/dist/js/{ArtifactContainer-B-hs0F5K.js → ArtifactContainer-CIoFoMv8.js} +1 -1
- package/frontend/dist/js/{ArtifactDemoPage-QFtJw-u4.js → ArtifactDemoPage-D0JEIAbZ.js} +1 -1
- package/frontend/dist/js/{ArtifactSandboxFrame-S9g_q47-.js → ArtifactSandboxFrame-REazyIrx.js} +2 -2
- package/frontend/dist/js/ArtifactSlideOutPanel-A-i94ghF.js +1 -0
- package/frontend/dist/js/AutomationsPage-Dow_lbIf.js +1 -0
- package/frontend/dist/js/{Button-BPf_pOxH.js → Button-C0r3J9G_.js} +1 -1
- package/frontend/dist/js/CalendarPage-DLjasZOd.js +1 -0
- package/frontend/dist/js/ChannelsPage-C8hvTD7J.js +1 -0
- package/frontend/dist/js/ChatPage-BAgBHUES.js +16 -0
- package/frontend/dist/js/EvaluationsPage-Bm8FRoeX.js +2 -0
- package/frontend/dist/js/FirstTimeSetup-BxJapNul.js +1 -0
- package/frontend/dist/js/GalleryPage-DHz9DkK7.js +1 -0
- package/frontend/dist/js/HomePage-BqNy-FWX.js +1 -0
- package/frontend/dist/js/LibreClawPage-BDzDILjY.js +1 -0
- package/frontend/dist/js/{ModelManagerModals-DBmm_slu.js → ModelManagerModals-DX9eT5r2.js} +1 -1
- package/frontend/dist/js/ModelSelector-ol8mK2ja.js +1 -0
- package/frontend/dist/js/NotesPage-DzHEx2O4.js +3 -0
- package/frontend/dist/js/{OptimizedSyntaxHighlighter-Dz7D1LPB.js → OptimizedSyntaxHighlighter-CZNPwN0i.js} +1 -1
- package/frontend/dist/js/PersonasPage-M_WKH2a9.js +13 -0
- package/frontend/dist/js/PluginUsagePage-Ctoka6JZ.js +1 -0
- package/frontend/dist/js/RichMessageContent-BqgafH4p.js +3 -0
- package/frontend/dist/js/SettingsModal-R9EDadAV.js +19 -0
- package/frontend/dist/js/{SettingsToggle-woG_WebF.js → SettingsToggle-BIVyfGjl.js} +1 -1
- package/frontend/dist/js/{SystemPage-Xr7XJv8c.js → SystemPage-aOkrSuY7.js} +1 -1
- package/frontend/dist/js/UserManagementPage-BOuaX3ay.js +1 -0
- package/frontend/dist/js/WorkPage-DxZISerQ.js +6 -0
- package/frontend/dist/js/api-6bSMtSNh.js +14 -0
- package/frontend/dist/js/{appStore-DXlmTjIC.js → appStore-CdObhQBb.js} +2 -2
- package/frontend/dist/js/ar-DT_VLBO3.js +2 -0
- package/frontend/dist/js/{authStore-B97lJ5Ee.js → authStore-DbqqrfZv.js} +3 -3
- package/frontend/dist/js/{babel-D24oDI9y.js → babel-DSqqk5kt.js} +1 -1
- package/frontend/dist/js/bn-BrvEqhGA.js +2 -0
- package/frontend/dist/js/calendarDates-Co3o_05Y.js +1 -0
- package/frontend/dist/js/chatStore-DbZPJNsu.js +3 -0
- package/frontend/dist/js/{config-CmEh9o8U.js → config-8KdgjtEx.js} +1 -1
- package/frontend/dist/js/cs-BthnM_YI.js +2 -0
- package/frontend/dist/js/da-DCth-6_j.js +2 -0
- package/frontend/dist/js/de-BBWt0gJ_.js +2 -0
- package/frontend/dist/js/es-DuslOPqM.js +2 -0
- package/frontend/dist/js/{estree-CZO8RS52.js → estree-BU5-LV_1.js} +7 -7
- package/frontend/dist/js/evaluationsApi-CwZJsgeG.js +1 -0
- package/frontend/dist/js/fr-DZAeg46S.js +2 -0
- package/frontend/dist/js/hi-ijDSiY77.js +2 -0
- package/frontend/dist/js/{html-C31pxhBG.js → html-DeoAy2bo.js} +10 -10
- package/frontend/dist/js/id-BS6qS0oq.js +2 -0
- package/frontend/dist/js/is-CO2PMDte.js +2 -0
- package/frontend/dist/js/it-ChhxT4hs.js +2 -0
- package/frontend/dist/js/ja-cyeqUeJN.js +2 -0
- package/frontend/dist/js/ko-CfqHTLXj.js +2 -0
- package/frontend/dist/js/{markdown-C1CEqXpL.js → markdown-DOLsYkBY.js} +6 -6
- package/frontend/dist/js/markdown-core-BK7WVOb7.js +32 -0
- package/frontend/dist/js/{markdown-math-DK846gXU.js → markdown-math-e1ByS3KY.js} +5 -5
- package/frontend/dist/js/{messageCodeStyles-BqQ6kOSU.js → messageCodeStyles-DhVgizF-.js} +2 -2
- package/frontend/dist/js/ms-DZ5pACe1.js +2 -0
- package/frontend/dist/js/nl-BsRsnOVp.js +2 -0
- package/frontend/dist/js/pl-DXSTAW4M.js +2 -0
- package/frontend/dist/js/{postcss-Bx9CTcIc.js → postcss-Bg15EcFE.js} +1 -1
- package/frontend/dist/js/pt-DmDx0JxB.js +2 -0
- package/frontend/dist/js/{react-vendor-C5yShNlM.js → react-vendor-BG_HPV6X.js} +1 -1
- package/frontend/dist/js/router-vendor-CbexJIsv.js +1 -0
- package/frontend/dist/js/ru-d2g8EWfY.js +2 -0
- package/frontend/dist/js/{standalone-Dz0pkxn0.js → standalone-BO5pzI8_.js} +5 -5
- package/frontend/dist/js/sv-CIhYUTVQ.js +2 -0
- package/frontend/dist/js/{syntax-highlight-3EEX6CGL.js → syntax-highlight-DcR9Vhb7.js} +3 -3
- package/frontend/dist/js/th-B3wSxanB.js +2 -0
- package/frontend/dist/js/tr-R4UGgPOY.js +2 -0
- package/frontend/dist/js/{typescript-DeXPhp90.js → typescript-6GJ8APmn.js} +2 -2
- package/frontend/dist/js/ui-CV0LYmkM.js +2 -0
- package/frontend/dist/js/ui-vendor-CWjIBW-z.js +177 -0
- package/frontend/dist/js/uk-BzUF4Vkt.js +2 -0
- package/frontend/dist/js/{useTranslation-Bho4HCyg.js → useTranslation-5b5UhAc9.js} +1 -1
- package/frontend/dist/js/{utils-D9W1Lnkn.js → utils-BGrdX9ie.js} +1 -1
- package/frontend/dist/js/{utils-vendor-DWh39_3x.js → utils-vendor-9bQ62WeD.js} +1 -1
- package/frontend/dist/js/vi-CKc6Fg4h.js +2 -0
- package/frontend/dist/js/{workApi-C4QtuhcE.js → workApi-BruWTlHM.js} +2 -2
- package/frontend/dist/js/{yaml-DioTxVLA.js → yaml-DUeWcOOH.js} +5 -5
- package/frontend/dist/js/zh-CXL-kDe8.js +2 -0
- package/package.json +2 -2
- package/plugins/openai.json +52 -9
- package/frontend/dist/assets/index-CDQ12KBq.js +0 -34
- package/frontend/dist/assets/index-DkFWzmFD.css +0 -1
- package/frontend/dist/js/ArtifactSlideOutPanel-D0UOo8RW.js +0 -1
- package/frontend/dist/js/ChatPage-fdd8500b.js +0 -23
- package/frontend/dist/js/FirstTimeSetup-BTjafGOq.js +0 -1
- package/frontend/dist/js/GalleryPage-hR9AJ6b-.js +0 -1
- package/frontend/dist/js/HomePage-JvTzq-Pp.js +0 -1
- package/frontend/dist/js/LibreClawPage-CLJvtXft.js +0 -1
- package/frontend/dist/js/ModelSelector-A-viVFEc.js +0 -1
- package/frontend/dist/js/ModelsPage-C4Rm-TZF.js +0 -2
- package/frontend/dist/js/NotesPage-C7bPU_KF.js +0 -1
- package/frontend/dist/js/PersonasPage-DpvxbFzv.js +0 -13
- package/frontend/dist/js/PluginUsagePage-laF-M2X9.js +0 -1
- package/frontend/dist/js/RichMessageContent-D0mK9vRA.js +0 -3
- package/frontend/dist/js/SettingsModal-DNEQ5sTt.js +0 -1
- package/frontend/dist/js/UserManagementPage-DGmbGTC2.js +0 -1
- package/frontend/dist/js/WorkPage-DNKFD9gS.js +0 -6
- package/frontend/dist/js/api-DGVZHSkS.js +0 -14
- package/frontend/dist/js/ar-DW79oSyN.js +0 -2
- package/frontend/dist/js/bn-BEAmE7VH.js +0 -2
- package/frontend/dist/js/chatStore-zilBUd1p.js +0 -3
- package/frontend/dist/js/cs-irpRMzRo.js +0 -2
- package/frontend/dist/js/da-cgilTVxc.js +0 -2
- package/frontend/dist/js/de-BU-eH3gU.js +0 -2
- package/frontend/dist/js/es-B2LRpZ0p.js +0 -2
- package/frontend/dist/js/fr-Cd5JM-oQ.js +0 -2
- package/frontend/dist/js/hi-CvOhdtiK.js +0 -2
- package/frontend/dist/js/id-BtRK5nR_.js +0 -2
- package/frontend/dist/js/is-CnEqwgRF.js +0 -2
- package/frontend/dist/js/it-CI6WjId6.js +0 -2
- package/frontend/dist/js/ja-B6gFOHi6.js +0 -2
- package/frontend/dist/js/ko-DuAhnmMf.js +0 -2
- package/frontend/dist/js/markdown-core-BmPPZiuD.js +0 -31
- package/frontend/dist/js/ms-C8BIP7MZ.js +0 -2
- package/frontend/dist/js/nl-DnEdspER.js +0 -2
- package/frontend/dist/js/pl-Ddyyvbse.js +0 -2
- package/frontend/dist/js/pt-BYuzm70F.js +0 -2
- package/frontend/dist/js/router-vendor-CbPDks39.js +0 -1
- package/frontend/dist/js/ru-CmSPFxPX.js +0 -2
- package/frontend/dist/js/sv-Dd6SQ9f1.js +0 -2
- package/frontend/dist/js/th-CpH8745B.js +0 -2
- package/frontend/dist/js/tr-DuVD75iJ.js +0 -2
- package/frontend/dist/js/ui-CO0DPqrI.js +0 -2
- package/frontend/dist/js/ui-vendor-mqjSfAaS.js +0 -177
- package/frontend/dist/js/uk-B0MU-M8k.js +0 -2
- package/frontend/dist/js/vi-BLmFNp_z.js +0 -2
- package/frontend/dist/js/zh-BQu0TLhw.js +0 -2
|
@@ -172,101 +172,1481 @@ 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
|
|
178
|
-
.prepare(`INSERT INTO knowledge_collections
|
|
179
|
-
(id, user_id, name, created_at, updated_at)
|
|
180
|
-
VALUES (?, ?, ?, ?, ?)
|
|
181
|
-
ON CONFLICT(id) DO UPDATE SET
|
|
182
|
-
name = excluded.name,
|
|
183
|
-
updated_at = excluded.updated_at
|
|
184
|
-
WHERE knowledge_collections.user_id = excluded.user_id`)
|
|
185
|
-
.run(collection.id, collection.user_id, collection.name, collection.created_at, collection.updated_at);
|
|
183
|
+
.prepare(`INSERT INTO knowledge_collections
|
|
184
|
+
(id, user_id, name, created_at, updated_at)
|
|
185
|
+
VALUES (?, ?, ?, ?, ?)
|
|
186
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
187
|
+
name = excluded.name,
|
|
188
|
+
updated_at = excluded.updated_at
|
|
189
|
+
WHERE knowledge_collections.user_id = excluded.user_id`)
|
|
190
|
+
.run(collection.id, collection.user_id, collection.name, collection.created_at, collection.updated_at);
|
|
191
|
+
}
|
|
192
|
+
async deleteAndDetach(collectionId, userId) {
|
|
193
|
+
const remove = this.database.transaction(() => {
|
|
194
|
+
this.database
|
|
195
|
+
.prepare('UPDATE documents SET collection_id = NULL WHERE collection_id = ? AND user_id = ?')
|
|
196
|
+
.run(collectionId, userId);
|
|
197
|
+
return this.database
|
|
198
|
+
.prepare('DELETE FROM knowledge_collections WHERE id = ? AND user_id = ?')
|
|
199
|
+
.run(collectionId, userId).changes;
|
|
200
|
+
});
|
|
201
|
+
return remove() > 0;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
class SQLiteNoteRepository {
|
|
205
|
+
constructor(database) {
|
|
206
|
+
this.database = database;
|
|
207
|
+
}
|
|
208
|
+
async listByOwner(userId, maximum) {
|
|
209
|
+
return this.database
|
|
210
|
+
.prepare(`SELECT * FROM notes
|
|
211
|
+
WHERE user_id = ?
|
|
212
|
+
ORDER BY pinned DESC, updated_at DESC
|
|
213
|
+
LIMIT ?`)
|
|
214
|
+
.all(userId, maximum);
|
|
215
|
+
}
|
|
216
|
+
async findByOwner(noteId, userId) {
|
|
217
|
+
return (this.database
|
|
218
|
+
.prepare('SELECT * FROM notes WHERE id = ? AND user_id = ?')
|
|
219
|
+
.get(noteId, userId) ?? null);
|
|
220
|
+
}
|
|
221
|
+
async replaceWithLimit(note, maximum) {
|
|
222
|
+
const replace = this.database.transaction(() => {
|
|
223
|
+
const exists = ensureSameOwner(this.database, 'notes', note.id, note.user_id);
|
|
224
|
+
if (!exists) {
|
|
225
|
+
const row = this.database
|
|
226
|
+
.prepare('SELECT COUNT(*) AS count FROM notes WHERE user_id = ?')
|
|
227
|
+
.get(note.user_id);
|
|
228
|
+
if (row.count >= maximum) {
|
|
229
|
+
throw new PersistenceResourceLimitError('note', maximum);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
this.database
|
|
233
|
+
.prepare(`INSERT INTO notes
|
|
234
|
+
(id, user_id, title, content, pinned, created_at, updated_at)
|
|
235
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
236
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
237
|
+
title = excluded.title,
|
|
238
|
+
content = excluded.content,
|
|
239
|
+
pinned = excluded.pinned,
|
|
240
|
+
updated_at = excluded.updated_at
|
|
241
|
+
WHERE notes.user_id = excluded.user_id`)
|
|
242
|
+
.run(note.id, note.user_id, note.title, note.content, note.pinned, note.created_at, note.updated_at);
|
|
243
|
+
});
|
|
244
|
+
replace();
|
|
245
|
+
}
|
|
246
|
+
async patchByOwner(noteId, userId, patch) {
|
|
247
|
+
const update = this.database.transaction(() => {
|
|
248
|
+
const assignments = [];
|
|
249
|
+
const values = [];
|
|
250
|
+
if (patch.title !== undefined) {
|
|
251
|
+
assignments.push('title = ?');
|
|
252
|
+
values.push(patch.title);
|
|
253
|
+
}
|
|
254
|
+
if (patch.content !== undefined) {
|
|
255
|
+
assignments.push('content = ?');
|
|
256
|
+
values.push(patch.content);
|
|
257
|
+
}
|
|
258
|
+
if (patch.pinned !== undefined) {
|
|
259
|
+
assignments.push('pinned = ?');
|
|
260
|
+
values.push(patch.pinned);
|
|
261
|
+
}
|
|
262
|
+
assignments.push('updated_at = ?');
|
|
263
|
+
values.push(patch.updated_at);
|
|
264
|
+
const result = this.database
|
|
265
|
+
.prepare(`UPDATE notes SET ${assignments.join(', ')}
|
|
266
|
+
WHERE id = ? AND user_id = ?`)
|
|
267
|
+
.run(...values, noteId, userId);
|
|
268
|
+
if (result.changes !== 1)
|
|
269
|
+
return null;
|
|
270
|
+
return this.database
|
|
271
|
+
.prepare('SELECT * FROM notes WHERE id = ? AND user_id = ?')
|
|
272
|
+
.get(noteId, userId);
|
|
273
|
+
});
|
|
274
|
+
return update.immediate();
|
|
275
|
+
}
|
|
276
|
+
async deleteByOwner(noteId, userId) {
|
|
277
|
+
return (this.database
|
|
278
|
+
.prepare('DELETE FROM notes WHERE id = ? AND user_id = ?')
|
|
279
|
+
.run(noteId, userId).changes > 0);
|
|
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
|
+
}
|
|
385
|
+
}
|
|
386
|
+
class SQLiteCalendarEventRepository {
|
|
387
|
+
constructor(database) {
|
|
388
|
+
this.database = database;
|
|
389
|
+
}
|
|
390
|
+
async listByOwnerBetween(userId, from, to, maximum) {
|
|
391
|
+
return this.database
|
|
392
|
+
.prepare(`SELECT * FROM calendar_events
|
|
393
|
+
WHERE user_id = ? AND start_at >= ? AND start_at < ?
|
|
394
|
+
ORDER BY start_at ASC
|
|
395
|
+
LIMIT ?`)
|
|
396
|
+
.all(userId, from, to, maximum);
|
|
397
|
+
}
|
|
398
|
+
async listRecurringByOwner(userId, maximum) {
|
|
399
|
+
return this.database
|
|
400
|
+
.prepare(`SELECT * FROM calendar_events
|
|
401
|
+
WHERE user_id = ? AND recurrence IS NOT NULL
|
|
402
|
+
ORDER BY start_at ASC
|
|
403
|
+
LIMIT ?`)
|
|
404
|
+
.all(userId, maximum);
|
|
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
|
+
}
|
|
430
|
+
async findByOwner(eventId, userId) {
|
|
431
|
+
return (this.database
|
|
432
|
+
.prepare('SELECT * FROM calendar_events WHERE id = ? AND user_id = ?')
|
|
433
|
+
.get(eventId, userId) ?? null);
|
|
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
|
+
}
|
|
457
|
+
async replaceWithLimit(event, maximum) {
|
|
458
|
+
const replace = this.database.transaction(() => {
|
|
459
|
+
const exists = ensureSameOwner(this.database, 'calendar_events', event.id, event.user_id);
|
|
460
|
+
if (!exists) {
|
|
461
|
+
const row = this.database
|
|
462
|
+
.prepare('SELECT COUNT(*) AS count FROM calendar_events WHERE user_id = ?')
|
|
463
|
+
.get(event.user_id);
|
|
464
|
+
if (row.count >= maximum) {
|
|
465
|
+
throw new PersistenceResourceLimitError('calendar-event', maximum);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
this.database
|
|
469
|
+
.prepare(`INSERT INTO calendar_events
|
|
470
|
+
(id, user_id, title, notes, start_at, end_at, all_day,
|
|
471
|
+
recurrence, calendar_id, reminder_minutes,
|
|
472
|
+
last_reminded_occurrence, created_at, updated_at)
|
|
473
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
474
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
475
|
+
title = excluded.title,
|
|
476
|
+
notes = excluded.notes,
|
|
477
|
+
start_at = excluded.start_at,
|
|
478
|
+
end_at = excluded.end_at,
|
|
479
|
+
all_day = excluded.all_day,
|
|
480
|
+
recurrence = excluded.recurrence,
|
|
481
|
+
calendar_id = excluded.calendar_id,
|
|
482
|
+
reminder_minutes = excluded.reminder_minutes,
|
|
483
|
+
last_reminded_occurrence = excluded.last_reminded_occurrence,
|
|
484
|
+
updated_at = excluded.updated_at
|
|
485
|
+
WHERE calendar_events.user_id = excluded.user_id`)
|
|
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);
|
|
1027
|
+
});
|
|
1028
|
+
replace();
|
|
1029
|
+
}
|
|
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);
|
|
186
1340
|
}
|
|
187
|
-
async
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
.run(collectionId, userId);
|
|
192
|
-
return this.database
|
|
193
|
-
.prepare('DELETE FROM knowledge_collections WHERE id = ? AND user_id = ?')
|
|
194
|
-
.run(collectionId, userId).changes;
|
|
195
|
-
});
|
|
196
|
-
return remove() > 0;
|
|
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);
|
|
197
1345
|
}
|
|
198
1346
|
}
|
|
199
|
-
class
|
|
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 {
|
|
200
1421
|
constructor(database) {
|
|
201
1422
|
this.database = database;
|
|
202
1423
|
}
|
|
203
1424
|
async listByOwner(userId, maximum) {
|
|
204
1425
|
return this.database
|
|
205
|
-
.prepare(`SELECT * FROM
|
|
1426
|
+
.prepare(`SELECT * FROM prompts
|
|
206
1427
|
WHERE user_id = ?
|
|
207
1428
|
ORDER BY updated_at DESC
|
|
208
1429
|
LIMIT ?`)
|
|
209
1430
|
.all(userId, maximum);
|
|
210
1431
|
}
|
|
211
|
-
async findByOwner(
|
|
1432
|
+
async findByOwner(promptId, userId) {
|
|
212
1433
|
return (this.database
|
|
213
|
-
.prepare('SELECT * FROM
|
|
214
|
-
.get(
|
|
1434
|
+
.prepare('SELECT * FROM prompts WHERE id = ? AND user_id = ?')
|
|
1435
|
+
.get(promptId, userId) ?? null);
|
|
215
1436
|
}
|
|
216
|
-
async
|
|
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) {
|
|
217
1448
|
const replace = this.database.transaction(() => {
|
|
218
|
-
const exists = ensureSameOwner(this.database, '
|
|
1449
|
+
const exists = ensureSameOwner(this.database, 'prompts', prompt.id, prompt.user_id);
|
|
219
1450
|
if (!exists) {
|
|
220
1451
|
const row = this.database
|
|
221
|
-
.prepare('SELECT COUNT(*) AS count FROM
|
|
222
|
-
.get(
|
|
1452
|
+
.prepare('SELECT COUNT(*) AS count FROM prompts WHERE user_id = ?')
|
|
1453
|
+
.get(prompt.user_id);
|
|
223
1454
|
if (row.count >= maximum) {
|
|
224
|
-
throw new PersistenceResourceLimitError('
|
|
1455
|
+
throw new PersistenceResourceLimitError('prompt', maximum);
|
|
225
1456
|
}
|
|
226
1457
|
}
|
|
227
1458
|
this.database
|
|
228
|
-
.prepare(`INSERT INTO
|
|
229
|
-
(id, user_id, title, content,
|
|
230
|
-
|
|
1459
|
+
.prepare(`INSERT INTO prompts
|
|
1460
|
+
(id, user_id, slug, title, description, content, variables,
|
|
1461
|
+
tags, version, created_at, updated_at)
|
|
1462
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
231
1463
|
ON CONFLICT(id) DO UPDATE SET
|
|
1464
|
+
slug = excluded.slug,
|
|
232
1465
|
title = excluded.title,
|
|
1466
|
+
description = excluded.description,
|
|
233
1467
|
content = excluded.content,
|
|
1468
|
+
variables = excluded.variables,
|
|
1469
|
+
tags = excluded.tags,
|
|
1470
|
+
version = excluded.version,
|
|
234
1471
|
updated_at = excluded.updated_at
|
|
235
|
-
WHERE
|
|
236
|
-
.run(
|
|
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
|
+
}
|
|
237
1482
|
});
|
|
238
1483
|
replace();
|
|
239
1484
|
}
|
|
240
|
-
async
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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) {
|
|
1500
|
+
return (this.database
|
|
1501
|
+
.prepare('DELETE FROM prompts WHERE id = ? AND user_id = ?')
|
|
1502
|
+
.run(promptId, userId).changes > 0);
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
class SQLiteSkillRepository {
|
|
1506
|
+
constructor(database) {
|
|
1507
|
+
this.database = database;
|
|
1508
|
+
}
|
|
1509
|
+
async listByOwner(userId, maximum) {
|
|
1510
|
+
return this.database
|
|
1511
|
+
.prepare(`SELECT * FROM skills
|
|
1512
|
+
WHERE user_id = ?
|
|
1513
|
+
ORDER BY updated_at DESC
|
|
1514
|
+
LIMIT ?`)
|
|
1515
|
+
.all(userId, maximum);
|
|
1516
|
+
}
|
|
1517
|
+
async findByOwner(skillId, userId) {
|
|
1518
|
+
return (this.database
|
|
1519
|
+
.prepare('SELECT * FROM skills WHERE id = ? AND user_id = ?')
|
|
1520
|
+
.get(skillId, userId) ?? null);
|
|
1521
|
+
}
|
|
1522
|
+
async findById(skillId) {
|
|
1523
|
+
return (this.database
|
|
1524
|
+
.prepare('SELECT * FROM skills WHERE id = ?')
|
|
1525
|
+
.get(skillId) ?? null);
|
|
1526
|
+
}
|
|
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) {
|
|
1533
|
+
const replace = this.database.transaction(() => {
|
|
1534
|
+
const exists = ensureSameOwner(this.database, 'skills', skill.id, skill.user_id);
|
|
1535
|
+
if (!exists) {
|
|
1536
|
+
const row = this.database
|
|
1537
|
+
.prepare('SELECT COUNT(*) AS count FROM skills WHERE user_id = ?')
|
|
1538
|
+
.get(skill.user_id);
|
|
1539
|
+
if (row.count >= maximum) {
|
|
1540
|
+
throw new PersistenceResourceLimitError('skill', maximum);
|
|
1541
|
+
}
|
|
247
1542
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
1543
|
+
this.database
|
|
1544
|
+
.prepare(`INSERT INTO skills
|
|
1545
|
+
(id, user_id, slug, name, description, instructions, enabled,
|
|
1546
|
+
version, created_at, updated_at)
|
|
1547
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1548
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
1549
|
+
slug = excluded.slug,
|
|
1550
|
+
name = excluded.name,
|
|
1551
|
+
description = excluded.description,
|
|
1552
|
+
instructions = excluded.instructions,
|
|
1553
|
+
enabled = excluded.enabled,
|
|
1554
|
+
version = excluded.version,
|
|
1555
|
+
updated_at = excluded.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);
|
|
251
1565
|
}
|
|
252
|
-
assignments.push('updated_at = ?');
|
|
253
|
-
values.push(patch.updated_at);
|
|
254
|
-
const result = this.database
|
|
255
|
-
.prepare(`UPDATE notes SET ${assignments.join(', ')}
|
|
256
|
-
WHERE id = ? AND user_id = ?`)
|
|
257
|
-
.run(...values, noteId, userId);
|
|
258
|
-
if (result.changes !== 1)
|
|
259
|
-
return null;
|
|
260
|
-
return this.database
|
|
261
|
-
.prepare('SELECT * FROM notes WHERE id = ? AND user_id = ?')
|
|
262
|
-
.get(noteId, userId);
|
|
263
1566
|
});
|
|
264
|
-
|
|
1567
|
+
replace();
|
|
265
1568
|
}
|
|
266
|
-
async
|
|
1569
|
+
async listVersions(skillId, maximum) {
|
|
1570
|
+
return this.database
|
|
1571
|
+
.prepare(`SELECT * FROM skill_versions
|
|
1572
|
+
WHERE skill_id = ?
|
|
1573
|
+
ORDER BY version DESC
|
|
1574
|
+
LIMIT ?`)
|
|
1575
|
+
.all(skillId, maximum);
|
|
1576
|
+
}
|
|
1577
|
+
async findVersion(skillId, version) {
|
|
267
1578
|
return (this.database
|
|
268
|
-
.prepare('
|
|
269
|
-
.
|
|
1579
|
+
.prepare('SELECT * FROM skill_versions WHERE skill_id = ? AND version = ?')
|
|
1580
|
+
.get(skillId, version) ?? null);
|
|
1581
|
+
}
|
|
1582
|
+
async deleteByOwner(skillId, userId) {
|
|
1583
|
+
return (this.database
|
|
1584
|
+
.prepare('DELETE FROM skills WHERE id = ? AND user_id = ?')
|
|
1585
|
+
.run(skillId, userId).changes > 0);
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
class SQLiteSkillFileRepository {
|
|
1589
|
+
constructor(database) {
|
|
1590
|
+
this.database = database;
|
|
1591
|
+
}
|
|
1592
|
+
async listBySkill(skillId) {
|
|
1593
|
+
return this.database
|
|
1594
|
+
.prepare(`SELECT * FROM skill_files
|
|
1595
|
+
WHERE skill_id = ?
|
|
1596
|
+
ORDER BY path ASC`)
|
|
1597
|
+
.all(skillId);
|
|
1598
|
+
}
|
|
1599
|
+
async find(skillId, path) {
|
|
1600
|
+
return (this.database
|
|
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();
|
|
1628
|
+
}
|
|
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();
|
|
1645
|
+
}
|
|
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);
|
|
270
1650
|
}
|
|
271
1651
|
}
|
|
272
1652
|
class SQLiteSessionFolderRepository {
|
|
@@ -440,6 +1820,10 @@ const ARCHIVE_TABLES = {
|
|
|
440
1820
|
'knowledge-collection': 'knowledge_collections',
|
|
441
1821
|
document: 'documents',
|
|
442
1822
|
persona: 'personas',
|
|
1823
|
+
prompt: 'prompts',
|
|
1824
|
+
skill: 'skills',
|
|
1825
|
+
'tool-server': 'tool_servers',
|
|
1826
|
+
calendar: 'calendars',
|
|
443
1827
|
};
|
|
444
1828
|
class SQLiteDataArchiveRepository {
|
|
445
1829
|
constructor(database) {
|
|
@@ -651,13 +2035,251 @@ class SQLiteDataArchiveRepository {
|
|
|
651
2035
|
apply.immediate();
|
|
652
2036
|
}
|
|
653
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
|
+
}
|
|
654
2255
|
export const createSQLiteResourceRepositories = (database) => ({
|
|
655
2256
|
chatSessions: new SQLiteChatSessionRepository(database),
|
|
656
2257
|
knowledgeCollections: new SQLiteKnowledgeCollectionRepository(database),
|
|
657
2258
|
notes: new SQLiteNoteRepository(database),
|
|
2259
|
+
calendars: new SQLiteCalendarRepository(database),
|
|
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),
|
|
2265
|
+
automations: new SQLiteAutomationRepository(database),
|
|
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),
|
|
658
2274
|
sessionFolders: new SQLiteSessionFolderRepository(database),
|
|
659
2275
|
preferences: new SQLitePreferenceRepository(database),
|
|
660
2276
|
systemSettings: new SQLiteSystemSettingRepository(database),
|
|
661
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),
|
|
662
2284
|
});
|
|
663
2285
|
//# sourceMappingURL=sqliteResourceRepositories.js.map
|