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