libre-webui 0.25.0 → 0.27.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 +33 -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/postgresMfaPushRecoveryMigration.d.ts +9 -0
- package/backend/dist/persistence/postgresMfaPushRecoveryMigration.d.ts.map +1 -0
- package/backend/dist/persistence/postgresMfaPushRecoveryMigration.js +107 -0
- package/backend/dist/persistence/postgresMfaPushRecoveryMigration.js.map +1 -0
- package/backend/dist/persistence/postgresMigrationRegistry.d.ts.map +1 -1
- package/backend/dist/persistence/postgresMigrationRegistry.js +12 -0
- package/backend/dist/persistence/postgresMigrationRegistry.js.map +1 -1
- package/backend/dist/persistence/postgresNotesV2Migration.d.ts +4 -0
- package/backend/dist/persistence/postgresNotesV2Migration.d.ts.map +1 -0
- package/backend/dist/persistence/postgresNotesV2Migration.js +57 -0
- package/backend/dist/persistence/postgresNotesV2Migration.js.map +1 -0
- package/backend/dist/persistence/postgresResourceRepositories.d.ts.map +1 -1
- package/backend/dist/persistence/postgresResourceRepositories.js +1930 -133
- package/backend/dist/persistence/postgresResourceRepositories.js.map +1 -1
- package/backend/dist/persistence/postgresSecurityRepositories.d.ts.map +1 -1
- package/backend/dist/persistence/postgresSecurityRepositories.js +150 -0
- package/backend/dist/persistence/postgresSecurityRepositories.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 +600 -3
- package/backend/dist/persistence/resourceTypes.d.ts.map +1 -1
- package/backend/dist/persistence/resourceTypes.js.map +1 -1
- package/backend/dist/persistence/securityTypes.d.ts +59 -0
- package/backend/dist/persistence/securityTypes.d.ts.map +1 -1
- package/backend/dist/persistence/sqliteExtensionRepositories.d.ts.map +1 -1
- package/backend/dist/persistence/sqliteExtensionRepositories.js +26 -4
- package/backend/dist/persistence/sqliteExtensionRepositories.js.map +1 -1
- package/backend/dist/persistence/sqliteMigrations.d.ts +10 -0
- package/backend/dist/persistence/sqliteMigrations.d.ts.map +1 -1
- package/backend/dist/persistence/sqliteMigrations.js +1492 -131
- 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 +1657 -134
- package/backend/dist/persistence/sqliteResourceRepositories.js.map +1 -1
- package/backend/dist/persistence/sqliteSecurityRepositories.d.ts.map +1 -1
- package/backend/dist/persistence/sqliteSecurityRepositories.js +134 -0
- package/backend/dist/persistence/sqliteSecurityRepositories.js.map +1 -1
- package/backend/dist/persistence/sqliteToPostgresMigration.d.ts.map +1 -1
- package/backend/dist/persistence/sqliteToPostgresMigration.js +302 -3
- package/backend/dist/persistence/sqliteToPostgresMigration.js.map +1 -1
- package/backend/dist/platform/jobs/domainJobContracts.d.ts +10 -0
- package/backend/dist/platform/jobs/domainJobContracts.d.ts.map +1 -1
- package/backend/dist/platform/jobs/domainJobContracts.js +10 -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 +374 -4
- 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/auth.d.ts.map +1 -1
- package/backend/dist/routes/auth.js +419 -1
- package/backend/dist/routes/auth.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 +68 -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/push.d.ts +3 -0
- package/backend/dist/routes/push.d.ts.map +1 -0
- package/backend/dist/routes/push.js +105 -0
- package/backend/dist/routes/push.js.map +1 -0
- package/backend/dist/routes/recovery.d.ts +3 -0
- package/backend/dist/routes/recovery.d.ts.map +1 -0
- package/backend/dist/routes/recovery.js +83 -0
- package/backend/dist/routes/recovery.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/users.d.ts.map +1 -1
- package/backend/dist/routes/users.js +35 -0
- package/backend/dist/routes/users.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/authService.d.ts +6 -0
- package/backend/dist/services/authService.d.ts.map +1 -1
- package/backend/dist/services/authService.js +9 -0
- package/backend/dist/services/authService.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 +40 -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 +26 -4
- package/backend/dist/services/dataArchiveService.js.map +1 -1
- package/backend/dist/services/documentMediaExtractionService.d.ts +40 -0
- package/backend/dist/services/documentMediaExtractionService.d.ts.map +1 -0
- package/backend/dist/services/documentMediaExtractionService.js +157 -0
- package/backend/dist/services/documentMediaExtractionService.js.map +1 -0
- package/backend/dist/services/documentService.d.ts +64 -1
- package/backend/dist/services/documentService.d.ts.map +1 -1
- package/backend/dist/services/documentService.js +342 -83
- 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/encryptionService.d.ts +6 -0
- package/backend/dist/services/encryptionService.d.ts.map +1 -1
- package/backend/dist/services/encryptionService.js +12 -0
- package/backend/dist/services/encryptionService.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/mfaService.d.ts +64 -0
- package/backend/dist/services/mfaService.d.ts.map +1 -0
- package/backend/dist/services/mfaService.js +263 -0
- package/backend/dist/services/mfaService.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 +60 -0
- package/backend/dist/services/notificationService.d.ts.map +1 -0
- package/backend/dist/services/notificationService.js +340 -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/preferencesService.d.ts +1 -1
- package/backend/dist/services/preferencesService.d.ts.map +1 -1
- package/backend/dist/services/preferencesService.js +1 -0
- package/backend/dist/services/preferencesService.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/recoveryDrillService.d.ts +57 -0
- package/backend/dist/services/recoveryDrillService.d.ts.map +1 -0
- package/backend/dist/services/recoveryDrillService.js +363 -0
- package/backend/dist/services/recoveryDrillService.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/services/webPushService.d.ts +71 -0
- package/backend/dist/services/webPushService.d.ts.map +1 -0
- package/backend/dist/services/webPushService.js +328 -0
- package/backend/dist/services/webPushService.js.map +1 -0
- package/backend/dist/services/webauthnService.d.ts +58 -0
- package/backend/dist/services/webauthnService.d.ts.map +1 -0
- package/backend/dist/services/webauthnService.js +300 -0
- package/backend/dist/services/webauthnService.js.map +1 -0
- 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 +199 -3
- 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 +97 -0
- package/backend/dist/utils/documentExtraction.d.ts.map +1 -0
- package/backend/dist/utils/documentExtraction.js +651 -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 +23 -0
- package/backend/dist/utils/toolEgress.d.ts.map +1 -0
- package/backend/dist/utils/toolEgress.js +169 -0
- package/backend/dist/utils/toolEgress.js.map +1 -0
- package/backend/dist/utils/totp.d.ts +19 -0
- package/backend/dist/utils/totp.d.ts.map +1 -0
- package/backend/dist/utils/totp.js +117 -0
- package/backend/dist/utils/totp.js.map +1 -0
- package/backend/dist/utils/webauthn.d.ts +43 -0
- package/backend/dist/utils/webauthn.d.ts.map +1 -0
- package/backend/dist/utils/webauthn.js +294 -0
- package/backend/dist/utils/webauthn.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-DBAf_VaS.js +47 -0
- package/frontend/dist/assets/index-kn_Axjts.css +1 -0
- package/frontend/dist/fonts/jetbrains-mono-latin-400-normal.woff2 +0 -0
- package/frontend/dist/fonts/jetbrains-mono-latin-700-normal.woff2 +0 -0
- package/frontend/dist/fonts/jetbrains-mono.css +24 -0
- package/frontend/dist/index.html +27 -20
- package/frontend/dist/js/ArtifactContainer-C9zNtCay.js +1 -0
- package/frontend/dist/js/{ArtifactDemoPage-BeNs3gHp.js → ArtifactDemoPage-C-EBFBCt.js} +1 -1
- package/frontend/dist/js/ArtifactSandboxFrame-CTA1j_d1.js +111 -0
- package/frontend/dist/js/ArtifactSlideOutPanel-BOENbOiv.js +1 -0
- package/frontend/dist/js/AutomationsPage-C58MQ4te.js +1 -0
- package/frontend/dist/js/{Button-BPf_pOxH.js → Button-C0r3J9G_.js} +1 -1
- package/frontend/dist/js/CalendarPage-BcdAZ292.js +1 -0
- package/frontend/dist/js/ChannelsPage-DPr9Yajs.js +1 -0
- package/frontend/dist/js/ChatPage-DsbB748s.js +14 -0
- package/frontend/dist/js/EvaluationsPage-FWIJu2fI.js +2 -0
- package/frontend/dist/js/FirstTimeSetup-Cy-w6xko.js +1 -0
- package/frontend/dist/js/GalleryPage-DDAtxM-x.js +1 -0
- package/frontend/dist/js/HomePage-DLKwmE7n.js +1 -0
- package/frontend/dist/js/LibreClawPage-vKOj-9w9.js +1 -0
- package/frontend/dist/js/{ModelManagerModals-DcLGYN3l.js → ModelManagerModals-BY5TK38_.js} +1 -1
- package/frontend/dist/js/ModelSelector-CoCS6MZ9.js +1 -0
- package/frontend/dist/js/NotesPage-BElIpXgA.js +3 -0
- package/frontend/dist/js/OptimizedSyntaxHighlighter-xgyE3dRA.js +1 -0
- package/frontend/dist/js/PersonasPage-P40jVbfU.js +13 -0
- package/frontend/dist/js/PluginUsagePage-CKUEPCJN.js +1 -0
- package/frontend/dist/js/RichMessageContent-Bm2WaEGi.js +3 -0
- package/frontend/dist/js/SettingsModal-DxSGzx9A.js +19 -0
- package/frontend/dist/js/{SettingsToggle-woG_WebF.js → SettingsToggle-BIVyfGjl.js} +1 -1
- package/frontend/dist/js/SystemPage-BT07dTMT.js +1 -0
- package/frontend/dist/js/UserManagementPage-B9Vz5cm3.js +1 -0
- package/frontend/dist/js/WorkPage-qoN_e_jK.js +6 -0
- package/frontend/dist/js/api-D7Ff5M2Q.js +14 -0
- package/frontend/dist/js/appStore-DmV5wqDz.js +2 -0
- package/frontend/dist/js/ar-YHHTVP8f.js +2 -0
- package/frontend/dist/js/artifactHtml-DDmfpcB0.js +41 -0
- package/frontend/dist/js/{authStore-DbMdvNgJ.js → authStore-BYW8-kP6.js} +3 -3
- package/frontend/dist/js/{babel-BsYoxD-7.js → babel-DSqqk5kt.js} +1 -1
- package/frontend/dist/js/bn-C8VTxBVl.js +2 -0
- package/frontend/dist/js/chatStore-D-NTlArO.js +3 -0
- package/frontend/dist/js/{config-DSOYQ9a3.js → config-BqZ5304Q.js} +1 -1
- package/frontend/dist/js/cs-CCngB5fC.js +2 -0
- package/frontend/dist/js/da-CM2PP2ma.js +2 -0
- package/frontend/dist/js/de-BqVkD-dw.js +2 -0
- package/frontend/dist/js/es-BtWsWBjZ.js +2 -0
- package/frontend/dist/js/{estree-BcnBcOAx.js → estree-DPkpBuOU.js} +3 -3
- package/frontend/dist/js/evaluationsApi-Dnf2dTd-.js +1 -0
- package/frontend/dist/js/fr-C68w9abT.js +2 -0
- package/frontend/dist/js/hi-DtBlcyXZ.js +2 -0
- package/frontend/dist/js/{html-DkY55Jys.js → html-BpGUXG8E.js} +2 -2
- package/frontend/dist/js/id-BWzIu8tn.js +2 -0
- package/frontend/dist/js/is-yeFwu4dJ.js +2 -0
- package/frontend/dist/js/it-AxEMG8Jg.js +2 -0
- package/frontend/dist/js/ja-B-VyokB8.js +2 -0
- package/frontend/dist/js/ko-CvwzYr9k.js +2 -0
- package/frontend/dist/js/{markdown-IZSI9DXw.js → markdown-DCBRWJSK.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-BQwf7sSz.js +2 -0
- package/frontend/dist/js/ms-BhEwb4b7.js +2 -0
- package/frontend/dist/js/nl-Dq-QRx1L.js +2 -0
- package/frontend/dist/js/pl-BNEy8mQm.js +2 -0
- package/frontend/dist/js/{postcss-BJ0okz7n.js → postcss-Bg15EcFE.js} +1 -1
- package/frontend/dist/js/pt-_3Jl3FdY.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-IhQiJ8ta.js +2 -0
- package/frontend/dist/js/{standalone-Csw3jfJ_.js → standalone-BFrK0R2A.js} +3 -3
- package/frontend/dist/js/sv-DWunXUNz.js +2 -0
- package/frontend/dist/js/{syntax-highlight-3EEX6CGL.js → syntax-highlight-P2zo9o06.js} +4 -4
- package/frontend/dist/js/th-DVnW2yyh.js +2 -0
- package/frontend/dist/js/tr-Do8CFmhR.js +2 -0
- package/frontend/dist/js/{typescript-CnF4UVx7.js → typescript-DYVyvmm_.js} +1 -1
- package/frontend/dist/js/ui-db9meqAB.js +2 -0
- package/frontend/dist/js/ui-vendor-C0DZ8-Cm.js +177 -0
- package/frontend/dist/js/uk-BNPKIZga.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-CZMFo9je.js +2 -0
- package/frontend/dist/js/{workApi-C3uy9EDT.js → workApi-CcprMH_1.js} +3 -3
- package/frontend/dist/js/{yaml-DzT5hBSH.js → yaml-DUeWcOOH.js} +2 -2
- package/frontend/dist/js/zh-Pg0881v0.js +2 -0
- package/frontend/dist/manifest.webmanifest +32 -0
- package/frontend/dist/sw.js +125 -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/ArtifactContainer-CpUi6ekF.js +0 -1
- package/frontend/dist/js/ArtifactSandboxFrame-qsj_Y3LV.js +0 -151
- 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/FirstTimeSetup-Df0YJni3.js +0 -1
- 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/OptimizedSyntaxHighlighter-Dz7D1LPB.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/appStore-0JnCUlEq.js +0 -2
- 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/messageCodeStyles-BFNR9Wv6.js +0 -2
- 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
|
@@ -34,17 +34,109 @@ const namedResource = (row) => ({
|
|
|
34
34
|
});
|
|
35
35
|
const note = (row) => ({
|
|
36
36
|
...row,
|
|
37
|
+
pinned: number(row.pinned ?? 0, 'note pinned'),
|
|
37
38
|
created_at: number(row.created_at, 'note created_at'),
|
|
38
39
|
updated_at: number(row.updated_at, 'note updated_at'),
|
|
39
40
|
});
|
|
41
|
+
const noteRevision = (row) => ({
|
|
42
|
+
...row,
|
|
43
|
+
created_at: number(row.created_at, 'note revision created_at'),
|
|
44
|
+
});
|
|
45
|
+
const noteAttachment = (row) => ({
|
|
46
|
+
...row,
|
|
47
|
+
size: number(row.size, 'note attachment size'),
|
|
48
|
+
created_at: number(row.created_at, 'note attachment created_at'),
|
|
49
|
+
});
|
|
40
50
|
const calendarEvent = (row) => ({
|
|
41
51
|
...row,
|
|
42
52
|
start_at: number(row.start_at, 'calendar event start_at'),
|
|
43
53
|
end_at: row.end_at === null ? null : number(row.end_at, 'calendar event end_at'),
|
|
44
54
|
all_day: number(row.all_day, 'calendar event all_day'),
|
|
55
|
+
reminder_minutes: row.reminder_minutes === null
|
|
56
|
+
? null
|
|
57
|
+
: number(row.reminder_minutes, 'calendar event reminder_minutes'),
|
|
58
|
+
last_reminded_occurrence: row.last_reminded_occurrence === null
|
|
59
|
+
? null
|
|
60
|
+
: number(row.last_reminded_occurrence, 'calendar event last_reminded_occurrence'),
|
|
45
61
|
created_at: number(row.created_at, 'calendar event created_at'),
|
|
46
62
|
updated_at: number(row.updated_at, 'calendar event updated_at'),
|
|
47
63
|
});
|
|
64
|
+
const calendar = (row) => ({
|
|
65
|
+
...row,
|
|
66
|
+
created_at: number(row.created_at, 'calendar created_at'),
|
|
67
|
+
updated_at: number(row.updated_at, 'calendar updated_at'),
|
|
68
|
+
});
|
|
69
|
+
const channel = (row) => ({
|
|
70
|
+
...row,
|
|
71
|
+
created_at: number(row.created_at, 'channel created_at'),
|
|
72
|
+
updated_at: number(row.updated_at, 'channel updated_at'),
|
|
73
|
+
archived_at: row.archived_at === null
|
|
74
|
+
? null
|
|
75
|
+
: number(row.archived_at, 'channel archived_at'),
|
|
76
|
+
});
|
|
77
|
+
const channelMember = (row) => ({
|
|
78
|
+
...row,
|
|
79
|
+
joined_at: number(row.joined_at, 'channel member joined_at'),
|
|
80
|
+
last_read_at: number(row.last_read_at, 'channel member last_read_at'),
|
|
81
|
+
});
|
|
82
|
+
const channelMessage = (row) => ({
|
|
83
|
+
...row,
|
|
84
|
+
created_at: number(row.created_at, 'channel message created_at'),
|
|
85
|
+
updated_at: number(row.updated_at, 'channel message updated_at'),
|
|
86
|
+
edited_at: row.edited_at === null
|
|
87
|
+
? null
|
|
88
|
+
: number(row.edited_at, 'channel message edited_at'),
|
|
89
|
+
deleted_at: row.deleted_at === null
|
|
90
|
+
? null
|
|
91
|
+
: number(row.deleted_at, 'channel message deleted_at'),
|
|
92
|
+
pinned_at: row.pinned_at === null
|
|
93
|
+
? null
|
|
94
|
+
: number(row.pinned_at, 'channel message pinned_at'),
|
|
95
|
+
});
|
|
96
|
+
const channelReaction = (row) => ({
|
|
97
|
+
...row,
|
|
98
|
+
created_at: number(row.created_at, 'channel reaction created_at'),
|
|
99
|
+
});
|
|
100
|
+
const channelAttachment = (row) => ({
|
|
101
|
+
...row,
|
|
102
|
+
size: number(row.size, 'channel attachment size'),
|
|
103
|
+
created_at: number(row.created_at, 'channel attachment created_at'),
|
|
104
|
+
});
|
|
105
|
+
const notification = (row) => ({
|
|
106
|
+
...row,
|
|
107
|
+
created_at: number(row.created_at, 'notification created_at'),
|
|
108
|
+
read_at: row.read_at === null ? null : number(row.read_at, 'notification read_at'),
|
|
109
|
+
});
|
|
110
|
+
const webhookTarget = (row) => ({
|
|
111
|
+
...row,
|
|
112
|
+
enabled: number(row.enabled, 'webhook target enabled'),
|
|
113
|
+
created_at: number(row.created_at, 'webhook target created_at'),
|
|
114
|
+
updated_at: number(row.updated_at, 'webhook target updated_at'),
|
|
115
|
+
});
|
|
116
|
+
const pushSubscription = (row) => ({
|
|
117
|
+
...row,
|
|
118
|
+
created_at: number(row.created_at, 'push subscription created_at'),
|
|
119
|
+
last_used_at: row.last_used_at === null
|
|
120
|
+
? null
|
|
121
|
+
: number(row.last_used_at, 'push subscription last_used_at'),
|
|
122
|
+
});
|
|
123
|
+
const recoveryDrill = (row) => ({
|
|
124
|
+
...row,
|
|
125
|
+
started_at: number(row.started_at, 'recovery drill started_at'),
|
|
126
|
+
finished_at: row.finished_at === null
|
|
127
|
+
? null
|
|
128
|
+
: number(row.finished_at, 'recovery drill finished_at'),
|
|
129
|
+
snapshot_bytes: row.snapshot_bytes === null
|
|
130
|
+
? null
|
|
131
|
+
: number(row.snapshot_bytes, 'recovery drill snapshot_bytes'),
|
|
132
|
+
rpo_seconds: row.rpo_seconds === null
|
|
133
|
+
? null
|
|
134
|
+
: number(row.rpo_seconds, 'recovery drill rpo_seconds'),
|
|
135
|
+
restore_ms: row.restore_ms === null
|
|
136
|
+
? null
|
|
137
|
+
: number(row.restore_ms, 'recovery drill restore_ms'),
|
|
138
|
+
created_at: number(row.created_at, 'recovery drill created_at'),
|
|
139
|
+
});
|
|
48
140
|
const automation = (row) => ({
|
|
49
141
|
...row,
|
|
50
142
|
next_run_at: row.next_run_at === null
|
|
@@ -68,6 +160,66 @@ const automationRun = (row) => ({
|
|
|
68
160
|
seen_at: row.seen_at === null ? null : number(row.seen_at, 'automation run seen_at'),
|
|
69
161
|
created_at: number(row.created_at, 'automation run created_at'),
|
|
70
162
|
});
|
|
163
|
+
const toolServer = (row) => ({
|
|
164
|
+
...row,
|
|
165
|
+
spec_revision: number(row.spec_revision, 'tool server spec_revision'),
|
|
166
|
+
enabled: number(row.enabled, 'tool server enabled'),
|
|
167
|
+
timeout_ms: number(row.timeout_ms, 'tool server timeout_ms'),
|
|
168
|
+
max_response_bytes: number(row.max_response_bytes, 'tool server max_response_bytes'),
|
|
169
|
+
created_at: number(row.created_at, 'tool server created_at'),
|
|
170
|
+
updated_at: number(row.updated_at, 'tool server updated_at'),
|
|
171
|
+
});
|
|
172
|
+
const toolServerTool = (row) => ({
|
|
173
|
+
...row,
|
|
174
|
+
side_effect: number(row.side_effect, 'tool side_effect'),
|
|
175
|
+
enabled: number(row.enabled, 'tool enabled'),
|
|
176
|
+
created_at: number(row.created_at, 'tool created_at'),
|
|
177
|
+
updated_at: number(row.updated_at, 'tool updated_at'),
|
|
178
|
+
});
|
|
179
|
+
const toolServerCredential = (row) => ({
|
|
180
|
+
...row,
|
|
181
|
+
created_at: number(row.created_at, 'tool credential created_at'),
|
|
182
|
+
updated_at: number(row.updated_at, 'tool credential updated_at'),
|
|
183
|
+
});
|
|
184
|
+
const toolApproval = (row) => ({
|
|
185
|
+
...row,
|
|
186
|
+
created_at: number(row.created_at, 'tool approval created_at'),
|
|
187
|
+
resolved_at: row.resolved_at === null
|
|
188
|
+
? null
|
|
189
|
+
: number(row.resolved_at, 'tool approval resolved_at'),
|
|
190
|
+
expires_at: row.expires_at === null
|
|
191
|
+
? null
|
|
192
|
+
: number(row.expires_at, 'tool approval expires_at'),
|
|
193
|
+
});
|
|
194
|
+
const prompt = (row) => ({
|
|
195
|
+
...row,
|
|
196
|
+
version: number(row.version, 'prompt version'),
|
|
197
|
+
created_at: number(row.created_at, 'prompt created_at'),
|
|
198
|
+
updated_at: number(row.updated_at, 'prompt updated_at'),
|
|
199
|
+
});
|
|
200
|
+
const promptVersion = (row) => ({
|
|
201
|
+
...row,
|
|
202
|
+
version: number(row.version, 'prompt revision version'),
|
|
203
|
+
created_at: number(row.created_at, 'prompt revision created_at'),
|
|
204
|
+
});
|
|
205
|
+
const skill = (row) => ({
|
|
206
|
+
...row,
|
|
207
|
+
enabled: number(row.enabled, 'skill enabled'),
|
|
208
|
+
version: number(row.version, 'skill version'),
|
|
209
|
+
created_at: number(row.created_at, 'skill created_at'),
|
|
210
|
+
updated_at: number(row.updated_at, 'skill updated_at'),
|
|
211
|
+
});
|
|
212
|
+
const skillVersion = (row) => ({
|
|
213
|
+
...row,
|
|
214
|
+
version: number(row.version, 'skill revision version'),
|
|
215
|
+
created_at: number(row.created_at, 'skill revision created_at'),
|
|
216
|
+
});
|
|
217
|
+
const skillFile = (row) => ({
|
|
218
|
+
...row,
|
|
219
|
+
size: number(row.size, 'skill file size'),
|
|
220
|
+
created_at: number(row.created_at, 'skill file created_at'),
|
|
221
|
+
updated_at: number(row.updated_at, 'skill file updated_at'),
|
|
222
|
+
});
|
|
71
223
|
const changes = (rowCount) => rowCount ?? 0;
|
|
72
224
|
const lockOwner = async (client, userId) => {
|
|
73
225
|
const result = await client.query('SELECT id FROM users WHERE id = $1 FOR UPDATE', [userId]);
|
|
@@ -251,6 +403,10 @@ class PostgresKnowledgeCollectionRepository {
|
|
|
251
403
|
ORDER BY lower(name), name`, [userId]);
|
|
252
404
|
return result.rows.map(namedResource);
|
|
253
405
|
}
|
|
406
|
+
async findById(collectionId) {
|
|
407
|
+
const result = await this.database.query('SELECT * FROM knowledge_collections WHERE id = $1', [collectionId]);
|
|
408
|
+
return result.rows[0] ? namedResource(result.rows[0]) : null;
|
|
409
|
+
}
|
|
254
410
|
async replace(collection) {
|
|
255
411
|
await this.database.transaction(async (client) => {
|
|
256
412
|
await lockOwner(client, collection.user_id);
|
|
@@ -282,7 +438,7 @@ class PostgresNoteRepository {
|
|
|
282
438
|
async listByOwner(userId, maximum) {
|
|
283
439
|
const result = await this.database.query(`SELECT * FROM notes
|
|
284
440
|
WHERE user_id = $1
|
|
285
|
-
ORDER BY updated_at DESC
|
|
441
|
+
ORDER BY pinned DESC, updated_at DESC
|
|
286
442
|
LIMIT $2`, [userId, maximum]);
|
|
287
443
|
return result.rows.map(note);
|
|
288
444
|
}
|
|
@@ -303,17 +459,19 @@ class PostgresNoteRepository {
|
|
|
303
459
|
throw new PersistenceResourceLimitError('note', maximum);
|
|
304
460
|
}
|
|
305
461
|
}
|
|
306
|
-
const result = await client.query(`INSERT INTO notes (id, user_id, title, content, created_at, updated_at)
|
|
307
|
-
VALUES ($1, $2, $3, $4, $5, $6)
|
|
462
|
+
const result = await client.query(`INSERT INTO notes (id, user_id, title, content, pinned, created_at, updated_at)
|
|
463
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
308
464
|
ON CONFLICT (id) DO UPDATE SET
|
|
309
465
|
title = EXCLUDED.title,
|
|
310
466
|
content = EXCLUDED.content,
|
|
467
|
+
pinned = EXCLUDED.pinned,
|
|
311
468
|
updated_at = EXCLUDED.updated_at
|
|
312
469
|
WHERE notes.user_id = EXCLUDED.user_id`, [
|
|
313
470
|
value.id,
|
|
314
471
|
value.user_id,
|
|
315
472
|
value.title,
|
|
316
473
|
value.content,
|
|
474
|
+
value.pinned,
|
|
317
475
|
value.created_at,
|
|
318
476
|
value.updated_at,
|
|
319
477
|
]);
|
|
@@ -332,6 +490,8 @@ class PostgresNoteRepository {
|
|
|
332
490
|
assign('title', patch.title);
|
|
333
491
|
if (patch.content !== undefined)
|
|
334
492
|
assign('content', patch.content);
|
|
493
|
+
if (patch.pinned !== undefined)
|
|
494
|
+
assign('pinned', patch.pinned);
|
|
335
495
|
assign('updated_at', patch.updated_at);
|
|
336
496
|
values.push(noteId, userId);
|
|
337
497
|
const result = await this.database.query(`UPDATE notes SET ${assignments.join(', ')}
|
|
@@ -342,6 +502,100 @@ class PostgresNoteRepository {
|
|
|
342
502
|
async deleteByOwner(noteId, userId) {
|
|
343
503
|
return (changes((await this.database.query('DELETE FROM notes WHERE id = $1 AND user_id = $2', [noteId, userId])).rowCount) > 0);
|
|
344
504
|
}
|
|
505
|
+
async findById(noteId) {
|
|
506
|
+
const result = await this.database.query('SELECT * FROM notes WHERE id = $1', [noteId]);
|
|
507
|
+
return result.rows[0] ? note(result.rows[0]) : null;
|
|
508
|
+
}
|
|
509
|
+
async patchById(noteId, patch) {
|
|
510
|
+
const assignments = [];
|
|
511
|
+
const values = [];
|
|
512
|
+
const assign = (column, value) => {
|
|
513
|
+
values.push(value);
|
|
514
|
+
assignments.push(`${column} = $${values.length}`);
|
|
515
|
+
};
|
|
516
|
+
if (patch.title !== undefined)
|
|
517
|
+
assign('title', patch.title);
|
|
518
|
+
if (patch.content !== undefined)
|
|
519
|
+
assign('content', patch.content);
|
|
520
|
+
if (patch.pinned !== undefined)
|
|
521
|
+
assign('pinned', patch.pinned);
|
|
522
|
+
assign('updated_at', patch.updated_at);
|
|
523
|
+
values.push(noteId);
|
|
524
|
+
const result = await this.database.query(`UPDATE notes SET ${assignments.join(', ')}
|
|
525
|
+
WHERE id = $${values.length}
|
|
526
|
+
RETURNING *`, values);
|
|
527
|
+
return result.rows[0] ? note(result.rows[0]) : null;
|
|
528
|
+
}
|
|
529
|
+
async listRevisions(noteId, maximum) {
|
|
530
|
+
const result = await this.database.query(`SELECT * FROM note_revisions
|
|
531
|
+
WHERE note_id = $1
|
|
532
|
+
ORDER BY created_at DESC, id DESC
|
|
533
|
+
LIMIT $2`, [noteId, maximum]);
|
|
534
|
+
return result.rows.map(noteRevision);
|
|
535
|
+
}
|
|
536
|
+
async findRevision(revisionId) {
|
|
537
|
+
const result = await this.database.query('SELECT * FROM note_revisions WHERE id = $1', [revisionId]);
|
|
538
|
+
return result.rows[0] ? noteRevision(result.rows[0]) : null;
|
|
539
|
+
}
|
|
540
|
+
async insertRevision(revision, maximum) {
|
|
541
|
+
await this.database.transaction(async (client) => {
|
|
542
|
+
// Strictly increasing per note so same-millisecond snapshots keep a
|
|
543
|
+
// deterministic order for listing, restore, and pruning.
|
|
544
|
+
await client.query(`INSERT INTO note_revisions (id, note_id, title, content, created_at)
|
|
545
|
+
SELECT $1, $2, $3, $4,
|
|
546
|
+
GREATEST($5::bigint, COALESCE(MAX(created_at), 0) + 1)
|
|
547
|
+
FROM note_revisions WHERE note_id = $2`, [
|
|
548
|
+
revision.id,
|
|
549
|
+
revision.note_id,
|
|
550
|
+
revision.title,
|
|
551
|
+
revision.content,
|
|
552
|
+
revision.created_at,
|
|
553
|
+
]);
|
|
554
|
+
await client.query(`DELETE FROM note_revisions
|
|
555
|
+
WHERE note_id = $1
|
|
556
|
+
AND id NOT IN (
|
|
557
|
+
SELECT id FROM note_revisions
|
|
558
|
+
WHERE note_id = $1
|
|
559
|
+
ORDER BY created_at DESC, id DESC
|
|
560
|
+
LIMIT $2
|
|
561
|
+
)`, [revision.note_id, maximum]);
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
async listAttachments(noteId) {
|
|
565
|
+
const result = await this.database.query(`SELECT * FROM note_attachments
|
|
566
|
+
WHERE note_id = $1
|
|
567
|
+
ORDER BY created_at ASC, id ASC`, [noteId]);
|
|
568
|
+
return result.rows.map(noteAttachment);
|
|
569
|
+
}
|
|
570
|
+
async findAttachment(attachmentId) {
|
|
571
|
+
const result = await this.database.query('SELECT * FROM note_attachments WHERE id = $1', [attachmentId]);
|
|
572
|
+
return result.rows[0] ? noteAttachment(result.rows[0]) : null;
|
|
573
|
+
}
|
|
574
|
+
async insertAttachmentWithLimit(attachment, maximum) {
|
|
575
|
+
await this.database.transaction(async (client) => {
|
|
576
|
+
await client.query('SELECT id FROM notes WHERE id = $1 FOR UPDATE', [
|
|
577
|
+
attachment.note_id,
|
|
578
|
+
]);
|
|
579
|
+
const count = await client.query('SELECT COUNT(*)::text AS count FROM note_attachments WHERE note_id = $1', [attachment.note_id]);
|
|
580
|
+
if (Number(count.rows[0]?.count || 0) >= maximum) {
|
|
581
|
+
throw new PersistenceResourceLimitError('note-attachment', maximum);
|
|
582
|
+
}
|
|
583
|
+
await client.query(`INSERT INTO note_attachments
|
|
584
|
+
(id, note_id, blob_id, filename, content_type, size, created_at)
|
|
585
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)`, [
|
|
586
|
+
attachment.id,
|
|
587
|
+
attachment.note_id,
|
|
588
|
+
attachment.blob_id,
|
|
589
|
+
attachment.filename,
|
|
590
|
+
attachment.content_type,
|
|
591
|
+
attachment.size,
|
|
592
|
+
attachment.created_at,
|
|
593
|
+
]);
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
async deleteAttachment(attachmentId) {
|
|
597
|
+
return (changes((await this.database.query('DELETE FROM note_attachments WHERE id = $1', [attachmentId])).rowCount) > 0);
|
|
598
|
+
}
|
|
345
599
|
}
|
|
346
600
|
class PostgresCalendarEventRepository {
|
|
347
601
|
constructor(database) {
|
|
@@ -361,10 +615,48 @@ class PostgresCalendarEventRepository {
|
|
|
361
615
|
LIMIT $2`, [userId, maximum]);
|
|
362
616
|
return result.rows.map(calendarEvent);
|
|
363
617
|
}
|
|
618
|
+
async listByCalendarsBetween(calendarIds, from, to, maximum) {
|
|
619
|
+
if (calendarIds.length === 0)
|
|
620
|
+
return [];
|
|
621
|
+
const result = await this.database.query(`SELECT * FROM calendar_events
|
|
622
|
+
WHERE calendar_id = ANY($1::text[])
|
|
623
|
+
AND start_at >= $2 AND start_at < $3
|
|
624
|
+
ORDER BY start_at ASC
|
|
625
|
+
LIMIT $4`, [[...calendarIds], from, to, maximum]);
|
|
626
|
+
return result.rows.map(calendarEvent);
|
|
627
|
+
}
|
|
628
|
+
async listRecurringByCalendars(calendarIds, maximum) {
|
|
629
|
+
if (calendarIds.length === 0)
|
|
630
|
+
return [];
|
|
631
|
+
const result = await this.database.query(`SELECT * FROM calendar_events
|
|
632
|
+
WHERE calendar_id = ANY($1::text[])
|
|
633
|
+
AND recurrence IS NOT NULL
|
|
634
|
+
ORDER BY start_at ASC
|
|
635
|
+
LIMIT $2`, [[...calendarIds], maximum]);
|
|
636
|
+
return result.rows.map(calendarEvent);
|
|
637
|
+
}
|
|
364
638
|
async findByOwner(eventId, userId) {
|
|
365
639
|
const result = await this.database.query('SELECT * FROM calendar_events WHERE id = $1 AND user_id = $2', [eventId, userId]);
|
|
366
640
|
return result.rows[0] ? calendarEvent(result.rows[0]) : null;
|
|
367
641
|
}
|
|
642
|
+
async findById(eventId) {
|
|
643
|
+
const result = await this.database.query('SELECT * FROM calendar_events WHERE id = $1', [eventId]);
|
|
644
|
+
return result.rows[0] ? calendarEvent(result.rows[0]) : null;
|
|
645
|
+
}
|
|
646
|
+
async listWithReminders(maximum) {
|
|
647
|
+
const result = await this.database.query(`SELECT * FROM calendar_events
|
|
648
|
+
WHERE reminder_minutes IS NOT NULL
|
|
649
|
+
ORDER BY start_at ASC
|
|
650
|
+
LIMIT $1`, [maximum]);
|
|
651
|
+
return result.rows.map(calendarEvent);
|
|
652
|
+
}
|
|
653
|
+
async markReminded(eventId, occurrenceStart) {
|
|
654
|
+
return (changes((await this.database.query(`UPDATE calendar_events
|
|
655
|
+
SET last_reminded_occurrence = $1
|
|
656
|
+
WHERE id = $2
|
|
657
|
+
AND (last_reminded_occurrence IS NULL
|
|
658
|
+
OR last_reminded_occurrence < $1)`, [occurrenceStart, eventId])).rowCount) > 0);
|
|
659
|
+
}
|
|
368
660
|
async replaceWithLimit(value, maximum) {
|
|
369
661
|
await this.database.transaction(async (client) => {
|
|
370
662
|
await lockOwner(client, value.user_id);
|
|
@@ -380,8 +672,9 @@ class PostgresCalendarEventRepository {
|
|
|
380
672
|
}
|
|
381
673
|
const result = await client.query(`INSERT INTO calendar_events
|
|
382
674
|
(id, user_id, title, notes, start_at, end_at, all_day,
|
|
383
|
-
recurrence,
|
|
384
|
-
|
|
675
|
+
recurrence, calendar_id, reminder_minutes,
|
|
676
|
+
last_reminded_occurrence, created_at, updated_at)
|
|
677
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
|
|
385
678
|
ON CONFLICT (id) DO UPDATE SET
|
|
386
679
|
title = EXCLUDED.title,
|
|
387
680
|
notes = EXCLUDED.notes,
|
|
@@ -389,6 +682,9 @@ class PostgresCalendarEventRepository {
|
|
|
389
682
|
end_at = EXCLUDED.end_at,
|
|
390
683
|
all_day = EXCLUDED.all_day,
|
|
391
684
|
recurrence = EXCLUDED.recurrence,
|
|
685
|
+
calendar_id = EXCLUDED.calendar_id,
|
|
686
|
+
reminder_minutes = EXCLUDED.reminder_minutes,
|
|
687
|
+
last_reminded_occurrence = EXCLUDED.last_reminded_occurrence,
|
|
392
688
|
updated_at = EXCLUDED.updated_at
|
|
393
689
|
WHERE calendar_events.user_id = EXCLUDED.user_id`, [
|
|
394
690
|
value.id,
|
|
@@ -399,6 +695,64 @@ class PostgresCalendarEventRepository {
|
|
|
399
695
|
value.end_at,
|
|
400
696
|
value.all_day,
|
|
401
697
|
value.recurrence,
|
|
698
|
+
value.calendar_id,
|
|
699
|
+
value.reminder_minutes,
|
|
700
|
+
value.last_reminded_occurrence,
|
|
701
|
+
value.created_at,
|
|
702
|
+
value.updated_at,
|
|
703
|
+
]);
|
|
704
|
+
if (result.rowCount !== 1)
|
|
705
|
+
throw new PersistenceResourceConflictError();
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
async deleteByOwner(eventId, userId) {
|
|
709
|
+
return (changes((await this.database.query('DELETE FROM calendar_events WHERE id = $1 AND user_id = $2', [eventId, userId])).rowCount) > 0);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
class PostgresCalendarRepository {
|
|
713
|
+
constructor(database) {
|
|
714
|
+
this.database = database;
|
|
715
|
+
}
|
|
716
|
+
async listByOwner(userId, maximum) {
|
|
717
|
+
const result = await this.database.query(`SELECT * FROM calendars
|
|
718
|
+
WHERE user_id = $1
|
|
719
|
+
ORDER BY created_at ASC
|
|
720
|
+
LIMIT $2`, [userId, maximum]);
|
|
721
|
+
return result.rows.map(calendar);
|
|
722
|
+
}
|
|
723
|
+
async findByOwner(calendarId, userId) {
|
|
724
|
+
const result = await this.database.query('SELECT * FROM calendars WHERE id = $1 AND user_id = $2', [calendarId, userId]);
|
|
725
|
+
return result.rows[0] ? calendar(result.rows[0]) : null;
|
|
726
|
+
}
|
|
727
|
+
async findById(calendarId) {
|
|
728
|
+
const result = await this.database.query('SELECT * FROM calendars WHERE id = $1', [calendarId]);
|
|
729
|
+
return result.rows[0] ? calendar(result.rows[0]) : null;
|
|
730
|
+
}
|
|
731
|
+
async replaceWithLimit(value, maximum) {
|
|
732
|
+
await this.database.transaction(async (client) => {
|
|
733
|
+
await lockOwner(client, value.user_id);
|
|
734
|
+
const existing = await client.query('SELECT user_id FROM calendars WHERE id = $1 FOR UPDATE', [value.id]);
|
|
735
|
+
if (existing.rows[0] && existing.rows[0].user_id !== value.user_id) {
|
|
736
|
+
throw new PersistenceResourceConflictError();
|
|
737
|
+
}
|
|
738
|
+
if (!existing.rows[0]) {
|
|
739
|
+
const count = await client.query('SELECT COUNT(*)::text AS count FROM calendars WHERE user_id = $1', [value.user_id]);
|
|
740
|
+
if (Number(count.rows[0]?.count || 0) >= maximum) {
|
|
741
|
+
throw new PersistenceResourceLimitError('calendar', maximum);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
const result = await client.query(`INSERT INTO calendars
|
|
745
|
+
(id, user_id, name, color, created_at, updated_at)
|
|
746
|
+
VALUES ($1, $2, $3, $4, $5, $6)
|
|
747
|
+
ON CONFLICT (id) DO UPDATE SET
|
|
748
|
+
name = EXCLUDED.name,
|
|
749
|
+
color = EXCLUDED.color,
|
|
750
|
+
updated_at = EXCLUDED.updated_at
|
|
751
|
+
WHERE calendars.user_id = EXCLUDED.user_id`, [
|
|
752
|
+
value.id,
|
|
753
|
+
value.user_id,
|
|
754
|
+
value.name,
|
|
755
|
+
value.color,
|
|
402
756
|
value.created_at,
|
|
403
757
|
value.updated_at,
|
|
404
758
|
]);
|
|
@@ -406,181 +760,1298 @@ class PostgresCalendarEventRepository {
|
|
|
406
760
|
throw new PersistenceResourceConflictError();
|
|
407
761
|
});
|
|
408
762
|
}
|
|
409
|
-
async
|
|
410
|
-
return
|
|
763
|
+
async deleteAndDetach(calendarId, userId) {
|
|
764
|
+
return this.database.transaction(async (client) => {
|
|
765
|
+
const deleted = changes((await client.query('DELETE FROM calendars WHERE id = $1 AND user_id = $2', [calendarId, userId])).rowCount) > 0;
|
|
766
|
+
if (deleted) {
|
|
767
|
+
await client.query(`UPDATE calendar_events
|
|
768
|
+
SET calendar_id = NULL
|
|
769
|
+
WHERE calendar_id = $1 AND user_id = $2`, [calendarId, userId]);
|
|
770
|
+
}
|
|
771
|
+
return deleted;
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
class PostgresChannelRepository {
|
|
776
|
+
constructor(database) {
|
|
777
|
+
this.database = database;
|
|
778
|
+
}
|
|
779
|
+
async insertWithOwner(value, owner, maximumPerUser) {
|
|
780
|
+
await this.database.transaction(async (client) => {
|
|
781
|
+
await lockOwner(client, owner.user_id);
|
|
782
|
+
const count = await client.query('SELECT COUNT(*)::text AS count FROM channels WHERE created_by = $1', [owner.user_id]);
|
|
783
|
+
if (Number(count.rows[0]?.count || 0) >= maximumPerUser) {
|
|
784
|
+
throw new PersistenceResourceLimitError('channel', maximumPerUser);
|
|
785
|
+
}
|
|
786
|
+
await client.query(`INSERT INTO channels
|
|
787
|
+
(id, type, name, description, dm_key, created_by,
|
|
788
|
+
created_at, updated_at, archived_at)
|
|
789
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`, [
|
|
790
|
+
value.id,
|
|
791
|
+
value.type,
|
|
792
|
+
value.name,
|
|
793
|
+
value.description,
|
|
794
|
+
value.dm_key,
|
|
795
|
+
value.created_by,
|
|
796
|
+
value.created_at,
|
|
797
|
+
value.updated_at,
|
|
798
|
+
value.archived_at,
|
|
799
|
+
]);
|
|
800
|
+
await client.query(`INSERT INTO channel_members
|
|
801
|
+
(channel_id, user_id, role, joined_at, last_read_at)
|
|
802
|
+
VALUES ($1, $2, $3, $4, $5)`, [
|
|
803
|
+
owner.channel_id,
|
|
804
|
+
owner.user_id,
|
|
805
|
+
owner.role,
|
|
806
|
+
owner.joined_at,
|
|
807
|
+
owner.last_read_at,
|
|
808
|
+
]);
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
async findById(channelId) {
|
|
812
|
+
const result = await this.database.query('SELECT * FROM channels WHERE id = $1', [channelId]);
|
|
813
|
+
return result.rows[0] ? channel(result.rows[0]) : null;
|
|
814
|
+
}
|
|
815
|
+
async findByDmKey(dmKey) {
|
|
816
|
+
const result = await this.database.query('SELECT * FROM channels WHERE dm_key = $1', [dmKey]);
|
|
817
|
+
return result.rows[0] ? channel(result.rows[0]) : null;
|
|
818
|
+
}
|
|
819
|
+
async listForUser(userId, maximum) {
|
|
820
|
+
const result = await this.database.query(`SELECT
|
|
821
|
+
c.id, c.type, c.name, c.description, c.dm_key, c.created_by,
|
|
822
|
+
c.created_at, c.updated_at, c.archived_at,
|
|
823
|
+
m.channel_id AS member_channel_id, m.user_id AS member_user_id,
|
|
824
|
+
m.role AS member_role, m.joined_at AS member_joined_at,
|
|
825
|
+
m.last_read_at AS member_last_read_at
|
|
826
|
+
FROM channel_members m
|
|
827
|
+
JOIN channels c ON c.id = m.channel_id
|
|
828
|
+
WHERE m.user_id = $1
|
|
829
|
+
ORDER BY c.updated_at DESC
|
|
830
|
+
LIMIT $2`, [userId, maximum]);
|
|
831
|
+
return result.rows.map(row => ({
|
|
832
|
+
channel: channel(row),
|
|
833
|
+
member: channelMember({
|
|
834
|
+
channel_id: row.member_channel_id,
|
|
835
|
+
user_id: row.member_user_id,
|
|
836
|
+
role: row.member_role,
|
|
837
|
+
joined_at: row.member_joined_at,
|
|
838
|
+
last_read_at: row.member_last_read_at,
|
|
839
|
+
}),
|
|
840
|
+
}));
|
|
841
|
+
}
|
|
842
|
+
async listPublic(maximum) {
|
|
843
|
+
const result = await this.database.query(`SELECT * FROM channels
|
|
844
|
+
WHERE type = 'public' AND archived_at IS NULL
|
|
845
|
+
ORDER BY updated_at DESC
|
|
846
|
+
LIMIT $1`, [maximum]);
|
|
847
|
+
return result.rows.map(channel);
|
|
848
|
+
}
|
|
849
|
+
async update(value) {
|
|
850
|
+
await this.database.query(`UPDATE channels SET
|
|
851
|
+
name = $1,
|
|
852
|
+
description = $2,
|
|
853
|
+
updated_at = $3,
|
|
854
|
+
archived_at = $4
|
|
855
|
+
WHERE id = $5`, [
|
|
856
|
+
value.name,
|
|
857
|
+
value.description,
|
|
858
|
+
value.updated_at,
|
|
859
|
+
value.archived_at,
|
|
860
|
+
value.id,
|
|
861
|
+
]);
|
|
862
|
+
}
|
|
863
|
+
async delete(channelId) {
|
|
864
|
+
return (changes((await this.database.query('DELETE FROM channels WHERE id = $1', [
|
|
865
|
+
channelId,
|
|
866
|
+
])).rowCount) > 0);
|
|
867
|
+
}
|
|
868
|
+
async upsertMember(member, maximumMembers) {
|
|
869
|
+
await this.database.transaction(async (client) => {
|
|
870
|
+
const existing = await client.query(`SELECT 1 FROM channel_members
|
|
871
|
+
WHERE channel_id = $1 AND user_id = $2
|
|
872
|
+
FOR UPDATE`, [member.channel_id, member.user_id]);
|
|
873
|
+
if (!existing.rows[0]) {
|
|
874
|
+
const count = await client.query(`SELECT COUNT(*)::text AS count FROM channel_members
|
|
875
|
+
WHERE channel_id = $1`, [member.channel_id]);
|
|
876
|
+
if (Number(count.rows[0]?.count || 0) >= maximumMembers) {
|
|
877
|
+
throw new PersistenceResourceLimitError('channel-member', maximumMembers);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
await client.query(`INSERT INTO channel_members
|
|
881
|
+
(channel_id, user_id, role, joined_at, last_read_at)
|
|
882
|
+
VALUES ($1, $2, $3, $4, $5)
|
|
883
|
+
ON CONFLICT (channel_id, user_id) DO UPDATE SET
|
|
884
|
+
role = EXCLUDED.role`, [
|
|
885
|
+
member.channel_id,
|
|
886
|
+
member.user_id,
|
|
887
|
+
member.role,
|
|
888
|
+
member.joined_at,
|
|
889
|
+
member.last_read_at,
|
|
890
|
+
]);
|
|
891
|
+
});
|
|
892
|
+
}
|
|
893
|
+
async findMember(channelId, userId) {
|
|
894
|
+
const result = await this.database.query('SELECT * FROM channel_members WHERE channel_id = $1 AND user_id = $2', [channelId, userId]);
|
|
895
|
+
return result.rows[0] ? channelMember(result.rows[0]) : null;
|
|
896
|
+
}
|
|
897
|
+
async listMembers(channelId, maximum) {
|
|
898
|
+
const result = await this.database.query(`SELECT * FROM channel_members
|
|
899
|
+
WHERE channel_id = $1
|
|
900
|
+
ORDER BY joined_at ASC
|
|
901
|
+
LIMIT $2`, [channelId, maximum]);
|
|
902
|
+
return result.rows.map(channelMember);
|
|
903
|
+
}
|
|
904
|
+
async removeMember(channelId, userId) {
|
|
905
|
+
return (changes((await this.database.query('DELETE FROM channel_members WHERE channel_id = $1 AND user_id = $2', [channelId, userId])).rowCount) > 0);
|
|
906
|
+
}
|
|
907
|
+
async advanceLastRead(channelId, userId, lastReadAt) {
|
|
908
|
+
return (changes((await this.database.query(`UPDATE channel_members
|
|
909
|
+
SET last_read_at = $1
|
|
910
|
+
WHERE channel_id = $2 AND user_id = $3 AND last_read_at < $1`, [lastReadAt, channelId, userId])).rowCount) > 0);
|
|
911
|
+
}
|
|
912
|
+
async unreadSummaryForUser(userId) {
|
|
913
|
+
const result = await this.database.query(`SELECT
|
|
914
|
+
m.channel_id,
|
|
915
|
+
COUNT(msg.id)::text AS unread_count,
|
|
916
|
+
MAX(msg.created_at) AS latest_message_at
|
|
917
|
+
FROM channel_members m
|
|
918
|
+
LEFT JOIN channel_messages msg
|
|
919
|
+
ON msg.channel_id = m.channel_id
|
|
920
|
+
AND msg.created_at > m.last_read_at
|
|
921
|
+
AND msg.deleted_at IS NULL
|
|
922
|
+
AND (msg.user_id IS NULL OR msg.user_id != m.user_id
|
|
923
|
+
OR msg.author_kind != 'user')
|
|
924
|
+
WHERE m.user_id = $1
|
|
925
|
+
GROUP BY m.channel_id`, [userId]);
|
|
926
|
+
return result.rows.map(row => ({
|
|
927
|
+
channel_id: row.channel_id,
|
|
928
|
+
unread_count: number(row.unread_count, 'channel unread_count'),
|
|
929
|
+
latest_message_at: row.latest_message_at === null
|
|
930
|
+
? null
|
|
931
|
+
: number(row.latest_message_at, 'channel latest_message_at'),
|
|
932
|
+
}));
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
class PostgresChannelMessageRepository {
|
|
936
|
+
constructor(database) {
|
|
937
|
+
this.database = database;
|
|
938
|
+
}
|
|
939
|
+
async insertIfAbsent(message, attachments, maximumPerChannel) {
|
|
940
|
+
return this.database.transaction(async (client) => {
|
|
941
|
+
const existing = await client.query('SELECT * FROM channel_messages WHERE id = $1', [message.id]);
|
|
942
|
+
if (existing.rows[0]) {
|
|
943
|
+
return { stored: channelMessage(existing.rows[0]), inserted: false };
|
|
944
|
+
}
|
|
945
|
+
const count = await client.query(`SELECT COUNT(*)::text AS count FROM channel_messages
|
|
946
|
+
WHERE channel_id = $1`, [message.channel_id]);
|
|
947
|
+
if (Number(count.rows[0]?.count || 0) >= maximumPerChannel) {
|
|
948
|
+
throw new PersistenceResourceLimitError('channel-message', maximumPerChannel);
|
|
949
|
+
}
|
|
950
|
+
await client.query(`INSERT INTO channel_messages
|
|
951
|
+
(id, channel_id, user_id, parent_id, author_kind, model, content,
|
|
952
|
+
metadata, created_at, updated_at, edited_at, deleted_at,
|
|
953
|
+
pinned_at, pinned_by)
|
|
954
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)`, [
|
|
955
|
+
message.id,
|
|
956
|
+
message.channel_id,
|
|
957
|
+
message.user_id,
|
|
958
|
+
message.parent_id,
|
|
959
|
+
message.author_kind,
|
|
960
|
+
message.model,
|
|
961
|
+
message.content,
|
|
962
|
+
message.metadata,
|
|
963
|
+
message.created_at,
|
|
964
|
+
message.updated_at,
|
|
965
|
+
message.edited_at,
|
|
966
|
+
message.deleted_at,
|
|
967
|
+
message.pinned_at,
|
|
968
|
+
message.pinned_by,
|
|
969
|
+
]);
|
|
970
|
+
for (const attachment of attachments) {
|
|
971
|
+
if (attachment.message_id !== message.id) {
|
|
972
|
+
throw new Error('A channel attachment does not belong to its message');
|
|
973
|
+
}
|
|
974
|
+
await client.query(`INSERT INTO channel_attachments
|
|
975
|
+
(id, message_id, channel_id, blob_id, filename, content_type,
|
|
976
|
+
size, created_by, created_at)
|
|
977
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`, [
|
|
978
|
+
attachment.id,
|
|
979
|
+
attachment.message_id,
|
|
980
|
+
attachment.channel_id,
|
|
981
|
+
attachment.blob_id,
|
|
982
|
+
attachment.filename,
|
|
983
|
+
attachment.content_type,
|
|
984
|
+
attachment.size,
|
|
985
|
+
attachment.created_by,
|
|
986
|
+
attachment.created_at,
|
|
987
|
+
]);
|
|
988
|
+
}
|
|
989
|
+
await client.query('UPDATE channels SET updated_at = $1 WHERE id = $2', [
|
|
990
|
+
message.created_at,
|
|
991
|
+
message.channel_id,
|
|
992
|
+
]);
|
|
993
|
+
return { stored: message, inserted: true };
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
async findById(messageId) {
|
|
997
|
+
const result = await this.database.query('SELECT * FROM channel_messages WHERE id = $1', [messageId]);
|
|
998
|
+
return result.rows[0] ? channelMessage(result.rows[0]) : null;
|
|
999
|
+
}
|
|
1000
|
+
async listPage(channelId, options) {
|
|
1001
|
+
const clauses = ['channel_id = $1'];
|
|
1002
|
+
const parameters = [channelId];
|
|
1003
|
+
if (options.parentId === null) {
|
|
1004
|
+
clauses.push('parent_id IS NULL');
|
|
1005
|
+
}
|
|
1006
|
+
else if (options.parentId !== undefined) {
|
|
1007
|
+
parameters.push(options.parentId);
|
|
1008
|
+
clauses.push(`parent_id = $${parameters.length}`);
|
|
1009
|
+
}
|
|
1010
|
+
if (options.before) {
|
|
1011
|
+
parameters.push(options.before.created_at);
|
|
1012
|
+
const createdIndex = parameters.length;
|
|
1013
|
+
parameters.push(options.before.id);
|
|
1014
|
+
clauses.push(`(created_at < $${createdIndex} OR ` +
|
|
1015
|
+
`(created_at = $${createdIndex} AND id < $${parameters.length}))`);
|
|
1016
|
+
}
|
|
1017
|
+
if (options.after) {
|
|
1018
|
+
parameters.push(options.after.created_at);
|
|
1019
|
+
const createdIndex = parameters.length;
|
|
1020
|
+
parameters.push(options.after.id);
|
|
1021
|
+
clauses.push(`(created_at > $${createdIndex} OR ` +
|
|
1022
|
+
`(created_at = $${createdIndex} AND id > $${parameters.length}))`);
|
|
1023
|
+
}
|
|
1024
|
+
const direction = options.after ? 'ASC' : 'DESC';
|
|
1025
|
+
parameters.push(options.limit);
|
|
1026
|
+
const result = await this.database.query(`SELECT * FROM channel_messages
|
|
1027
|
+
WHERE ${clauses.join(' AND ')}
|
|
1028
|
+
ORDER BY created_at ${direction}, id ${direction}
|
|
1029
|
+
LIMIT $${parameters.length}`, parameters);
|
|
1030
|
+
return result.rows.map(channelMessage);
|
|
1031
|
+
}
|
|
1032
|
+
async listThread(parentId, maximum) {
|
|
1033
|
+
const result = await this.database.query(`SELECT * FROM channel_messages
|
|
1034
|
+
WHERE parent_id = $1
|
|
1035
|
+
ORDER BY created_at ASC, id ASC
|
|
1036
|
+
LIMIT $2`, [parentId, maximum]);
|
|
1037
|
+
return result.rows.map(channelMessage);
|
|
1038
|
+
}
|
|
1039
|
+
async countThreadReplies(parentIds) {
|
|
1040
|
+
if (parentIds.length === 0)
|
|
1041
|
+
return {};
|
|
1042
|
+
const result = await this.database.query(`SELECT parent_id, COUNT(*)::text AS count
|
|
1043
|
+
FROM channel_messages
|
|
1044
|
+
WHERE parent_id = ANY($1::text[]) AND deleted_at IS NULL
|
|
1045
|
+
GROUP BY parent_id`, [[...parentIds]]);
|
|
1046
|
+
const counts = {};
|
|
1047
|
+
for (const row of result.rows) {
|
|
1048
|
+
counts[row.parent_id] = number(row.count, 'channel thread reply count');
|
|
1049
|
+
}
|
|
1050
|
+
return counts;
|
|
1051
|
+
}
|
|
1052
|
+
async update(message) {
|
|
1053
|
+
await this.database.query(`UPDATE channel_messages SET
|
|
1054
|
+
content = $1,
|
|
1055
|
+
metadata = $2,
|
|
1056
|
+
updated_at = $3,
|
|
1057
|
+
edited_at = $4,
|
|
1058
|
+
deleted_at = $5,
|
|
1059
|
+
pinned_at = $6,
|
|
1060
|
+
pinned_by = $7
|
|
1061
|
+
WHERE id = $8`, [
|
|
1062
|
+
message.content,
|
|
1063
|
+
message.metadata,
|
|
1064
|
+
message.updated_at,
|
|
1065
|
+
message.edited_at,
|
|
1066
|
+
message.deleted_at,
|
|
1067
|
+
message.pinned_at,
|
|
1068
|
+
message.pinned_by,
|
|
1069
|
+
message.id,
|
|
1070
|
+
]);
|
|
1071
|
+
}
|
|
1072
|
+
async listPinned(channelId, maximum) {
|
|
1073
|
+
const result = await this.database.query(`SELECT * FROM channel_messages
|
|
1074
|
+
WHERE channel_id = $1 AND pinned_at IS NOT NULL
|
|
1075
|
+
AND deleted_at IS NULL
|
|
1076
|
+
ORDER BY pinned_at DESC
|
|
1077
|
+
LIMIT $2`, [channelId, maximum]);
|
|
1078
|
+
return result.rows.map(channelMessage);
|
|
1079
|
+
}
|
|
1080
|
+
async listRecentForChannels(channelIds, maximum) {
|
|
1081
|
+
if (channelIds.length === 0)
|
|
1082
|
+
return [];
|
|
1083
|
+
const result = await this.database.query(`SELECT * FROM channel_messages
|
|
1084
|
+
WHERE channel_id = ANY($1::text[]) AND deleted_at IS NULL
|
|
1085
|
+
ORDER BY created_at DESC
|
|
1086
|
+
LIMIT $2`, [[...channelIds], maximum]);
|
|
1087
|
+
return result.rows.map(channelMessage);
|
|
1088
|
+
}
|
|
1089
|
+
async addReaction(reaction, maximumPerMessage) {
|
|
1090
|
+
return this.database.transaction(async (client) => {
|
|
1091
|
+
const existing = await client.query(`SELECT 1 FROM channel_reactions
|
|
1092
|
+
WHERE message_id = $1 AND user_id = $2 AND emoji = $3`, [reaction.message_id, reaction.user_id, reaction.emoji]);
|
|
1093
|
+
if (existing.rows[0])
|
|
1094
|
+
return false;
|
|
1095
|
+
const count = await client.query(`SELECT COUNT(*)::text AS count FROM channel_reactions
|
|
1096
|
+
WHERE message_id = $1`, [reaction.message_id]);
|
|
1097
|
+
if (Number(count.rows[0]?.count || 0) >= maximumPerMessage) {
|
|
1098
|
+
throw new PersistenceResourceLimitError('channel-reaction', maximumPerMessage);
|
|
1099
|
+
}
|
|
1100
|
+
const result = await client.query(`INSERT INTO channel_reactions
|
|
1101
|
+
(id, message_id, user_id, emoji, created_at)
|
|
1102
|
+
VALUES ($1, $2, $3, $4, $5)
|
|
1103
|
+
ON CONFLICT (message_id, user_id, emoji) DO NOTHING`, [
|
|
1104
|
+
reaction.id,
|
|
1105
|
+
reaction.message_id,
|
|
1106
|
+
reaction.user_id,
|
|
1107
|
+
reaction.emoji,
|
|
1108
|
+
reaction.created_at,
|
|
1109
|
+
]);
|
|
1110
|
+
return changes(result.rowCount) > 0;
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
async removeReaction(messageId, userId, emoji) {
|
|
1114
|
+
return (changes((await this.database.query(`DELETE FROM channel_reactions
|
|
1115
|
+
WHERE message_id = $1 AND user_id = $2 AND emoji = $3`, [messageId, userId, emoji])).rowCount) > 0);
|
|
1116
|
+
}
|
|
1117
|
+
async listReactionsForMessages(messageIds) {
|
|
1118
|
+
if (messageIds.length === 0)
|
|
1119
|
+
return [];
|
|
1120
|
+
const result = await this.database.query(`SELECT * FROM channel_reactions
|
|
1121
|
+
WHERE message_id = ANY($1::text[])
|
|
1122
|
+
ORDER BY created_at ASC`, [[...messageIds]]);
|
|
1123
|
+
return result.rows.map(channelReaction);
|
|
1124
|
+
}
|
|
1125
|
+
async findAttachment(attachmentId) {
|
|
1126
|
+
const result = await this.database.query('SELECT * FROM channel_attachments WHERE id = $1', [attachmentId]);
|
|
1127
|
+
return result.rows[0] ? channelAttachment(result.rows[0]) : null;
|
|
1128
|
+
}
|
|
1129
|
+
async listAttachmentsForMessages(messageIds) {
|
|
1130
|
+
if (messageIds.length === 0)
|
|
1131
|
+
return [];
|
|
1132
|
+
const result = await this.database.query(`SELECT * FROM channel_attachments
|
|
1133
|
+
WHERE message_id = ANY($1::text[])
|
|
1134
|
+
ORDER BY created_at ASC`, [[...messageIds]]);
|
|
1135
|
+
return result.rows.map(channelAttachment);
|
|
1136
|
+
}
|
|
1137
|
+
async listAttachmentBlobIds(channelId) {
|
|
1138
|
+
const result = await this.database.query('SELECT blob_id, created_by FROM channel_attachments WHERE channel_id = $1', [channelId]);
|
|
1139
|
+
return result.rows;
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
class PostgresNotificationRepository {
|
|
1143
|
+
constructor(database) {
|
|
1144
|
+
this.database = database;
|
|
1145
|
+
}
|
|
1146
|
+
async insertWithLimit(value, maximumPerUser) {
|
|
1147
|
+
return this.database.transaction(async (client) => {
|
|
1148
|
+
await lockOwner(client, value.user_id);
|
|
1149
|
+
const result = await client.query(`INSERT INTO notifications
|
|
1150
|
+
(id, user_id, type, title, body, href, source_key,
|
|
1151
|
+
created_at, read_at)
|
|
1152
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
|
1153
|
+
ON CONFLICT (user_id, source_key) DO NOTHING`, [
|
|
1154
|
+
value.id,
|
|
1155
|
+
value.user_id,
|
|
1156
|
+
value.type,
|
|
1157
|
+
value.title,
|
|
1158
|
+
value.body,
|
|
1159
|
+
value.href,
|
|
1160
|
+
value.source_key,
|
|
1161
|
+
value.created_at,
|
|
1162
|
+
value.read_at,
|
|
1163
|
+
]);
|
|
1164
|
+
if (changes(result.rowCount) === 0)
|
|
1165
|
+
return false;
|
|
1166
|
+
await client.query(`DELETE FROM notifications
|
|
1167
|
+
WHERE user_id = $1
|
|
1168
|
+
AND id NOT IN (
|
|
1169
|
+
SELECT id FROM notifications
|
|
1170
|
+
WHERE user_id = $1
|
|
1171
|
+
ORDER BY created_at DESC, id DESC
|
|
1172
|
+
LIMIT $2
|
|
1173
|
+
)`, [value.user_id, maximumPerUser]);
|
|
1174
|
+
return true;
|
|
1175
|
+
});
|
|
1176
|
+
}
|
|
1177
|
+
async listByOwner(userId, options) {
|
|
1178
|
+
const clauses = ['user_id = $1'];
|
|
1179
|
+
const parameters = [userId];
|
|
1180
|
+
if (options.before !== undefined) {
|
|
1181
|
+
parameters.push(options.before);
|
|
1182
|
+
clauses.push(`created_at < $${parameters.length}`);
|
|
1183
|
+
}
|
|
1184
|
+
if (options.unreadOnly) {
|
|
1185
|
+
clauses.push('read_at IS NULL');
|
|
1186
|
+
}
|
|
1187
|
+
parameters.push(options.limit);
|
|
1188
|
+
const result = await this.database.query(`SELECT * FROM notifications
|
|
1189
|
+
WHERE ${clauses.join(' AND ')}
|
|
1190
|
+
ORDER BY created_at DESC, id DESC
|
|
1191
|
+
LIMIT $${parameters.length}`, parameters);
|
|
1192
|
+
return result.rows.map(notification);
|
|
1193
|
+
}
|
|
1194
|
+
async countUnread(userId) {
|
|
1195
|
+
const result = await this.database.query(`SELECT COUNT(*)::text AS count FROM notifications
|
|
1196
|
+
WHERE user_id = $1 AND read_at IS NULL`, [userId]);
|
|
1197
|
+
return Number(result.rows[0]?.count || 0);
|
|
1198
|
+
}
|
|
1199
|
+
async markRead(notificationId, userId, readAt) {
|
|
1200
|
+
return (changes((await this.database.query(`UPDATE notifications SET read_at = $1
|
|
1201
|
+
WHERE id = $2 AND user_id = $3 AND read_at IS NULL`, [readAt, notificationId, userId])).rowCount) > 0);
|
|
1202
|
+
}
|
|
1203
|
+
async markAllRead(userId, readAt) {
|
|
1204
|
+
return changes((await this.database.query(`UPDATE notifications SET read_at = $1
|
|
1205
|
+
WHERE user_id = $2 AND read_at IS NULL`, [readAt, userId])).rowCount);
|
|
1206
|
+
}
|
|
1207
|
+
async deleteByOwner(notificationId, userId) {
|
|
1208
|
+
return (changes((await this.database.query('DELETE FROM notifications WHERE id = $1 AND user_id = $2', [notificationId, userId])).rowCount) > 0);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
class PostgresWebhookTargetRepository {
|
|
1212
|
+
constructor(database) {
|
|
1213
|
+
this.database = database;
|
|
1214
|
+
}
|
|
1215
|
+
async list(maximum) {
|
|
1216
|
+
const result = await this.database.query('SELECT * FROM webhook_targets ORDER BY created_at ASC LIMIT $1', [maximum]);
|
|
1217
|
+
return result.rows.map(webhookTarget);
|
|
1218
|
+
}
|
|
1219
|
+
async findById(targetId) {
|
|
1220
|
+
const result = await this.database.query('SELECT * FROM webhook_targets WHERE id = $1', [targetId]);
|
|
1221
|
+
return result.rows[0] ? webhookTarget(result.rows[0]) : null;
|
|
1222
|
+
}
|
|
1223
|
+
async replaceWithLimit(value, maximum) {
|
|
1224
|
+
await this.database.transaction(async (client) => {
|
|
1225
|
+
const existing = await client.query('SELECT 1 FROM webhook_targets WHERE id = $1 FOR UPDATE', [value.id]);
|
|
1226
|
+
if (!existing.rows[0]) {
|
|
1227
|
+
const count = await client.query('SELECT COUNT(*)::text AS count FROM webhook_targets');
|
|
1228
|
+
if (Number(count.rows[0]?.count || 0) >= maximum) {
|
|
1229
|
+
throw new PersistenceResourceLimitError('webhook-target', maximum);
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
await client.query(`INSERT INTO webhook_targets
|
|
1233
|
+
(id, name, url, secret, events, enabled, created_by,
|
|
1234
|
+
created_at, updated_at)
|
|
1235
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
|
1236
|
+
ON CONFLICT (id) DO UPDATE SET
|
|
1237
|
+
name = EXCLUDED.name,
|
|
1238
|
+
url = EXCLUDED.url,
|
|
1239
|
+
secret = EXCLUDED.secret,
|
|
1240
|
+
events = EXCLUDED.events,
|
|
1241
|
+
enabled = EXCLUDED.enabled,
|
|
1242
|
+
updated_at = EXCLUDED.updated_at`, [
|
|
1243
|
+
value.id,
|
|
1244
|
+
value.name,
|
|
1245
|
+
value.url,
|
|
1246
|
+
value.secret,
|
|
1247
|
+
value.events,
|
|
1248
|
+
value.enabled,
|
|
1249
|
+
value.created_by,
|
|
1250
|
+
value.created_at,
|
|
1251
|
+
value.updated_at,
|
|
1252
|
+
]);
|
|
1253
|
+
});
|
|
1254
|
+
}
|
|
1255
|
+
async delete(targetId) {
|
|
1256
|
+
return (changes((await this.database.query('DELETE FROM webhook_targets WHERE id = $1', [targetId])).rowCount) > 0);
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
class PostgresPushSubscriptionRepository {
|
|
1260
|
+
constructor(database) {
|
|
1261
|
+
this.database = database;
|
|
1262
|
+
}
|
|
1263
|
+
async upsertByEndpoint(record) {
|
|
1264
|
+
await this.database.query(`INSERT INTO push_subscriptions
|
|
1265
|
+
(id, user_id, session_id, endpoint_lookup, subscription,
|
|
1266
|
+
user_agent, created_at, last_used_at)
|
|
1267
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
|
1268
|
+
ON CONFLICT (endpoint_lookup) DO UPDATE SET
|
|
1269
|
+
user_id = excluded.user_id,
|
|
1270
|
+
session_id = excluded.session_id,
|
|
1271
|
+
subscription = excluded.subscription,
|
|
1272
|
+
user_agent = excluded.user_agent,
|
|
1273
|
+
last_used_at = excluded.last_used_at`, [
|
|
1274
|
+
record.id,
|
|
1275
|
+
record.user_id,
|
|
1276
|
+
record.session_id,
|
|
1277
|
+
record.endpoint_lookup,
|
|
1278
|
+
record.subscription,
|
|
1279
|
+
record.user_agent,
|
|
1280
|
+
record.created_at,
|
|
1281
|
+
record.last_used_at,
|
|
1282
|
+
]);
|
|
1283
|
+
}
|
|
1284
|
+
async listByUser(userId) {
|
|
1285
|
+
const result = await this.database.query(`SELECT * FROM push_subscriptions
|
|
1286
|
+
WHERE user_id = $1
|
|
1287
|
+
ORDER BY created_at ASC`, [userId]);
|
|
1288
|
+
return result.rows.map(pushSubscription);
|
|
1289
|
+
}
|
|
1290
|
+
async findByLookup(endpointLookup) {
|
|
1291
|
+
const result = await this.database.query('SELECT * FROM push_subscriptions WHERE endpoint_lookup = $1', [endpointLookup]);
|
|
1292
|
+
return result.rows[0] ? pushSubscription(result.rows[0]) : null;
|
|
1293
|
+
}
|
|
1294
|
+
async findById(id) {
|
|
1295
|
+
const result = await this.database.query('SELECT * FROM push_subscriptions WHERE id = $1', [id]);
|
|
1296
|
+
return result.rows[0] ? pushSubscription(result.rows[0]) : null;
|
|
1297
|
+
}
|
|
1298
|
+
async touch(id, lastUsedAt) {
|
|
1299
|
+
const result = await this.database.query('UPDATE push_subscriptions SET last_used_at = $1 WHERE id = $2', [lastUsedAt, id]);
|
|
1300
|
+
return changes(result.rowCount) > 0;
|
|
1301
|
+
}
|
|
1302
|
+
async delete(id) {
|
|
1303
|
+
const result = await this.database.query('DELETE FROM push_subscriptions WHERE id = $1', [id]);
|
|
1304
|
+
return changes(result.rowCount) > 0;
|
|
1305
|
+
}
|
|
1306
|
+
async deleteByLookup(userId, endpointLookup) {
|
|
1307
|
+
const result = await this.database.query(`DELETE FROM push_subscriptions
|
|
1308
|
+
WHERE user_id = $1 AND endpoint_lookup = $2`, [userId, endpointLookup]);
|
|
1309
|
+
return changes(result.rowCount) > 0;
|
|
1310
|
+
}
|
|
1311
|
+
async deleteForSession(sessionId) {
|
|
1312
|
+
const result = await this.database.query('DELETE FROM push_subscriptions WHERE session_id = $1', [sessionId]);
|
|
1313
|
+
return changes(result.rowCount);
|
|
1314
|
+
}
|
|
1315
|
+
async deleteForUser(userId) {
|
|
1316
|
+
const result = await this.database.query('DELETE FROM push_subscriptions WHERE user_id = $1', [userId]);
|
|
1317
|
+
return changes(result.rowCount);
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
class PostgresRecoveryDrillRepository {
|
|
1321
|
+
constructor(database) {
|
|
1322
|
+
this.database = database;
|
|
1323
|
+
}
|
|
1324
|
+
async insert(record) {
|
|
1325
|
+
await this.database.query(`INSERT INTO recovery_drills
|
|
1326
|
+
(id, status, origin, started_at, finished_at, snapshot_bytes,
|
|
1327
|
+
rpo_seconds, restore_ms, error, report, created_by, created_at)
|
|
1328
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)`, [
|
|
1329
|
+
record.id,
|
|
1330
|
+
record.status,
|
|
1331
|
+
record.origin,
|
|
1332
|
+
record.started_at,
|
|
1333
|
+
record.finished_at,
|
|
1334
|
+
record.snapshot_bytes,
|
|
1335
|
+
record.rpo_seconds,
|
|
1336
|
+
record.restore_ms,
|
|
1337
|
+
record.error,
|
|
1338
|
+
record.report,
|
|
1339
|
+
record.created_by,
|
|
1340
|
+
record.created_at,
|
|
1341
|
+
]);
|
|
1342
|
+
}
|
|
1343
|
+
async update(id, patch) {
|
|
1344
|
+
const assignments = [];
|
|
1345
|
+
const parameters = [];
|
|
1346
|
+
for (const [column, value] of Object.entries(patch)) {
|
|
1347
|
+
if (value === undefined)
|
|
1348
|
+
continue;
|
|
1349
|
+
parameters.push(value);
|
|
1350
|
+
assignments.push(`${column} = $${parameters.length}`);
|
|
1351
|
+
}
|
|
1352
|
+
if (assignments.length === 0)
|
|
1353
|
+
return false;
|
|
1354
|
+
parameters.push(id);
|
|
1355
|
+
const result = await this.database.query(`UPDATE recovery_drills SET ${assignments.join(', ')}
|
|
1356
|
+
WHERE id = $${parameters.length}`, parameters);
|
|
1357
|
+
return changes(result.rowCount) > 0;
|
|
1358
|
+
}
|
|
1359
|
+
async findById(id) {
|
|
1360
|
+
const result = await this.database.query('SELECT * FROM recovery_drills WHERE id = $1', [id]);
|
|
1361
|
+
return result.rows[0] ? recoveryDrill(result.rows[0]) : null;
|
|
1362
|
+
}
|
|
1363
|
+
async list(limit) {
|
|
1364
|
+
const result = await this.database.query(`SELECT * FROM recovery_drills
|
|
1365
|
+
ORDER BY started_at DESC, id DESC
|
|
1366
|
+
LIMIT $1`, [limit]);
|
|
1367
|
+
return result.rows.map(recoveryDrill);
|
|
1368
|
+
}
|
|
1369
|
+
async findLatestFinished() {
|
|
1370
|
+
const result = await this.database.query(`SELECT * FROM recovery_drills
|
|
1371
|
+
WHERE status != 'running'
|
|
1372
|
+
ORDER BY started_at DESC, id DESC
|
|
1373
|
+
LIMIT 1`);
|
|
1374
|
+
return result.rows[0] ? recoveryDrill(result.rows[0]) : null;
|
|
1375
|
+
}
|
|
1376
|
+
async findRunning() {
|
|
1377
|
+
const result = await this.database.query(`SELECT * FROM recovery_drills
|
|
1378
|
+
WHERE status = 'running'
|
|
1379
|
+
ORDER BY started_at DESC, id DESC
|
|
1380
|
+
LIMIT 1`);
|
|
1381
|
+
return result.rows[0] ? recoveryDrill(result.rows[0]) : null;
|
|
1382
|
+
}
|
|
1383
|
+
async pruneToLimit(limit) {
|
|
1384
|
+
const result = await this.database.query(`DELETE FROM recovery_drills
|
|
1385
|
+
WHERE status != 'running'
|
|
1386
|
+
AND id NOT IN (
|
|
1387
|
+
SELECT id FROM recovery_drills
|
|
1388
|
+
WHERE status != 'running'
|
|
1389
|
+
ORDER BY started_at DESC, id DESC
|
|
1390
|
+
LIMIT $1
|
|
1391
|
+
)`, [limit]);
|
|
1392
|
+
return changes(result.rowCount);
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
class PostgresAutomationRepository {
|
|
1396
|
+
constructor(database) {
|
|
1397
|
+
this.database = database;
|
|
1398
|
+
}
|
|
1399
|
+
async listByOwner(userId, maximum) {
|
|
1400
|
+
const result = await this.database.query(`SELECT * FROM automations
|
|
1401
|
+
WHERE user_id = $1
|
|
1402
|
+
ORDER BY updated_at DESC
|
|
1403
|
+
LIMIT $2`, [userId, maximum]);
|
|
1404
|
+
return result.rows.map(automation);
|
|
1405
|
+
}
|
|
1406
|
+
async findByOwner(automationId, userId) {
|
|
1407
|
+
const result = await this.database.query('SELECT * FROM automations WHERE id = $1 AND user_id = $2', [automationId, userId]);
|
|
1408
|
+
return result.rows[0] ? automation(result.rows[0]) : null;
|
|
1409
|
+
}
|
|
1410
|
+
async findById(automationId) {
|
|
1411
|
+
const result = await this.database.query('SELECT * FROM automations WHERE id = $1', [automationId]);
|
|
1412
|
+
return result.rows[0] ? automation(result.rows[0]) : null;
|
|
1413
|
+
}
|
|
1414
|
+
async replaceWithLimit(value, maximum) {
|
|
1415
|
+
await this.database.transaction(async (client) => {
|
|
1416
|
+
await lockOwner(client, value.user_id);
|
|
1417
|
+
const existing = await client.query('SELECT user_id FROM automations WHERE id = $1 FOR UPDATE', [value.id]);
|
|
1418
|
+
if (existing.rows[0] && existing.rows[0].user_id !== value.user_id) {
|
|
1419
|
+
throw new PersistenceResourceConflictError();
|
|
1420
|
+
}
|
|
1421
|
+
if (!existing.rows[0]) {
|
|
1422
|
+
const count = await client.query('SELECT COUNT(*)::text AS count FROM automations WHERE user_id = $1', [value.user_id]);
|
|
1423
|
+
if (Number(count.rows[0]?.count || 0) >= maximum) {
|
|
1424
|
+
throw new PersistenceResourceLimitError('automation', maximum);
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
const result = await client.query(`INSERT INTO automations
|
|
1428
|
+
(id, user_id, name, instructions, triggers, provider, model,
|
|
1429
|
+
notify, status, next_run_at, last_run_at, created_at, updated_at)
|
|
1430
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
|
|
1431
|
+
ON CONFLICT (id) DO UPDATE SET
|
|
1432
|
+
name = EXCLUDED.name,
|
|
1433
|
+
instructions = EXCLUDED.instructions,
|
|
1434
|
+
triggers = EXCLUDED.triggers,
|
|
1435
|
+
provider = EXCLUDED.provider,
|
|
1436
|
+
model = EXCLUDED.model,
|
|
1437
|
+
notify = EXCLUDED.notify,
|
|
1438
|
+
status = EXCLUDED.status,
|
|
1439
|
+
next_run_at = EXCLUDED.next_run_at,
|
|
1440
|
+
last_run_at = EXCLUDED.last_run_at,
|
|
1441
|
+
updated_at = EXCLUDED.updated_at
|
|
1442
|
+
WHERE automations.user_id = EXCLUDED.user_id`, [
|
|
1443
|
+
value.id,
|
|
1444
|
+
value.user_id,
|
|
1445
|
+
value.name,
|
|
1446
|
+
value.instructions,
|
|
1447
|
+
value.triggers,
|
|
1448
|
+
value.provider,
|
|
1449
|
+
value.model,
|
|
1450
|
+
value.notify,
|
|
1451
|
+
value.status,
|
|
1452
|
+
value.next_run_at,
|
|
1453
|
+
value.last_run_at,
|
|
1454
|
+
value.created_at,
|
|
1455
|
+
value.updated_at,
|
|
1456
|
+
]);
|
|
1457
|
+
if (result.rowCount !== 1)
|
|
1458
|
+
throw new PersistenceResourceConflictError();
|
|
1459
|
+
});
|
|
1460
|
+
}
|
|
1461
|
+
async listDue(now, maximum) {
|
|
1462
|
+
const result = await this.database.query(`SELECT * FROM automations
|
|
1463
|
+
WHERE status = 'active'
|
|
1464
|
+
AND next_run_at IS NOT NULL
|
|
1465
|
+
AND next_run_at <= $1
|
|
1466
|
+
ORDER BY next_run_at ASC
|
|
1467
|
+
LIMIT $2`, [now, maximum]);
|
|
1468
|
+
return result.rows.map(automation);
|
|
1469
|
+
}
|
|
1470
|
+
async advanceNextRun(automationId, observedNextRunAt, nextRunAt, lastRunAt) {
|
|
1471
|
+
return (changes((await this.database.query(`UPDATE automations
|
|
1472
|
+
SET next_run_at = $1, last_run_at = $2
|
|
1473
|
+
WHERE id = $3 AND next_run_at = $4`, [nextRunAt, lastRunAt, automationId, observedNextRunAt])).rowCount) > 0);
|
|
1474
|
+
}
|
|
1475
|
+
async setStatus(automationId, userId, status, nextRunAt, updatedAt) {
|
|
1476
|
+
return (changes((await this.database.query(`UPDATE automations
|
|
1477
|
+
SET status = $1, next_run_at = $2, updated_at = $3
|
|
1478
|
+
WHERE id = $4 AND user_id = $5`, [status, nextRunAt, updatedAt, automationId, userId])).rowCount) > 0);
|
|
1479
|
+
}
|
|
1480
|
+
async deleteByOwner(automationId, userId) {
|
|
1481
|
+
return (changes((await this.database.query('DELETE FROM automations WHERE id = $1 AND user_id = $2', [automationId, userId])).rowCount) > 0);
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
class PostgresAutomationRunRepository {
|
|
1485
|
+
constructor(database) {
|
|
1486
|
+
this.database = database;
|
|
1487
|
+
}
|
|
1488
|
+
async insert(run) {
|
|
1489
|
+
await this.database.query(`INSERT INTO automation_runs
|
|
1490
|
+
(id, automation_id, user_id, scheduled_for, started_at, finished_at,
|
|
1491
|
+
status, session_id, assistant_message_id, error, seen_at, created_at)
|
|
1492
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)`, [
|
|
1493
|
+
run.id,
|
|
1494
|
+
run.automation_id,
|
|
1495
|
+
run.user_id,
|
|
1496
|
+
run.scheduled_for,
|
|
1497
|
+
run.started_at,
|
|
1498
|
+
run.finished_at,
|
|
1499
|
+
run.status,
|
|
1500
|
+
run.session_id,
|
|
1501
|
+
run.assistant_message_id,
|
|
1502
|
+
run.error,
|
|
1503
|
+
run.seen_at,
|
|
1504
|
+
run.created_at,
|
|
1505
|
+
]);
|
|
1506
|
+
}
|
|
1507
|
+
async findByOwner(runId, userId) {
|
|
1508
|
+
const result = await this.database.query('SELECT * FROM automation_runs WHERE id = $1 AND user_id = $2', [runId, userId]);
|
|
1509
|
+
return result.rows[0] ? automationRun(result.rows[0]) : null;
|
|
1510
|
+
}
|
|
1511
|
+
async listByOwner(userId, options) {
|
|
1512
|
+
const clauses = ['user_id = $1'];
|
|
1513
|
+
const values = [userId];
|
|
1514
|
+
if (options.automationId !== undefined) {
|
|
1515
|
+
values.push(options.automationId);
|
|
1516
|
+
clauses.push(`automation_id = $${values.length}`);
|
|
1517
|
+
}
|
|
1518
|
+
if (options.from !== undefined) {
|
|
1519
|
+
values.push(options.from);
|
|
1520
|
+
clauses.push(`scheduled_for >= $${values.length}`);
|
|
1521
|
+
}
|
|
1522
|
+
if (options.to !== undefined) {
|
|
1523
|
+
values.push(options.to);
|
|
1524
|
+
clauses.push(`scheduled_for < $${values.length}`);
|
|
1525
|
+
}
|
|
1526
|
+
values.push(options.maximum);
|
|
1527
|
+
const result = await this.database.query(`SELECT * FROM automation_runs
|
|
1528
|
+
WHERE ${clauses.join(' AND ')}
|
|
1529
|
+
ORDER BY scheduled_for DESC
|
|
1530
|
+
LIMIT $${values.length}`, values);
|
|
1531
|
+
return result.rows.map(automationRun);
|
|
1532
|
+
}
|
|
1533
|
+
async listUnfinished(maximum) {
|
|
1534
|
+
const result = await this.database.query(`SELECT * FROM automation_runs
|
|
1535
|
+
WHERE status IN ('queued', 'running')
|
|
1536
|
+
ORDER BY scheduled_for ASC
|
|
1537
|
+
LIMIT $1`, [maximum]);
|
|
1538
|
+
return result.rows.map(automationRun);
|
|
1539
|
+
}
|
|
1540
|
+
async markStarted(runId, sessionId, assistantMessageId, startedAt) {
|
|
1541
|
+
return (changes((await this.database.query(`UPDATE automation_runs
|
|
1542
|
+
SET session_id = $1, assistant_message_id = $2, started_at = $3,
|
|
1543
|
+
status = 'running'
|
|
1544
|
+
WHERE id = $4 AND status = 'queued'`, [sessionId, assistantMessageId, startedAt, runId])).rowCount) > 0);
|
|
1545
|
+
}
|
|
1546
|
+
async finalize(runId, status, finishedAt, error) {
|
|
1547
|
+
return (changes((await this.database.query(`UPDATE automation_runs
|
|
1548
|
+
SET status = $1, finished_at = $2, error = $3
|
|
1549
|
+
WHERE id = $4 AND status IN ('queued', 'running')`, [status, finishedAt, error, runId])).rowCount) > 0);
|
|
1550
|
+
}
|
|
1551
|
+
async countUnseenFinished(userId) {
|
|
1552
|
+
const result = await this.database.query(`SELECT COUNT(*)::text AS count FROM automation_runs
|
|
1553
|
+
WHERE user_id = $1
|
|
1554
|
+
AND status IN ('succeeded', 'failed')
|
|
1555
|
+
AND seen_at IS NULL`, [userId]);
|
|
1556
|
+
return Number(result.rows[0]?.count || 0);
|
|
1557
|
+
}
|
|
1558
|
+
async markSeenBefore(userId, seenAt) {
|
|
1559
|
+
return changes((await this.database.query(`UPDATE automation_runs
|
|
1560
|
+
SET seen_at = $1
|
|
1561
|
+
WHERE user_id = $2
|
|
1562
|
+
AND status IN ('succeeded', 'failed')
|
|
1563
|
+
AND seen_at IS NULL
|
|
1564
|
+
AND finished_at IS NOT NULL
|
|
1565
|
+
AND finished_at <= $3`, [seenAt, userId, seenAt])).rowCount);
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
class PostgresToolServerRepository {
|
|
1569
|
+
constructor(database) {
|
|
1570
|
+
this.database = database;
|
|
1571
|
+
}
|
|
1572
|
+
async list(maximum) {
|
|
1573
|
+
const result = await this.database.query(`SELECT * FROM tool_servers
|
|
1574
|
+
ORDER BY updated_at DESC
|
|
1575
|
+
LIMIT $1`, [maximum]);
|
|
1576
|
+
return result.rows.map(toolServer);
|
|
1577
|
+
}
|
|
1578
|
+
async findById(serverId) {
|
|
1579
|
+
const result = await this.database.query('SELECT * FROM tool_servers WHERE id = $1', [serverId]);
|
|
1580
|
+
return result.rows[0] ? toolServer(result.rows[0]) : null;
|
|
1581
|
+
}
|
|
1582
|
+
async replaceWithLimit(value, maximum) {
|
|
1583
|
+
await this.database.transaction(async (client) => {
|
|
1584
|
+
await lockOwner(client, value.user_id);
|
|
1585
|
+
const existing = await client.query('SELECT id FROM tool_servers WHERE id = $1 FOR UPDATE', [value.id]);
|
|
1586
|
+
if (!existing.rows[0]) {
|
|
1587
|
+
const count = await client.query('SELECT COUNT(*)::text AS count FROM tool_servers');
|
|
1588
|
+
if (Number(count.rows[0]?.count || 0) >= maximum) {
|
|
1589
|
+
throw new PersistenceResourceLimitError('tool-server', maximum);
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
await client.query(`INSERT INTO tool_servers
|
|
1593
|
+
(id, user_id, name, description, kind, base_url, spec, spec_digest,
|
|
1594
|
+
spec_revision, auth_mode, auth_header, access_mode, enabled,
|
|
1595
|
+
timeout_ms, max_response_bytes, created_at, updated_at)
|
|
1596
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14,
|
|
1597
|
+
$15, $16, $17)
|
|
1598
|
+
ON CONFLICT (id) DO UPDATE SET
|
|
1599
|
+
name = EXCLUDED.name,
|
|
1600
|
+
description = EXCLUDED.description,
|
|
1601
|
+
base_url = EXCLUDED.base_url,
|
|
1602
|
+
spec = EXCLUDED.spec,
|
|
1603
|
+
spec_digest = EXCLUDED.spec_digest,
|
|
1604
|
+
spec_revision = EXCLUDED.spec_revision,
|
|
1605
|
+
auth_mode = EXCLUDED.auth_mode,
|
|
1606
|
+
auth_header = EXCLUDED.auth_header,
|
|
1607
|
+
access_mode = EXCLUDED.access_mode,
|
|
1608
|
+
enabled = EXCLUDED.enabled,
|
|
1609
|
+
timeout_ms = EXCLUDED.timeout_ms,
|
|
1610
|
+
max_response_bytes = EXCLUDED.max_response_bytes,
|
|
1611
|
+
updated_at = EXCLUDED.updated_at`, [
|
|
1612
|
+
value.id,
|
|
1613
|
+
value.user_id,
|
|
1614
|
+
value.name,
|
|
1615
|
+
value.description,
|
|
1616
|
+
value.kind,
|
|
1617
|
+
value.base_url,
|
|
1618
|
+
value.spec,
|
|
1619
|
+
value.spec_digest,
|
|
1620
|
+
value.spec_revision,
|
|
1621
|
+
value.auth_mode,
|
|
1622
|
+
value.auth_header,
|
|
1623
|
+
value.access_mode,
|
|
1624
|
+
value.enabled,
|
|
1625
|
+
value.timeout_ms,
|
|
1626
|
+
value.max_response_bytes,
|
|
1627
|
+
value.created_at,
|
|
1628
|
+
value.updated_at,
|
|
1629
|
+
]);
|
|
1630
|
+
});
|
|
1631
|
+
}
|
|
1632
|
+
async delete(serverId) {
|
|
1633
|
+
return (changes((await this.database.query('DELETE FROM tool_servers WHERE id = $1', [
|
|
1634
|
+
serverId,
|
|
1635
|
+
])).rowCount) > 0);
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
class PostgresToolServerToolRepository {
|
|
1639
|
+
constructor(database) {
|
|
1640
|
+
this.database = database;
|
|
1641
|
+
}
|
|
1642
|
+
async listByServer(serverId) {
|
|
1643
|
+
const result = await this.database.query(`SELECT * FROM tool_server_tools
|
|
1644
|
+
WHERE server_id = $1
|
|
1645
|
+
ORDER BY name ASC`, [serverId]);
|
|
1646
|
+
return result.rows.map(toolServerTool);
|
|
1647
|
+
}
|
|
1648
|
+
async replaceAllForServer(serverId, tools) {
|
|
1649
|
+
await this.database.transaction(async (client) => {
|
|
1650
|
+
const previousResult = await client.query(`SELECT name, enabled, side_effect FROM tool_server_tools
|
|
1651
|
+
WHERE server_id = $1 FOR UPDATE`, [serverId]);
|
|
1652
|
+
const previous = new Map(previousResult.rows.map(row => [
|
|
1653
|
+
row.name,
|
|
1654
|
+
{
|
|
1655
|
+
enabled: number(row.enabled, 'tool enabled'),
|
|
1656
|
+
side_effect: number(row.side_effect, 'tool side_effect'),
|
|
1657
|
+
},
|
|
1658
|
+
]));
|
|
1659
|
+
await client.query('DELETE FROM tool_server_tools WHERE server_id = $1', [
|
|
1660
|
+
serverId,
|
|
1661
|
+
]);
|
|
1662
|
+
for (const tool of tools) {
|
|
1663
|
+
if (tool.server_id !== serverId) {
|
|
1664
|
+
throw new Error('A tool row does not belong to its server');
|
|
1665
|
+
}
|
|
1666
|
+
const kept = previous.get(tool.name);
|
|
1667
|
+
await client.query(`INSERT INTO tool_server_tools
|
|
1668
|
+
(id, server_id, name, description, params_schema, detail,
|
|
1669
|
+
side_effect, enabled, created_at, updated_at)
|
|
1670
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`, [
|
|
1671
|
+
tool.id,
|
|
1672
|
+
tool.server_id,
|
|
1673
|
+
tool.name,
|
|
1674
|
+
tool.description,
|
|
1675
|
+
tool.params_schema,
|
|
1676
|
+
tool.detail,
|
|
1677
|
+
kept ? kept.side_effect : tool.side_effect,
|
|
1678
|
+
kept ? kept.enabled : tool.enabled,
|
|
1679
|
+
tool.created_at,
|
|
1680
|
+
tool.updated_at,
|
|
1681
|
+
]);
|
|
1682
|
+
}
|
|
1683
|
+
});
|
|
1684
|
+
}
|
|
1685
|
+
async updateOverrides(serverId, toolName, overrides, updatedAt) {
|
|
1686
|
+
const assignments = ['updated_at = $1'];
|
|
1687
|
+
const values = [updatedAt];
|
|
1688
|
+
if (overrides.enabled !== undefined) {
|
|
1689
|
+
values.push(overrides.enabled);
|
|
1690
|
+
assignments.push(`enabled = $${values.length}`);
|
|
1691
|
+
}
|
|
1692
|
+
if (overrides.side_effect !== undefined) {
|
|
1693
|
+
values.push(overrides.side_effect);
|
|
1694
|
+
assignments.push(`side_effect = $${values.length}`);
|
|
1695
|
+
}
|
|
1696
|
+
values.push(serverId, toolName);
|
|
1697
|
+
const result = await this.database.query(`UPDATE tool_server_tools
|
|
1698
|
+
SET ${assignments.join(', ')}
|
|
1699
|
+
WHERE server_id = $${values.length - 1} AND name = $${values.length}
|
|
1700
|
+
RETURNING *`, values);
|
|
1701
|
+
return result.rows[0] ? toolServerTool(result.rows[0]) : null;
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
class PostgresToolServerCredentialRepository {
|
|
1705
|
+
constructor(database) {
|
|
1706
|
+
this.database = database;
|
|
1707
|
+
}
|
|
1708
|
+
async find(serverId, userId) {
|
|
1709
|
+
const result = await this.database.query(`SELECT * FROM tool_server_credentials
|
|
1710
|
+
WHERE server_id = $1 AND user_id = $2`, [serverId, userId]);
|
|
1711
|
+
return result.rows[0] ? toolServerCredential(result.rows[0]) : null;
|
|
1712
|
+
}
|
|
1713
|
+
async upsert(credential) {
|
|
1714
|
+
await this.database.query(`INSERT INTO tool_server_credentials
|
|
1715
|
+
(id, server_id, user_id, secret, created_at, updated_at)
|
|
1716
|
+
VALUES ($1, $2, $3, $4, $5, $6)
|
|
1717
|
+
ON CONFLICT (server_id, user_id) DO UPDATE SET
|
|
1718
|
+
secret = EXCLUDED.secret,
|
|
1719
|
+
updated_at = EXCLUDED.updated_at`, [
|
|
1720
|
+
credential.id,
|
|
1721
|
+
credential.server_id,
|
|
1722
|
+
credential.user_id,
|
|
1723
|
+
credential.secret,
|
|
1724
|
+
credential.created_at,
|
|
1725
|
+
credential.updated_at,
|
|
1726
|
+
]);
|
|
1727
|
+
}
|
|
1728
|
+
async delete(serverId, userId) {
|
|
1729
|
+
return (changes((await this.database.query(`DELETE FROM tool_server_credentials
|
|
1730
|
+
WHERE server_id = $1 AND user_id = $2`, [serverId, userId])).rowCount) > 0);
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
class PostgresToolApprovalRepository {
|
|
1734
|
+
constructor(database) {
|
|
1735
|
+
this.database = database;
|
|
1736
|
+
}
|
|
1737
|
+
async insert(approval) {
|
|
1738
|
+
await this.database.query(`INSERT INTO tool_approvals
|
|
1739
|
+
(id, user_id, session_id, server_id, tool_name, call_id,
|
|
1740
|
+
arguments_digest, scope, status, created_at, resolved_at, expires_at)
|
|
1741
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)`, [
|
|
1742
|
+
approval.id,
|
|
1743
|
+
approval.user_id,
|
|
1744
|
+
approval.session_id,
|
|
1745
|
+
approval.server_id,
|
|
1746
|
+
approval.tool_name,
|
|
1747
|
+
approval.call_id,
|
|
1748
|
+
approval.arguments_digest,
|
|
1749
|
+
approval.scope,
|
|
1750
|
+
approval.status,
|
|
1751
|
+
approval.created_at,
|
|
1752
|
+
approval.resolved_at,
|
|
1753
|
+
approval.expires_at,
|
|
1754
|
+
]);
|
|
1755
|
+
}
|
|
1756
|
+
async findByOwner(approvalId, userId) {
|
|
1757
|
+
const result = await this.database.query('SELECT * FROM tool_approvals WHERE id = $1 AND user_id = $2', [approvalId, userId]);
|
|
1758
|
+
return result.rows[0] ? toolApproval(result.rows[0]) : null;
|
|
1759
|
+
}
|
|
1760
|
+
async findStanding(userId, serverId, toolName, sessionId) {
|
|
1761
|
+
const result = await this.database.query(`SELECT * FROM tool_approvals
|
|
1762
|
+
WHERE user_id = $1
|
|
1763
|
+
AND status = 'approved'
|
|
1764
|
+
AND tool_name = $2
|
|
1765
|
+
AND ((server_id IS NULL AND $3::text IS NULL) OR server_id = $3)
|
|
1766
|
+
AND (scope = 'always'
|
|
1767
|
+
OR (scope = 'session' AND session_id IS NOT NULL AND session_id = $4))
|
|
1768
|
+
ORDER BY resolved_at DESC
|
|
1769
|
+
LIMIT 1`, [userId, toolName, serverId, sessionId]);
|
|
1770
|
+
return result.rows[0] ? toolApproval(result.rows[0]) : null;
|
|
1771
|
+
}
|
|
1772
|
+
async resolvePending(approvalId, userId, status, scope, resolvedAt) {
|
|
1773
|
+
const result = await this.database.query(`UPDATE tool_approvals
|
|
1774
|
+
SET status = $1, scope = $2, resolved_at = $3
|
|
1775
|
+
WHERE id = $4 AND user_id = $5 AND status = 'pending'
|
|
1776
|
+
AND (expires_at IS NULL OR expires_at > $3)
|
|
1777
|
+
RETURNING *`, [status, scope, resolvedAt, approvalId, userId]);
|
|
1778
|
+
return result.rows[0] ? toolApproval(result.rows[0]) : null;
|
|
1779
|
+
}
|
|
1780
|
+
async listPendingByOwner(userId, maximum) {
|
|
1781
|
+
const result = await this.database.query(`SELECT * FROM tool_approvals
|
|
1782
|
+
WHERE user_id = $1 AND status = 'pending'
|
|
1783
|
+
ORDER BY created_at ASC
|
|
1784
|
+
LIMIT $2`, [userId, maximum]);
|
|
1785
|
+
return result.rows.map(toolApproval);
|
|
1786
|
+
}
|
|
1787
|
+
async listStandingByOwner(userId, maximum) {
|
|
1788
|
+
const result = await this.database.query(`SELECT * FROM tool_approvals
|
|
1789
|
+
WHERE user_id = $1 AND status = 'approved'
|
|
1790
|
+
AND scope IN ('session', 'always')
|
|
1791
|
+
ORDER BY resolved_at DESC
|
|
1792
|
+
LIMIT $2`, [userId, maximum]);
|
|
1793
|
+
return result.rows.map(toolApproval);
|
|
1794
|
+
}
|
|
1795
|
+
async expirePending(now) {
|
|
1796
|
+
return changes((await this.database.query(`UPDATE tool_approvals
|
|
1797
|
+
SET status = 'expired', resolved_at = $1
|
|
1798
|
+
WHERE status = 'pending' AND expires_at IS NOT NULL
|
|
1799
|
+
AND expires_at <= $1`, [now])).rowCount);
|
|
1800
|
+
}
|
|
1801
|
+
async deleteByOwner(approvalId, userId) {
|
|
1802
|
+
return (changes((await this.database.query('DELETE FROM tool_approvals WHERE id = $1 AND user_id = $2', [approvalId, userId])).rowCount) > 0);
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
class PostgresPromptRepository {
|
|
1806
|
+
constructor(database) {
|
|
1807
|
+
this.database = database;
|
|
1808
|
+
}
|
|
1809
|
+
async listByOwner(userId, maximum) {
|
|
1810
|
+
const result = await this.database.query(`SELECT * FROM prompts
|
|
1811
|
+
WHERE user_id = $1
|
|
1812
|
+
ORDER BY updated_at DESC
|
|
1813
|
+
LIMIT $2`, [userId, maximum]);
|
|
1814
|
+
return result.rows.map(prompt);
|
|
1815
|
+
}
|
|
1816
|
+
async findByOwner(promptId, userId) {
|
|
1817
|
+
const result = await this.database.query('SELECT * FROM prompts WHERE id = $1 AND user_id = $2', [promptId, userId]);
|
|
1818
|
+
return result.rows[0] ? prompt(result.rows[0]) : null;
|
|
1819
|
+
}
|
|
1820
|
+
async findById(promptId) {
|
|
1821
|
+
const result = await this.database.query('SELECT * FROM prompts WHERE id = $1', [promptId]);
|
|
1822
|
+
return result.rows[0] ? prompt(result.rows[0]) : null;
|
|
1823
|
+
}
|
|
1824
|
+
async findBySlug(userId, slug) {
|
|
1825
|
+
const result = await this.database.query('SELECT * FROM prompts WHERE user_id = $1 AND slug = $2', [userId, slug]);
|
|
1826
|
+
return result.rows[0] ? prompt(result.rows[0]) : null;
|
|
1827
|
+
}
|
|
1828
|
+
async replaceWithLimit(value, maximum, archivedVersion) {
|
|
1829
|
+
await this.database.transaction(async (client) => {
|
|
1830
|
+
await lockOwner(client, value.user_id);
|
|
1831
|
+
const existing = await client.query('SELECT user_id FROM prompts WHERE id = $1 FOR UPDATE', [value.id]);
|
|
1832
|
+
if (existing.rows[0] && existing.rows[0].user_id !== value.user_id) {
|
|
1833
|
+
throw new PersistenceResourceConflictError();
|
|
1834
|
+
}
|
|
1835
|
+
if (!existing.rows[0]) {
|
|
1836
|
+
const count = await client.query('SELECT COUNT(*)::text AS count FROM prompts WHERE user_id = $1', [value.user_id]);
|
|
1837
|
+
if (Number(count.rows[0]?.count || 0) >= maximum) {
|
|
1838
|
+
throw new PersistenceResourceLimitError('prompt', maximum);
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
const result = await client.query(`INSERT INTO prompts
|
|
1842
|
+
(id, user_id, slug, title, description, content, variables, tags,
|
|
1843
|
+
version, created_at, updated_at)
|
|
1844
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
|
1845
|
+
ON CONFLICT (id) DO UPDATE SET
|
|
1846
|
+
slug = EXCLUDED.slug,
|
|
1847
|
+
title = EXCLUDED.title,
|
|
1848
|
+
description = EXCLUDED.description,
|
|
1849
|
+
content = EXCLUDED.content,
|
|
1850
|
+
variables = EXCLUDED.variables,
|
|
1851
|
+
tags = EXCLUDED.tags,
|
|
1852
|
+
version = EXCLUDED.version,
|
|
1853
|
+
updated_at = EXCLUDED.updated_at
|
|
1854
|
+
WHERE prompts.user_id = EXCLUDED.user_id`, [
|
|
1855
|
+
value.id,
|
|
1856
|
+
value.user_id,
|
|
1857
|
+
value.slug,
|
|
1858
|
+
value.title,
|
|
1859
|
+
value.description,
|
|
1860
|
+
value.content,
|
|
1861
|
+
value.variables,
|
|
1862
|
+
value.tags,
|
|
1863
|
+
value.version,
|
|
1864
|
+
value.created_at,
|
|
1865
|
+
value.updated_at,
|
|
1866
|
+
]);
|
|
1867
|
+
if (result.rowCount !== 1)
|
|
1868
|
+
throw new PersistenceResourceConflictError();
|
|
1869
|
+
if (archivedVersion) {
|
|
1870
|
+
await client.query(`INSERT INTO prompt_versions
|
|
1871
|
+
(id, prompt_id, version, content, variables, created_at)
|
|
1872
|
+
VALUES ($1, $2, $3, $4, $5, $6)
|
|
1873
|
+
ON CONFLICT (prompt_id, version) DO NOTHING`, [
|
|
1874
|
+
archivedVersion.id,
|
|
1875
|
+
archivedVersion.prompt_id,
|
|
1876
|
+
archivedVersion.version,
|
|
1877
|
+
archivedVersion.content,
|
|
1878
|
+
archivedVersion.variables,
|
|
1879
|
+
archivedVersion.created_at,
|
|
1880
|
+
]);
|
|
1881
|
+
}
|
|
1882
|
+
});
|
|
1883
|
+
}
|
|
1884
|
+
async listVersions(promptId, maximum) {
|
|
1885
|
+
const result = await this.database.query(`SELECT * FROM prompt_versions
|
|
1886
|
+
WHERE prompt_id = $1
|
|
1887
|
+
ORDER BY version DESC
|
|
1888
|
+
LIMIT $2`, [promptId, maximum]);
|
|
1889
|
+
return result.rows.map(promptVersion);
|
|
1890
|
+
}
|
|
1891
|
+
async findVersion(promptId, version) {
|
|
1892
|
+
const result = await this.database.query('SELECT * FROM prompt_versions WHERE prompt_id = $1 AND version = $2', [promptId, version]);
|
|
1893
|
+
return result.rows[0] ? promptVersion(result.rows[0]) : null;
|
|
1894
|
+
}
|
|
1895
|
+
async deleteByOwner(promptId, userId) {
|
|
1896
|
+
return (changes((await this.database.query('DELETE FROM prompts WHERE id = $1 AND user_id = $2', [promptId, userId])).rowCount) > 0);
|
|
411
1897
|
}
|
|
412
1898
|
}
|
|
413
|
-
class
|
|
1899
|
+
class PostgresSkillRepository {
|
|
414
1900
|
constructor(database) {
|
|
415
1901
|
this.database = database;
|
|
416
1902
|
}
|
|
417
1903
|
async listByOwner(userId, maximum) {
|
|
418
|
-
const result = await this.database.query(`SELECT * FROM
|
|
1904
|
+
const result = await this.database.query(`SELECT * FROM skills
|
|
419
1905
|
WHERE user_id = $1
|
|
420
1906
|
ORDER BY updated_at DESC
|
|
421
1907
|
LIMIT $2`, [userId, maximum]);
|
|
422
|
-
return result.rows.map(
|
|
1908
|
+
return result.rows.map(skill);
|
|
423
1909
|
}
|
|
424
|
-
async findByOwner(
|
|
425
|
-
const result = await this.database.query('SELECT * FROM
|
|
426
|
-
return result.rows[0] ?
|
|
1910
|
+
async findByOwner(skillId, userId) {
|
|
1911
|
+
const result = await this.database.query('SELECT * FROM skills WHERE id = $1 AND user_id = $2', [skillId, userId]);
|
|
1912
|
+
return result.rows[0] ? skill(result.rows[0]) : null;
|
|
427
1913
|
}
|
|
428
|
-
async findById(
|
|
429
|
-
const result = await this.database.query('SELECT * FROM
|
|
430
|
-
return result.rows[0] ?
|
|
1914
|
+
async findById(skillId) {
|
|
1915
|
+
const result = await this.database.query('SELECT * FROM skills WHERE id = $1', [skillId]);
|
|
1916
|
+
return result.rows[0] ? skill(result.rows[0]) : null;
|
|
431
1917
|
}
|
|
432
|
-
async
|
|
1918
|
+
async findBySlug(userId, slug) {
|
|
1919
|
+
const result = await this.database.query('SELECT * FROM skills WHERE user_id = $1 AND slug = $2', [userId, slug]);
|
|
1920
|
+
return result.rows[0] ? skill(result.rows[0]) : null;
|
|
1921
|
+
}
|
|
1922
|
+
async replaceWithLimit(value, maximum, archivedVersion) {
|
|
433
1923
|
await this.database.transaction(async (client) => {
|
|
434
1924
|
await lockOwner(client, value.user_id);
|
|
435
|
-
const existing = await client.query('SELECT user_id FROM
|
|
1925
|
+
const existing = await client.query('SELECT user_id FROM skills WHERE id = $1 FOR UPDATE', [value.id]);
|
|
436
1926
|
if (existing.rows[0] && existing.rows[0].user_id !== value.user_id) {
|
|
437
1927
|
throw new PersistenceResourceConflictError();
|
|
438
1928
|
}
|
|
439
1929
|
if (!existing.rows[0]) {
|
|
440
|
-
const count = await client.query('SELECT COUNT(*)::text AS count FROM
|
|
1930
|
+
const count = await client.query('SELECT COUNT(*)::text AS count FROM skills WHERE user_id = $1', [value.user_id]);
|
|
441
1931
|
if (Number(count.rows[0]?.count || 0) >= maximum) {
|
|
442
|
-
throw new PersistenceResourceLimitError('
|
|
1932
|
+
throw new PersistenceResourceLimitError('skill', maximum);
|
|
443
1933
|
}
|
|
444
1934
|
}
|
|
445
|
-
const result = await client.query(`INSERT INTO
|
|
446
|
-
(id, user_id,
|
|
447
|
-
|
|
448
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10
|
|
1935
|
+
const result = await client.query(`INSERT INTO skills
|
|
1936
|
+
(id, user_id, slug, name, description, instructions, enabled,
|
|
1937
|
+
version, created_at, updated_at)
|
|
1938
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
|
449
1939
|
ON CONFLICT (id) DO UPDATE SET
|
|
1940
|
+
slug = EXCLUDED.slug,
|
|
450
1941
|
name = EXCLUDED.name,
|
|
1942
|
+
description = EXCLUDED.description,
|
|
451
1943
|
instructions = EXCLUDED.instructions,
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
model = EXCLUDED.model,
|
|
455
|
-
notify = EXCLUDED.notify,
|
|
456
|
-
status = EXCLUDED.status,
|
|
457
|
-
next_run_at = EXCLUDED.next_run_at,
|
|
458
|
-
last_run_at = EXCLUDED.last_run_at,
|
|
1944
|
+
enabled = EXCLUDED.enabled,
|
|
1945
|
+
version = EXCLUDED.version,
|
|
459
1946
|
updated_at = EXCLUDED.updated_at
|
|
460
|
-
WHERE
|
|
1947
|
+
WHERE skills.user_id = EXCLUDED.user_id`, [
|
|
461
1948
|
value.id,
|
|
462
1949
|
value.user_id,
|
|
1950
|
+
value.slug,
|
|
463
1951
|
value.name,
|
|
1952
|
+
value.description,
|
|
464
1953
|
value.instructions,
|
|
465
|
-
value.
|
|
466
|
-
value.
|
|
467
|
-
value.model,
|
|
468
|
-
value.notify,
|
|
469
|
-
value.status,
|
|
470
|
-
value.next_run_at,
|
|
471
|
-
value.last_run_at,
|
|
1954
|
+
value.enabled,
|
|
1955
|
+
value.version,
|
|
472
1956
|
value.created_at,
|
|
473
1957
|
value.updated_at,
|
|
474
1958
|
]);
|
|
475
1959
|
if (result.rowCount !== 1)
|
|
476
1960
|
throw new PersistenceResourceConflictError();
|
|
1961
|
+
if (archivedVersion) {
|
|
1962
|
+
await client.query(`INSERT INTO skill_versions
|
|
1963
|
+
(id, skill_id, version, instructions, created_at)
|
|
1964
|
+
VALUES ($1, $2, $3, $4, $5)
|
|
1965
|
+
ON CONFLICT (skill_id, version) DO NOTHING`, [
|
|
1966
|
+
archivedVersion.id,
|
|
1967
|
+
archivedVersion.skill_id,
|
|
1968
|
+
archivedVersion.version,
|
|
1969
|
+
archivedVersion.instructions,
|
|
1970
|
+
archivedVersion.created_at,
|
|
1971
|
+
]);
|
|
1972
|
+
}
|
|
477
1973
|
});
|
|
478
1974
|
}
|
|
479
|
-
async
|
|
480
|
-
const result = await this.database.query(`SELECT * FROM
|
|
481
|
-
WHERE
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
LIMIT $2`, [now, maximum]);
|
|
486
|
-
return result.rows.map(automation);
|
|
487
|
-
}
|
|
488
|
-
async advanceNextRun(automationId, observedNextRunAt, nextRunAt, lastRunAt) {
|
|
489
|
-
return (changes((await this.database.query(`UPDATE automations
|
|
490
|
-
SET next_run_at = $1, last_run_at = $2
|
|
491
|
-
WHERE id = $3 AND next_run_at = $4`, [nextRunAt, lastRunAt, automationId, observedNextRunAt])).rowCount) > 0);
|
|
1975
|
+
async listVersions(skillId, maximum) {
|
|
1976
|
+
const result = await this.database.query(`SELECT * FROM skill_versions
|
|
1977
|
+
WHERE skill_id = $1
|
|
1978
|
+
ORDER BY version DESC
|
|
1979
|
+
LIMIT $2`, [skillId, maximum]);
|
|
1980
|
+
return result.rows.map(skillVersion);
|
|
492
1981
|
}
|
|
493
|
-
async
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
WHERE id = $4 AND user_id = $5`, [status, nextRunAt, updatedAt, automationId, userId])).rowCount) > 0);
|
|
1982
|
+
async findVersion(skillId, version) {
|
|
1983
|
+
const result = await this.database.query('SELECT * FROM skill_versions WHERE skill_id = $1 AND version = $2', [skillId, version]);
|
|
1984
|
+
return result.rows[0] ? skillVersion(result.rows[0]) : null;
|
|
497
1985
|
}
|
|
498
|
-
async deleteByOwner(
|
|
499
|
-
return (changes((await this.database.query('DELETE FROM
|
|
1986
|
+
async deleteByOwner(skillId, userId) {
|
|
1987
|
+
return (changes((await this.database.query('DELETE FROM skills WHERE id = $1 AND user_id = $2', [skillId, userId])).rowCount) > 0);
|
|
500
1988
|
}
|
|
501
1989
|
}
|
|
502
|
-
class
|
|
1990
|
+
class PostgresSkillFileRepository {
|
|
503
1991
|
constructor(database) {
|
|
504
1992
|
this.database = database;
|
|
505
1993
|
}
|
|
506
|
-
async
|
|
507
|
-
await this.database.query(`
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
run.id,
|
|
512
|
-
run.automation_id,
|
|
513
|
-
run.user_id,
|
|
514
|
-
run.scheduled_for,
|
|
515
|
-
run.started_at,
|
|
516
|
-
run.finished_at,
|
|
517
|
-
run.status,
|
|
518
|
-
run.session_id,
|
|
519
|
-
run.assistant_message_id,
|
|
520
|
-
run.error,
|
|
521
|
-
run.seen_at,
|
|
522
|
-
run.created_at,
|
|
523
|
-
]);
|
|
524
|
-
}
|
|
525
|
-
async findByOwner(runId, userId) {
|
|
526
|
-
const result = await this.database.query('SELECT * FROM automation_runs WHERE id = $1 AND user_id = $2', [runId, userId]);
|
|
527
|
-
return result.rows[0] ? automationRun(result.rows[0]) : null;
|
|
528
|
-
}
|
|
529
|
-
async listByOwner(userId, options) {
|
|
530
|
-
const clauses = ['user_id = $1'];
|
|
531
|
-
const values = [userId];
|
|
532
|
-
if (options.automationId !== undefined) {
|
|
533
|
-
values.push(options.automationId);
|
|
534
|
-
clauses.push(`automation_id = $${values.length}`);
|
|
535
|
-
}
|
|
536
|
-
if (options.from !== undefined) {
|
|
537
|
-
values.push(options.from);
|
|
538
|
-
clauses.push(`scheduled_for >= $${values.length}`);
|
|
539
|
-
}
|
|
540
|
-
if (options.to !== undefined) {
|
|
541
|
-
values.push(options.to);
|
|
542
|
-
clauses.push(`scheduled_for < $${values.length}`);
|
|
543
|
-
}
|
|
544
|
-
values.push(options.maximum);
|
|
545
|
-
const result = await this.database.query(`SELECT * FROM automation_runs
|
|
546
|
-
WHERE ${clauses.join(' AND ')}
|
|
547
|
-
ORDER BY scheduled_for DESC
|
|
548
|
-
LIMIT $${values.length}`, values);
|
|
549
|
-
return result.rows.map(automationRun);
|
|
550
|
-
}
|
|
551
|
-
async listUnfinished(maximum) {
|
|
552
|
-
const result = await this.database.query(`SELECT * FROM automation_runs
|
|
553
|
-
WHERE status IN ('queued', 'running')
|
|
554
|
-
ORDER BY scheduled_for ASC
|
|
555
|
-
LIMIT $1`, [maximum]);
|
|
556
|
-
return result.rows.map(automationRun);
|
|
1994
|
+
async listBySkill(skillId) {
|
|
1995
|
+
const result = await this.database.query(`SELECT * FROM skill_files
|
|
1996
|
+
WHERE skill_id = $1
|
|
1997
|
+
ORDER BY path ASC`, [skillId]);
|
|
1998
|
+
return result.rows.map(skillFile);
|
|
557
1999
|
}
|
|
558
|
-
async
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
status = 'running'
|
|
562
|
-
WHERE id = $4 AND status = 'queued'`, [sessionId, assistantMessageId, startedAt, runId])).rowCount) > 0);
|
|
2000
|
+
async find(skillId, path) {
|
|
2001
|
+
const result = await this.database.query('SELECT * FROM skill_files WHERE skill_id = $1 AND path = $2', [skillId, path]);
|
|
2002
|
+
return result.rows[0] ? skillFile(result.rows[0]) : null;
|
|
563
2003
|
}
|
|
564
|
-
async
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
2004
|
+
async upsert(file, maximumPerSkill) {
|
|
2005
|
+
await this.database.transaction(async (client) => {
|
|
2006
|
+
const existing = await client.query('SELECT id FROM skill_files WHERE skill_id = $1 AND path = $2 FOR UPDATE', [file.skill_id, file.path]);
|
|
2007
|
+
if (!existing.rows[0]) {
|
|
2008
|
+
const count = await client.query('SELECT COUNT(*)::text AS count FROM skill_files WHERE skill_id = $1', [file.skill_id]);
|
|
2009
|
+
if (Number(count.rows[0]?.count || 0) >= maximumPerSkill) {
|
|
2010
|
+
throw new PersistenceResourceLimitError('skill-file', maximumPerSkill);
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
await client.query(`INSERT INTO skill_files
|
|
2014
|
+
(id, skill_id, path, content, size, created_at, updated_at)
|
|
2015
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
2016
|
+
ON CONFLICT (skill_id, path) DO UPDATE SET
|
|
2017
|
+
content = EXCLUDED.content,
|
|
2018
|
+
size = EXCLUDED.size,
|
|
2019
|
+
updated_at = EXCLUDED.updated_at`, [
|
|
2020
|
+
file.id,
|
|
2021
|
+
file.skill_id,
|
|
2022
|
+
file.path,
|
|
2023
|
+
file.content,
|
|
2024
|
+
file.size,
|
|
2025
|
+
file.created_at,
|
|
2026
|
+
file.updated_at,
|
|
2027
|
+
]);
|
|
2028
|
+
});
|
|
568
2029
|
}
|
|
569
|
-
async
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
2030
|
+
async replaceAllForSkill(skillId, files) {
|
|
2031
|
+
await this.database.transaction(async (client) => {
|
|
2032
|
+
await client.query('DELETE FROM skill_files WHERE skill_id = $1', [
|
|
2033
|
+
skillId,
|
|
2034
|
+
]);
|
|
2035
|
+
for (const file of files) {
|
|
2036
|
+
if (file.skill_id !== skillId) {
|
|
2037
|
+
throw new Error('A skill file does not belong to its skill');
|
|
2038
|
+
}
|
|
2039
|
+
await client.query(`INSERT INTO skill_files
|
|
2040
|
+
(id, skill_id, path, content, size, created_at, updated_at)
|
|
2041
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)`, [
|
|
2042
|
+
file.id,
|
|
2043
|
+
file.skill_id,
|
|
2044
|
+
file.path,
|
|
2045
|
+
file.content,
|
|
2046
|
+
file.size,
|
|
2047
|
+
file.created_at,
|
|
2048
|
+
file.updated_at,
|
|
2049
|
+
]);
|
|
2050
|
+
}
|
|
2051
|
+
});
|
|
575
2052
|
}
|
|
576
|
-
async
|
|
577
|
-
return changes((await this.database.query(
|
|
578
|
-
SET seen_at = $1
|
|
579
|
-
WHERE user_id = $2
|
|
580
|
-
AND status IN ('succeeded', 'failed')
|
|
581
|
-
AND seen_at IS NULL
|
|
582
|
-
AND finished_at IS NOT NULL
|
|
583
|
-
AND finished_at <= $3`, [seenAt, userId, seenAt])).rowCount);
|
|
2053
|
+
async delete(skillId, path) {
|
|
2054
|
+
return (changes((await this.database.query('DELETE FROM skill_files WHERE skill_id = $1 AND path = $2', [skillId, path])).rowCount) > 0);
|
|
584
2055
|
}
|
|
585
2056
|
}
|
|
586
2057
|
class PostgresSessionFolderRepository {
|
|
@@ -736,6 +2207,10 @@ const ARCHIVE_TABLES = {
|
|
|
736
2207
|
'knowledge-collection': 'knowledge_collections',
|
|
737
2208
|
document: 'documents',
|
|
738
2209
|
persona: 'personas',
|
|
2210
|
+
prompt: 'prompts',
|
|
2211
|
+
skill: 'skills',
|
|
2212
|
+
'tool-server': 'tool_servers',
|
|
2213
|
+
calendar: 'calendars',
|
|
739
2214
|
};
|
|
740
2215
|
class PostgresDataArchiveRepository {
|
|
741
2216
|
constructor(database) {
|
|
@@ -1027,17 +2502,339 @@ class PostgresDataArchiveRepository {
|
|
|
1027
2502
|
});
|
|
1028
2503
|
}
|
|
1029
2504
|
}
|
|
2505
|
+
const modelTariff = (row) => ({
|
|
2506
|
+
...row,
|
|
2507
|
+
input_per_million: row.input_per_million === null ? null : Number(row.input_per_million),
|
|
2508
|
+
output_per_million: row.output_per_million === null ? null : Number(row.output_per_million),
|
|
2509
|
+
unit_price: row.unit_price === null ? null : Number(row.unit_price),
|
|
2510
|
+
effective_from: number(row.effective_from, 'tariff effective_from'),
|
|
2511
|
+
created_at: number(row.created_at, 'tariff created_at'),
|
|
2512
|
+
});
|
|
2513
|
+
const usageBudget = (row) => ({
|
|
2514
|
+
...row,
|
|
2515
|
+
amount_usd: Number(row.amount_usd),
|
|
2516
|
+
created_at: number(row.created_at, 'budget created_at'),
|
|
2517
|
+
updated_at: number(row.updated_at, 'budget updated_at'),
|
|
2518
|
+
});
|
|
2519
|
+
const messageFeedback = (row) => ({
|
|
2520
|
+
...row,
|
|
2521
|
+
rating: number(row.rating, 'feedback rating'),
|
|
2522
|
+
created_at: number(row.created_at, 'feedback created_at'),
|
|
2523
|
+
updated_at: number(row.updated_at, 'feedback updated_at'),
|
|
2524
|
+
});
|
|
2525
|
+
const arenaVote = (row) => ({
|
|
2526
|
+
...row,
|
|
2527
|
+
created_at: number(row.created_at, 'arena vote created_at'),
|
|
2528
|
+
});
|
|
2529
|
+
const evalSet = (row) => ({
|
|
2530
|
+
...row,
|
|
2531
|
+
created_at: number(row.created_at, 'eval set created_at'),
|
|
2532
|
+
updated_at: number(row.updated_at, 'eval set updated_at'),
|
|
2533
|
+
});
|
|
2534
|
+
const evalRun = (row) => ({
|
|
2535
|
+
...row,
|
|
2536
|
+
created_at: number(row.created_at, 'eval run created_at'),
|
|
2537
|
+
updated_at: number(row.updated_at, 'eval run updated_at'),
|
|
2538
|
+
completed_at: row.completed_at === null
|
|
2539
|
+
? null
|
|
2540
|
+
: number(row.completed_at, 'eval run completed_at'),
|
|
2541
|
+
});
|
|
2542
|
+
class PostgresModelTariffRepository {
|
|
2543
|
+
constructor(database) {
|
|
2544
|
+
this.database = database;
|
|
2545
|
+
}
|
|
2546
|
+
async listAll(maximum) {
|
|
2547
|
+
const result = await this.database.query(`SELECT * FROM model_tariffs
|
|
2548
|
+
ORDER BY plugin_id ASC, model ASC, effective_from DESC
|
|
2549
|
+
LIMIT $1`, [maximum]);
|
|
2550
|
+
return result.rows.map(modelTariff);
|
|
2551
|
+
}
|
|
2552
|
+
async insert(tariff) {
|
|
2553
|
+
await this.database.query(`INSERT INTO model_tariffs
|
|
2554
|
+
(id, plugin_id, model, input_per_million, output_per_million,
|
|
2555
|
+
unit_price, currency, effective_from, created_by, created_at)
|
|
2556
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`, [
|
|
2557
|
+
tariff.id,
|
|
2558
|
+
tariff.plugin_id,
|
|
2559
|
+
tariff.model,
|
|
2560
|
+
tariff.input_per_million,
|
|
2561
|
+
tariff.output_per_million,
|
|
2562
|
+
tariff.unit_price,
|
|
2563
|
+
tariff.currency,
|
|
2564
|
+
tariff.effective_from,
|
|
2565
|
+
tariff.created_by,
|
|
2566
|
+
tariff.created_at,
|
|
2567
|
+
]);
|
|
2568
|
+
}
|
|
2569
|
+
async deleteById(tariffId) {
|
|
2570
|
+
const result = await this.database.query('DELETE FROM model_tariffs WHERE id = $1', [tariffId]);
|
|
2571
|
+
return changes(result.rowCount) > 0;
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
class PostgresUsageBudgetRepository {
|
|
2575
|
+
constructor(database) {
|
|
2576
|
+
this.database = database;
|
|
2577
|
+
}
|
|
2578
|
+
async listAll(maximum) {
|
|
2579
|
+
const result = await this.database.query(`SELECT * FROM usage_budgets
|
|
2580
|
+
ORDER BY created_at ASC, id ASC
|
|
2581
|
+
LIMIT $1`, [maximum]);
|
|
2582
|
+
return result.rows.map(usageBudget);
|
|
2583
|
+
}
|
|
2584
|
+
async findById(budgetId) {
|
|
2585
|
+
const result = await this.database.query('SELECT * FROM usage_budgets WHERE id = $1', [budgetId]);
|
|
2586
|
+
return result.rows[0] ? usageBudget(result.rows[0]) : null;
|
|
2587
|
+
}
|
|
2588
|
+
async replace(budget) {
|
|
2589
|
+
await this.database.query(`INSERT INTO usage_budgets
|
|
2590
|
+
(id, name, principal_type, principal_id, period, amount_usd,
|
|
2591
|
+
mode, created_by, created_at, updated_at)
|
|
2592
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
|
2593
|
+
ON CONFLICT (id) DO UPDATE SET
|
|
2594
|
+
name = EXCLUDED.name,
|
|
2595
|
+
principal_type = EXCLUDED.principal_type,
|
|
2596
|
+
principal_id = EXCLUDED.principal_id,
|
|
2597
|
+
period = EXCLUDED.period,
|
|
2598
|
+
amount_usd = EXCLUDED.amount_usd,
|
|
2599
|
+
mode = EXCLUDED.mode,
|
|
2600
|
+
updated_at = EXCLUDED.updated_at`, [
|
|
2601
|
+
budget.id,
|
|
2602
|
+
budget.name,
|
|
2603
|
+
budget.principal_type,
|
|
2604
|
+
budget.principal_id,
|
|
2605
|
+
budget.period,
|
|
2606
|
+
budget.amount_usd,
|
|
2607
|
+
budget.mode,
|
|
2608
|
+
budget.created_by,
|
|
2609
|
+
budget.created_at,
|
|
2610
|
+
budget.updated_at,
|
|
2611
|
+
]);
|
|
2612
|
+
}
|
|
2613
|
+
async deleteById(budgetId) {
|
|
2614
|
+
const result = await this.database.query('DELETE FROM usage_budgets WHERE id = $1', [budgetId]);
|
|
2615
|
+
return changes(result.rowCount) > 0;
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
class PostgresMessageFeedbackRepository {
|
|
2619
|
+
constructor(database) {
|
|
2620
|
+
this.database = database;
|
|
2621
|
+
}
|
|
2622
|
+
async upsertByMessage(feedback) {
|
|
2623
|
+
await this.database.query(`INSERT INTO message_feedback
|
|
2624
|
+
(id, user_id, session_id, message_id, rating, tags, comment,
|
|
2625
|
+
model, plugin_id, snapshot, created_at, updated_at)
|
|
2626
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
|
2627
|
+
ON CONFLICT (user_id, message_id) DO UPDATE SET
|
|
2628
|
+
rating = EXCLUDED.rating,
|
|
2629
|
+
tags = EXCLUDED.tags,
|
|
2630
|
+
comment = EXCLUDED.comment,
|
|
2631
|
+
model = EXCLUDED.model,
|
|
2632
|
+
plugin_id = EXCLUDED.plugin_id,
|
|
2633
|
+
snapshot = EXCLUDED.snapshot,
|
|
2634
|
+
updated_at = EXCLUDED.updated_at`, [
|
|
2635
|
+
feedback.id,
|
|
2636
|
+
feedback.user_id,
|
|
2637
|
+
feedback.session_id,
|
|
2638
|
+
feedback.message_id,
|
|
2639
|
+
feedback.rating,
|
|
2640
|
+
feedback.tags,
|
|
2641
|
+
feedback.comment,
|
|
2642
|
+
feedback.model,
|
|
2643
|
+
feedback.plugin_id,
|
|
2644
|
+
feedback.snapshot,
|
|
2645
|
+
feedback.created_at,
|
|
2646
|
+
feedback.updated_at,
|
|
2647
|
+
]);
|
|
2648
|
+
}
|
|
2649
|
+
async deleteByMessage(userId, messageId) {
|
|
2650
|
+
const result = await this.database.query('DELETE FROM message_feedback WHERE user_id = $1 AND message_id = $2', [userId, messageId]);
|
|
2651
|
+
return changes(result.rowCount) > 0;
|
|
2652
|
+
}
|
|
2653
|
+
async listByOwner(userId, maximum) {
|
|
2654
|
+
const result = await this.database.query(`SELECT * FROM message_feedback
|
|
2655
|
+
WHERE user_id = $1
|
|
2656
|
+
ORDER BY created_at DESC, id DESC
|
|
2657
|
+
LIMIT $2`, [userId, maximum]);
|
|
2658
|
+
return result.rows.map(messageFeedback);
|
|
2659
|
+
}
|
|
2660
|
+
async listAll(maximum) {
|
|
2661
|
+
const result = await this.database.query(`SELECT * FROM message_feedback
|
|
2662
|
+
ORDER BY created_at DESC, id DESC
|
|
2663
|
+
LIMIT $1`, [maximum]);
|
|
2664
|
+
return result.rows.map(messageFeedback);
|
|
2665
|
+
}
|
|
2666
|
+
}
|
|
2667
|
+
class PostgresArenaVoteRepository {
|
|
2668
|
+
constructor(database) {
|
|
2669
|
+
this.database = database;
|
|
2670
|
+
}
|
|
2671
|
+
async insertOnce(vote) {
|
|
2672
|
+
const result = await this.database.query(`INSERT INTO arena_votes
|
|
2673
|
+
(id, user_id, compare_group, model_a, model_b, winner, created_at)
|
|
2674
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
2675
|
+
ON CONFLICT (user_id, compare_group) DO NOTHING`, [
|
|
2676
|
+
vote.id,
|
|
2677
|
+
vote.user_id,
|
|
2678
|
+
vote.compare_group,
|
|
2679
|
+
vote.model_a,
|
|
2680
|
+
vote.model_b,
|
|
2681
|
+
vote.winner,
|
|
2682
|
+
vote.created_at,
|
|
2683
|
+
]);
|
|
2684
|
+
return changes(result.rowCount) > 0;
|
|
2685
|
+
}
|
|
2686
|
+
async listAllOrdered(maximum) {
|
|
2687
|
+
const result = await this.database.query(`SELECT * FROM arena_votes
|
|
2688
|
+
ORDER BY created_at ASC, id ASC
|
|
2689
|
+
LIMIT $1`, [maximum]);
|
|
2690
|
+
return result.rows.map(arenaVote);
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
class PostgresEvalSetRepository {
|
|
2694
|
+
constructor(database) {
|
|
2695
|
+
this.database = database;
|
|
2696
|
+
}
|
|
2697
|
+
async listByOwner(userId, maximum) {
|
|
2698
|
+
const result = await this.database.query(`SELECT * FROM eval_sets
|
|
2699
|
+
WHERE user_id = $1
|
|
2700
|
+
ORDER BY updated_at DESC, id DESC
|
|
2701
|
+
LIMIT $2`, [userId, maximum]);
|
|
2702
|
+
return result.rows.map(evalSet);
|
|
2703
|
+
}
|
|
2704
|
+
async findByOwner(setId, userId) {
|
|
2705
|
+
const result = await this.database.query('SELECT * FROM eval_sets WHERE id = $1 AND user_id = $2', [setId, userId]);
|
|
2706
|
+
return result.rows[0] ? evalSet(result.rows[0]) : null;
|
|
2707
|
+
}
|
|
2708
|
+
async replaceWithLimit(value, maximum) {
|
|
2709
|
+
await this.database.transaction(async (client) => {
|
|
2710
|
+
await lockOwner(client, value.user_id);
|
|
2711
|
+
const existing = await client.query('SELECT user_id FROM eval_sets WHERE id = $1 FOR UPDATE', [value.id]);
|
|
2712
|
+
if (existing.rows[0] && existing.rows[0].user_id !== value.user_id) {
|
|
2713
|
+
throw new PersistenceResourceConflictError();
|
|
2714
|
+
}
|
|
2715
|
+
if (!existing.rows[0]) {
|
|
2716
|
+
const count = await client.query('SELECT COUNT(*)::text AS count FROM eval_sets WHERE user_id = $1', [value.user_id]);
|
|
2717
|
+
if (Number(count.rows[0]?.count || 0) >= maximum) {
|
|
2718
|
+
throw new PersistenceResourceLimitError('eval-set', maximum);
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2721
|
+
const result = await client.query(`INSERT INTO eval_sets
|
|
2722
|
+
(id, user_id, name, description, items, created_at, updated_at)
|
|
2723
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
2724
|
+
ON CONFLICT (id) DO UPDATE SET
|
|
2725
|
+
name = EXCLUDED.name,
|
|
2726
|
+
description = EXCLUDED.description,
|
|
2727
|
+
items = EXCLUDED.items,
|
|
2728
|
+
updated_at = EXCLUDED.updated_at
|
|
2729
|
+
WHERE eval_sets.user_id = EXCLUDED.user_id`, [
|
|
2730
|
+
value.id,
|
|
2731
|
+
value.user_id,
|
|
2732
|
+
value.name,
|
|
2733
|
+
value.description,
|
|
2734
|
+
value.items,
|
|
2735
|
+
value.created_at,
|
|
2736
|
+
value.updated_at,
|
|
2737
|
+
]);
|
|
2738
|
+
if (result.rowCount !== 1)
|
|
2739
|
+
throw new PersistenceResourceConflictError();
|
|
2740
|
+
});
|
|
2741
|
+
}
|
|
2742
|
+
async deleteByOwner(setId, userId) {
|
|
2743
|
+
const result = await this.database.query('DELETE FROM eval_sets WHERE id = $1 AND user_id = $2', [setId, userId]);
|
|
2744
|
+
return changes(result.rowCount) > 0;
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
class PostgresEvalRunRepository {
|
|
2748
|
+
constructor(database) {
|
|
2749
|
+
this.database = database;
|
|
2750
|
+
}
|
|
2751
|
+
async listByOwner(userId, maximum) {
|
|
2752
|
+
const result = await this.database.query(`SELECT * FROM eval_runs
|
|
2753
|
+
WHERE user_id = $1
|
|
2754
|
+
ORDER BY created_at DESC, id DESC
|
|
2755
|
+
LIMIT $2`, [userId, maximum]);
|
|
2756
|
+
return result.rows.map(evalRun);
|
|
2757
|
+
}
|
|
2758
|
+
async listBySet(setId, userId, maximum) {
|
|
2759
|
+
const result = await this.database.query(`SELECT * FROM eval_runs
|
|
2760
|
+
WHERE set_id = $1 AND user_id = $2
|
|
2761
|
+
ORDER BY created_at DESC, id DESC
|
|
2762
|
+
LIMIT $3`, [setId, userId, maximum]);
|
|
2763
|
+
return result.rows.map(evalRun);
|
|
2764
|
+
}
|
|
2765
|
+
async findByOwner(runId, userId) {
|
|
2766
|
+
const result = await this.database.query('SELECT * FROM eval_runs WHERE id = $1 AND user_id = $2', [runId, userId]);
|
|
2767
|
+
return result.rows[0] ? evalRun(result.rows[0]) : null;
|
|
2768
|
+
}
|
|
2769
|
+
async insert(run) {
|
|
2770
|
+
await this.database.query(`INSERT INTO eval_runs
|
|
2771
|
+
(id, set_id, user_id, label, plugin_id, model, status, results,
|
|
2772
|
+
error, created_at, updated_at, completed_at)
|
|
2773
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)`, [
|
|
2774
|
+
run.id,
|
|
2775
|
+
run.set_id,
|
|
2776
|
+
run.user_id,
|
|
2777
|
+
run.label,
|
|
2778
|
+
run.plugin_id,
|
|
2779
|
+
run.model,
|
|
2780
|
+
run.status,
|
|
2781
|
+
run.results,
|
|
2782
|
+
run.error,
|
|
2783
|
+
run.created_at,
|
|
2784
|
+
run.updated_at,
|
|
2785
|
+
run.completed_at,
|
|
2786
|
+
]);
|
|
2787
|
+
}
|
|
2788
|
+
async update(runId, userId, updates) {
|
|
2789
|
+
const result = await this.database.query(`UPDATE eval_runs SET
|
|
2790
|
+
status = $1,
|
|
2791
|
+
results = COALESCE($2, results),
|
|
2792
|
+
error = $3,
|
|
2793
|
+
updated_at = $4,
|
|
2794
|
+
completed_at = COALESCE($5, completed_at)
|
|
2795
|
+
WHERE id = $6 AND user_id = $7`, [
|
|
2796
|
+
updates.status,
|
|
2797
|
+
updates.results ?? null,
|
|
2798
|
+
updates.error ?? null,
|
|
2799
|
+
updates.updated_at,
|
|
2800
|
+
updates.completed_at ?? null,
|
|
2801
|
+
runId,
|
|
2802
|
+
userId,
|
|
2803
|
+
]);
|
|
2804
|
+
return changes(result.rowCount) > 0;
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
1030
2807
|
export const createPostgresResourceRepositories = (database) => ({
|
|
1031
2808
|
chatSessions: new PostgresChatSessionRepository(database),
|
|
1032
2809
|
knowledgeCollections: new PostgresKnowledgeCollectionRepository(database),
|
|
1033
2810
|
notes: new PostgresNoteRepository(database),
|
|
2811
|
+
calendars: new PostgresCalendarRepository(database),
|
|
1034
2812
|
calendarEvents: new PostgresCalendarEventRepository(database),
|
|
2813
|
+
channels: new PostgresChannelRepository(database),
|
|
2814
|
+
channelMessages: new PostgresChannelMessageRepository(database),
|
|
2815
|
+
notifications: new PostgresNotificationRepository(database),
|
|
2816
|
+
webhookTargets: new PostgresWebhookTargetRepository(database),
|
|
2817
|
+
pushSubscriptions: new PostgresPushSubscriptionRepository(database),
|
|
2818
|
+
recoveryDrills: new PostgresRecoveryDrillRepository(database),
|
|
1035
2819
|
automations: new PostgresAutomationRepository(database),
|
|
1036
2820
|
automationRuns: new PostgresAutomationRunRepository(database),
|
|
2821
|
+
toolServers: new PostgresToolServerRepository(database),
|
|
2822
|
+
toolServerTools: new PostgresToolServerToolRepository(database),
|
|
2823
|
+
toolServerCredentials: new PostgresToolServerCredentialRepository(database),
|
|
2824
|
+
toolApprovals: new PostgresToolApprovalRepository(database),
|
|
2825
|
+
prompts: new PostgresPromptRepository(database),
|
|
2826
|
+
skills: new PostgresSkillRepository(database),
|
|
2827
|
+
skillFiles: new PostgresSkillFileRepository(database),
|
|
1037
2828
|
sessionFolders: new PostgresSessionFolderRepository(database),
|
|
1038
2829
|
preferences: new PostgresPreferenceRepository(database),
|
|
1039
2830
|
systemSettings: new PostgresSystemSettingRepository(database),
|
|
1040
2831
|
archive: new PostgresDataArchiveRepository(database),
|
|
2832
|
+
modelTariffs: new PostgresModelTariffRepository(database),
|
|
2833
|
+
usageBudgets: new PostgresUsageBudgetRepository(database),
|
|
2834
|
+
messageFeedback: new PostgresMessageFeedbackRepository(database),
|
|
2835
|
+
arenaVotes: new PostgresArenaVoteRepository(database),
|
|
2836
|
+
evalSets: new PostgresEvalSetRepository(database),
|
|
2837
|
+
evalRuns: new PostgresEvalRunRepository(database),
|
|
1041
2838
|
});
|
|
1042
2839
|
/** Create transaction-scoped repositories over one pinned pooled client. */
|
|
1043
2840
|
export const createPostgresTransactionalResourceRepositories = (database, client) => {
|