libre-webui 0.21.3 → 0.22.1
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 +308 -328
- package/backend/dist/cli/migrateSqliteToPostgres.d.ts +2 -0
- package/backend/dist/cli/migrateSqliteToPostgres.d.ts.map +1 -0
- package/backend/dist/cli/migrateSqliteToPostgres.js +113 -0
- package/backend/dist/cli/migrateSqliteToPostgres.js.map +1 -0
- package/backend/dist/cli/recoveryBackup.d.ts +3 -0
- package/backend/dist/cli/recoveryBackup.d.ts.map +1 -0
- package/backend/dist/cli/recoveryBackup.js +251 -0
- package/backend/dist/cli/recoveryBackup.js.map +1 -0
- package/backend/dist/cli/recoveryInventory.d.ts +2 -0
- package/backend/dist/cli/recoveryInventory.d.ts.map +1 -0
- package/backend/dist/cli/recoveryInventory.js +109 -0
- package/backend/dist/cli/recoveryInventory.js.map +1 -0
- package/backend/dist/db.d.ts +27 -0
- package/backend/dist/db.d.ts.map +1 -1
- package/backend/dist/db.js +333 -16
- package/backend/dist/db.js.map +1 -1
- package/backend/dist/env.d.ts.map +1 -1
- package/backend/dist/env.js +12 -2
- package/backend/dist/env.js.map +1 -1
- package/backend/dist/index.d.ts.map +1 -1
- package/backend/dist/index.js +235 -47
- package/backend/dist/index.js.map +1 -1
- package/backend/dist/main.d.ts +2 -0
- package/backend/dist/main.d.ts.map +1 -0
- package/backend/dist/main.js +137 -0
- package/backend/dist/main.js.map +1 -0
- package/backend/dist/middleware/auth.d.ts.map +1 -1
- package/backend/dist/middleware/auth.js +7 -3
- package/backend/dist/middleware/auth.js.map +1 -1
- package/backend/dist/middleware/chatCancellationAdmission.d.ts +7 -0
- package/backend/dist/middleware/chatCancellationAdmission.d.ts.map +1 -0
- package/backend/dist/middleware/chatCancellationAdmission.js +17 -0
- package/backend/dist/middleware/chatCancellationAdmission.js.map +1 -0
- package/backend/dist/middleware/coordinatedRateLimit.d.ts +18 -0
- package/backend/dist/middleware/coordinatedRateLimit.d.ts.map +1 -0
- package/backend/dist/middleware/coordinatedRateLimit.js +52 -0
- package/backend/dist/middleware/coordinatedRateLimit.js.map +1 -0
- package/backend/dist/middleware/sharedRateLimit.d.ts +26 -0
- package/backend/dist/middleware/sharedRateLimit.d.ts.map +1 -0
- package/backend/dist/middleware/sharedRateLimit.js +99 -0
- package/backend/dist/middleware/sharedRateLimit.js.map +1 -0
- package/backend/dist/models/personaModel.d.ts +1 -26
- package/backend/dist/models/personaModel.d.ts.map +1 -1
- package/backend/dist/models/personaModel.js +54 -247
- package/backend/dist/models/personaModel.js.map +1 -1
- package/backend/dist/models/userModel.d.ts +23 -66
- package/backend/dist/models/userModel.d.ts.map +1 -1
- package/backend/dist/models/userModel.js +110 -187
- package/backend/dist/models/userModel.js.map +1 -1
- package/backend/dist/persistence/chatGenerationTypes.d.ts +24 -0
- package/backend/dist/persistence/chatGenerationTypes.d.ts.map +1 -0
- package/backend/dist/persistence/chatGenerationTypes.js +7 -0
- package/backend/dist/persistence/chatGenerationTypes.js.map +1 -0
- package/backend/dist/persistence/extensionTypes.d.ts +185 -0
- package/backend/dist/persistence/extensionTypes.d.ts.map +1 -0
- package/backend/dist/persistence/extensionTypes.js +13 -0
- package/backend/dist/persistence/extensionTypes.js.map +1 -0
- package/backend/dist/persistence/identityDeletionTypes.d.ts +16 -0
- package/backend/dist/persistence/identityDeletionTypes.d.ts.map +1 -0
- package/backend/dist/persistence/identityDeletionTypes.js +7 -0
- package/backend/dist/persistence/identityDeletionTypes.js.map +1 -0
- package/backend/dist/persistence/index.d.ts +48 -0
- package/backend/dist/persistence/index.d.ts.map +1 -0
- package/backend/dist/persistence/index.js +159 -0
- package/backend/dist/persistence/index.js.map +1 -0
- package/backend/dist/persistence/postgresConfig.d.ts +32 -0
- package/backend/dist/persistence/postgresConfig.d.ts.map +1 -0
- package/backend/dist/persistence/postgresConfig.js +91 -0
- package/backend/dist/persistence/postgresConfig.js.map +1 -0
- package/backend/dist/persistence/postgresCoreMigration.d.ts +8 -0
- package/backend/dist/persistence/postgresCoreMigration.d.ts.map +1 -0
- package/backend/dist/persistence/postgresCoreMigration.js +179 -0
- package/backend/dist/persistence/postgresCoreMigration.js.map +1 -0
- package/backend/dist/persistence/postgresDatabase.d.ts +40 -0
- package/backend/dist/persistence/postgresDatabase.d.ts.map +1 -0
- package/backend/dist/persistence/postgresDatabase.js +148 -0
- package/backend/dist/persistence/postgresDatabase.js.map +1 -0
- package/backend/dist/persistence/postgresExtensionMigration.d.ts +4 -0
- package/backend/dist/persistence/postgresExtensionMigration.d.ts.map +1 -0
- package/backend/dist/persistence/postgresExtensionMigration.js +143 -0
- package/backend/dist/persistence/postgresExtensionMigration.js.map +1 -0
- package/backend/dist/persistence/postgresExtensionRepositories.d.ts +6 -0
- package/backend/dist/persistence/postgresExtensionRepositories.d.ts.map +1 -0
- package/backend/dist/persistence/postgresExtensionRepositories.js +550 -0
- package/backend/dist/persistence/postgresExtensionRepositories.js.map +1 -0
- package/backend/dist/persistence/postgresImportState.d.ts +9 -0
- package/backend/dist/persistence/postgresImportState.d.ts.map +1 -0
- package/backend/dist/persistence/postgresImportState.js +64 -0
- package/backend/dist/persistence/postgresImportState.js.map +1 -0
- package/backend/dist/persistence/postgresMigrationRegistry.d.ts +4 -0
- package/backend/dist/persistence/postgresMigrationRegistry.d.ts.map +1 -0
- package/backend/dist/persistence/postgresMigrationRegistry.js +33 -0
- package/backend/dist/persistence/postgresMigrationRegistry.js.map +1 -0
- package/backend/dist/persistence/postgresMigrationTypes.d.ts +24 -0
- package/backend/dist/persistence/postgresMigrationTypes.d.ts.map +1 -0
- package/backend/dist/persistence/postgresMigrationTypes.js +7 -0
- package/backend/dist/persistence/postgresMigrationTypes.js.map +1 -0
- package/backend/dist/persistence/postgresMigrations.d.ts +15 -0
- package/backend/dist/persistence/postgresMigrations.d.ts.map +1 -0
- package/backend/dist/persistence/postgresMigrations.js +333 -0
- package/backend/dist/persistence/postgresMigrations.js.map +1 -0
- package/backend/dist/persistence/postgresPersistence.d.ts +17 -0
- package/backend/dist/persistence/postgresPersistence.d.ts.map +1 -0
- package/backend/dist/persistence/postgresPersistence.js +332 -0
- package/backend/dist/persistence/postgresPersistence.js.map +1 -0
- package/backend/dist/persistence/postgresPluginDefinitionMigration.d.ts +4 -0
- package/backend/dist/persistence/postgresPluginDefinitionMigration.d.ts.map +1 -0
- package/backend/dist/persistence/postgresPluginDefinitionMigration.js +40 -0
- package/backend/dist/persistence/postgresPluginDefinitionMigration.js.map +1 -0
- package/backend/dist/persistence/postgresResourceRepositories.d.ts +7 -0
- package/backend/dist/persistence/postgresResourceRepositories.d.ts.map +1 -0
- package/backend/dist/persistence/postgresResourceRepositories.js +776 -0
- package/backend/dist/persistence/postgresResourceRepositories.js.map +1 -0
- package/backend/dist/persistence/postgresSchemaInspector.d.ts +14 -0
- package/backend/dist/persistence/postgresSchemaInspector.d.ts.map +1 -0
- package/backend/dist/persistence/postgresSchemaInspector.js +817 -0
- package/backend/dist/persistence/postgresSchemaInspector.js.map +1 -0
- package/backend/dist/persistence/resourceTypes.d.ts +207 -0
- package/backend/dist/persistence/resourceTypes.d.ts.map +1 -0
- package/backend/dist/persistence/resourceTypes.js +38 -0
- package/backend/dist/persistence/resourceTypes.js.map +1 -0
- package/backend/dist/persistence/sqliteExtensionRepositories.d.ts +4 -0
- package/backend/dist/persistence/sqliteExtensionRepositories.d.ts.map +1 -0
- package/backend/dist/persistence/sqliteExtensionRepositories.js +494 -0
- package/backend/dist/persistence/sqliteExtensionRepositories.js.map +1 -0
- package/backend/dist/persistence/sqliteMigrations.d.ts +93 -0
- package/backend/dist/persistence/sqliteMigrations.d.ts.map +1 -0
- package/backend/dist/persistence/sqliteMigrations.js +2232 -0
- package/backend/dist/persistence/sqliteMigrations.js.map +1 -0
- package/backend/dist/persistence/sqlitePersistence.d.ts +23 -0
- package/backend/dist/persistence/sqlitePersistence.d.ts.map +1 -0
- package/backend/dist/persistence/sqlitePersistence.js +596 -0
- package/backend/dist/persistence/sqlitePersistence.js.map +1 -0
- package/backend/dist/persistence/sqliteResourceRepositories.d.ts +4 -0
- package/backend/dist/persistence/sqliteResourceRepositories.d.ts.map +1 -0
- package/backend/dist/persistence/sqliteResourceRepositories.js +663 -0
- package/backend/dist/persistence/sqliteResourceRepositories.js.map +1 -0
- package/backend/dist/persistence/sqliteSyncExecutor.d.ts +5 -0
- package/backend/dist/persistence/sqliteSyncExecutor.d.ts.map +1 -0
- package/backend/dist/persistence/sqliteSyncExecutor.js +19 -0
- package/backend/dist/persistence/sqliteSyncExecutor.js.map +1 -0
- package/backend/dist/persistence/sqliteToPostgresMigration.d.ts +80 -0
- package/backend/dist/persistence/sqliteToPostgresMigration.d.ts.map +1 -0
- package/backend/dist/persistence/sqliteToPostgresMigration.js +1543 -0
- package/backend/dist/persistence/sqliteToPostgresMigration.js.map +1 -0
- package/backend/dist/persistence/types.d.ts +131 -0
- package/backend/dist/persistence/types.d.ts.map +1 -0
- package/backend/dist/persistence/types.js +18 -0
- package/backend/dist/persistence/types.js.map +1 -0
- package/backend/dist/persistence/voiceProfileNameLookup.d.ts +9 -0
- package/backend/dist/persistence/voiceProfileNameLookup.d.ts.map +1 -0
- package/backend/dist/persistence/voiceProfileNameLookup.js +15 -0
- package/backend/dist/persistence/voiceProfileNameLookup.js.map +1 -0
- package/backend/dist/platform/coordination/index.d.ts +7 -0
- package/backend/dist/platform/coordination/index.d.ts.map +1 -0
- package/backend/dist/platform/coordination/index.js +24 -0
- package/backend/dist/platform/coordination/index.js.map +1 -0
- package/backend/dist/platform/coordination/localCoordinator.d.ts +49 -0
- package/backend/dist/platform/coordination/localCoordinator.d.ts.map +1 -0
- package/backend/dist/platform/coordination/localCoordinator.js +380 -0
- package/backend/dist/platform/coordination/localCoordinator.js.map +1 -0
- package/backend/dist/platform/coordination/redisCoordinator.d.ts +83 -0
- package/backend/dist/platform/coordination/redisCoordinator.d.ts.map +1 -0
- package/backend/dist/platform/coordination/redisCoordinator.js +726 -0
- package/backend/dist/platform/coordination/redisCoordinator.js.map +1 -0
- package/backend/dist/platform/coordination/service.d.ts +11 -0
- package/backend/dist/platform/coordination/service.d.ts.map +1 -0
- package/backend/dist/platform/coordination/service.js +55 -0
- package/backend/dist/platform/coordination/service.js.map +1 -0
- package/backend/dist/platform/coordination/sharedAdmission.d.ts +53 -0
- package/backend/dist/platform/coordination/sharedAdmission.d.ts.map +1 -0
- package/backend/dist/platform/coordination/sharedAdmission.js +194 -0
- package/backend/dist/platform/coordination/sharedAdmission.js.map +1 -0
- package/backend/dist/platform/coordination/types.d.ts +76 -0
- package/backend/dist/platform/coordination/types.d.ts.map +1 -0
- package/backend/dist/platform/coordination/types.js +32 -0
- package/backend/dist/platform/coordination/types.js.map +1 -0
- package/backend/dist/platform/events/durableEventGateway.d.ts +54 -0
- package/backend/dist/platform/events/durableEventGateway.d.ts.map +1 -0
- package/backend/dist/platform/events/durableEventGateway.js +262 -0
- package/backend/dist/platform/events/durableEventGateway.js.map +1 -0
- package/backend/dist/platform/events/index.d.ts +3 -0
- package/backend/dist/platform/events/index.d.ts.map +1 -0
- package/backend/dist/platform/events/index.js +8 -0
- package/backend/dist/platform/events/index.js.map +1 -0
- package/backend/dist/platform/events/service.d.ts +8 -0
- package/backend/dist/platform/events/service.d.ts.map +1 -0
- package/backend/dist/platform/events/service.js +25 -0
- package/backend/dist/platform/events/service.js.map +1 -0
- package/backend/dist/platform/jobs/chatGenerationEnqueuer.d.ts +3 -0
- package/backend/dist/platform/jobs/chatGenerationEnqueuer.d.ts.map +1 -0
- package/backend/dist/platform/jobs/chatGenerationEnqueuer.js +55 -0
- package/backend/dist/platform/jobs/chatGenerationEnqueuer.js.map +1 -0
- package/backend/dist/platform/jobs/documentIngestionEnqueuer.d.ts +3 -0
- package/backend/dist/platform/jobs/documentIngestionEnqueuer.d.ts.map +1 -0
- package/backend/dist/platform/jobs/documentIngestionEnqueuer.js +38 -0
- package/backend/dist/platform/jobs/documentIngestionEnqueuer.js.map +1 -0
- package/backend/dist/platform/jobs/domainJobContracts.d.ts +30 -0
- package/backend/dist/platform/jobs/domainJobContracts.d.ts.map +1 -0
- package/backend/dist/platform/jobs/domainJobContracts.js +53 -0
- package/backend/dist/platform/jobs/domainJobContracts.js.map +1 -0
- package/backend/dist/platform/jobs/domainJobHandlers.d.ts +11 -0
- package/backend/dist/platform/jobs/domainJobHandlers.d.ts.map +1 -0
- package/backend/dist/platform/jobs/domainJobHandlers.js +617 -0
- package/backend/dist/platform/jobs/domainJobHandlers.js.map +1 -0
- package/backend/dist/platform/jobs/durableEventIdentity.d.ts +8 -0
- package/backend/dist/platform/jobs/durableEventIdentity.d.ts.map +1 -0
- package/backend/dist/platform/jobs/durableEventIdentity.js +33 -0
- package/backend/dist/platform/jobs/durableEventIdentity.js.map +1 -0
- package/backend/dist/platform/jobs/durableJobRuntime.d.ts +57 -0
- package/backend/dist/platform/jobs/durableJobRuntime.d.ts.map +1 -0
- package/backend/dist/platform/jobs/durableJobRuntime.js +131 -0
- package/backend/dist/platform/jobs/durableJobRuntime.js.map +1 -0
- package/backend/dist/platform/jobs/durableJobRuntimeBackend.d.ts +28 -0
- package/backend/dist/platform/jobs/durableJobRuntimeBackend.d.ts.map +1 -0
- package/backend/dist/platform/jobs/durableJobRuntimeBackend.js +62 -0
- package/backend/dist/platform/jobs/durableJobRuntimeBackend.js.map +1 -0
- package/backend/dist/platform/jobs/durableJobService.d.ts +85 -0
- package/backend/dist/platform/jobs/durableJobService.d.ts.map +1 -0
- package/backend/dist/platform/jobs/durableJobService.js +726 -0
- package/backend/dist/platform/jobs/durableJobService.js.map +1 -0
- package/backend/dist/platform/jobs/durableJobTypes.d.ts +195 -0
- package/backend/dist/platform/jobs/durableJobTypes.d.ts.map +1 -0
- package/backend/dist/platform/jobs/durableJobTypes.js +72 -0
- package/backend/dist/platform/jobs/durableJobTypes.js.map +1 -0
- package/backend/dist/platform/jobs/embeddedDurableJobWorker.d.ts +87 -0
- package/backend/dist/platform/jobs/embeddedDurableJobWorker.d.ts.map +1 -0
- package/backend/dist/platform/jobs/embeddedDurableJobWorker.js +429 -0
- package/backend/dist/platform/jobs/embeddedDurableJobWorker.js.map +1 -0
- package/backend/dist/platform/jobs/identityDeletionEnqueuer.d.ts +3 -0
- package/backend/dist/platform/jobs/identityDeletionEnqueuer.d.ts.map +1 -0
- package/backend/dist/platform/jobs/identityDeletionEnqueuer.js +36 -0
- package/backend/dist/platform/jobs/identityDeletionEnqueuer.js.map +1 -0
- package/backend/dist/platform/jobs/index.d.ts +18 -0
- package/backend/dist/platform/jobs/index.d.ts.map +1 -0
- package/backend/dist/platform/jobs/index.js +34 -0
- package/backend/dist/platform/jobs/index.js.map +1 -0
- package/backend/dist/platform/jobs/postgresDurableEventIdempotencyMigration.d.ts +4 -0
- package/backend/dist/platform/jobs/postgresDurableEventIdempotencyMigration.d.ts.map +1 -0
- package/backend/dist/platform/jobs/postgresDurableEventIdempotencyMigration.js +25 -0
- package/backend/dist/platform/jobs/postgresDurableEventIdempotencyMigration.js.map +1 -0
- package/backend/dist/platform/jobs/postgresDurableEventReplayIndexMigration.d.ts +4 -0
- package/backend/dist/platform/jobs/postgresDurableEventReplayIndexMigration.d.ts.map +1 -0
- package/backend/dist/platform/jobs/postgresDurableEventReplayIndexMigration.js +23 -0
- package/backend/dist/platform/jobs/postgresDurableEventReplayIndexMigration.js.map +1 -0
- package/backend/dist/platform/jobs/postgresDurableJobMigration.d.ts +11 -0
- package/backend/dist/platform/jobs/postgresDurableJobMigration.d.ts.map +1 -0
- package/backend/dist/platform/jobs/postgresDurableJobMigration.js +106 -0
- package/backend/dist/platform/jobs/postgresDurableJobMigration.js.map +1 -0
- package/backend/dist/platform/jobs/postgresDurableJobRepository.d.ts +88 -0
- package/backend/dist/platform/jobs/postgresDurableJobRepository.d.ts.map +1 -0
- package/backend/dist/platform/jobs/postgresDurableJobRepository.js +1185 -0
- package/backend/dist/platform/jobs/postgresDurableJobRepository.js.map +1 -0
- package/backend/dist/platform/jobs/postgresDurableJobService.d.ts +86 -0
- package/backend/dist/platform/jobs/postgresDurableJobService.d.ts.map +1 -0
- package/backend/dist/platform/jobs/postgresDurableJobService.js +696 -0
- package/backend/dist/platform/jobs/postgresDurableJobService.js.map +1 -0
- package/backend/dist/platform/jobs/postgresDurableJobWorker.d.ts +53 -0
- package/backend/dist/platform/jobs/postgresDurableJobWorker.d.ts.map +1 -0
- package/backend/dist/platform/jobs/postgresDurableJobWorker.js +393 -0
- package/backend/dist/platform/jobs/postgresDurableJobWorker.js.map +1 -0
- package/backend/dist/platform/jobs/resourceDeletionEnqueuer.d.ts +3 -0
- package/backend/dist/platform/jobs/resourceDeletionEnqueuer.d.ts.map +1 -0
- package/backend/dist/platform/jobs/resourceDeletionEnqueuer.js +43 -0
- package/backend/dist/platform/jobs/resourceDeletionEnqueuer.js.map +1 -0
- package/backend/dist/platform/jobs/sqliteDurableJobRepository.d.ts +166 -0
- package/backend/dist/platform/jobs/sqliteDurableJobRepository.d.ts.map +1 -0
- package/backend/dist/platform/jobs/sqliteDurableJobRepository.js +1008 -0
- package/backend/dist/platform/jobs/sqliteDurableJobRepository.js.map +1 -0
- package/backend/dist/platform/jobs/videoGenerationEnqueuer.d.ts +4 -0
- package/backend/dist/platform/jobs/videoGenerationEnqueuer.d.ts.map +1 -0
- package/backend/dist/platform/jobs/videoGenerationEnqueuer.js +46 -0
- package/backend/dist/platform/jobs/videoGenerationEnqueuer.js.map +1 -0
- package/backend/dist/platform/jobs/workExecutionEnqueuer.d.ts +3 -0
- package/backend/dist/platform/jobs/workExecutionEnqueuer.d.ts.map +1 -0
- package/backend/dist/platform/jobs/workExecutionEnqueuer.js +38 -0
- package/backend/dist/platform/jobs/workExecutionEnqueuer.js.map +1 -0
- package/backend/dist/platform/ollamaRuntimeConfig.d.ts +40 -0
- package/backend/dist/platform/ollamaRuntimeConfig.d.ts.map +1 -0
- package/backend/dist/platform/ollamaRuntimeConfig.js +72 -0
- package/backend/dist/platform/ollamaRuntimeConfig.js.map +1 -0
- package/backend/dist/platform/recovery/backupArchive.d.ts +120 -0
- package/backend/dist/platform/recovery/backupArchive.d.ts.map +1 -0
- package/backend/dist/platform/recovery/backupArchive.js +842 -0
- package/backend/dist/platform/recovery/backupArchive.js.map +1 -0
- package/backend/dist/platform/recovery/index.d.ts +3 -0
- package/backend/dist/platform/recovery/index.d.ts.map +1 -0
- package/backend/dist/platform/recovery/index.js +8 -0
- package/backend/dist/platform/recovery/index.js.map +1 -0
- package/backend/dist/platform/recovery/teamBackupArchive.d.ts +98 -0
- package/backend/dist/platform/recovery/teamBackupArchive.d.ts.map +1 -0
- package/backend/dist/platform/recovery/teamBackupArchive.js +1203 -0
- package/backend/dist/platform/recovery/teamBackupArchive.js.map +1 -0
- package/backend/dist/platform/runtimeConfig.d.ts +70 -0
- package/backend/dist/platform/runtimeConfig.d.ts.map +1 -0
- package/backend/dist/platform/runtimeConfig.js +205 -0
- package/backend/dist/platform/runtimeConfig.js.map +1 -0
- package/backend/dist/platform/storage/aesGcmKeyring.d.ts +26 -0
- package/backend/dist/platform/storage/aesGcmKeyring.d.ts.map +1 -0
- package/backend/dist/platform/storage/aesGcmKeyring.js +135 -0
- package/backend/dist/platform/storage/aesGcmKeyring.js.map +1 -0
- package/backend/dist/platform/storage/blobReferenceRepository.d.ts +36 -0
- package/backend/dist/platform/storage/blobReferenceRepository.d.ts.map +1 -0
- package/backend/dist/platform/storage/blobReferenceRepository.js +117 -0
- package/backend/dist/platform/storage/blobReferenceRepository.js.map +1 -0
- package/backend/dist/platform/storage/blobStore.d.ts +111 -0
- package/backend/dist/platform/storage/blobStore.d.ts.map +1 -0
- package/backend/dist/platform/storage/blobStore.js +46 -0
- package/backend/dist/platform/storage/blobStore.js.map +1 -0
- package/backend/dist/platform/storage/documentVectorIndex.d.ts +44 -0
- package/backend/dist/platform/storage/documentVectorIndex.d.ts.map +1 -0
- package/backend/dist/platform/storage/documentVectorIndex.js +110 -0
- package/backend/dist/platform/storage/documentVectorIndex.js.map +1 -0
- package/backend/dist/platform/storage/durableBlobQuotaPolicy.d.ts +82 -0
- package/backend/dist/platform/storage/durableBlobQuotaPolicy.d.ts.map +1 -0
- package/backend/dist/platform/storage/durableBlobQuotaPolicy.js +583 -0
- package/backend/dist/platform/storage/durableBlobQuotaPolicy.js.map +1 -0
- package/backend/dist/platform/storage/index.d.ts +19 -0
- package/backend/dist/platform/storage/index.d.ts.map +1 -0
- package/backend/dist/platform/storage/index.js +35 -0
- package/backend/dist/platform/storage/index.js.map +1 -0
- package/backend/dist/platform/storage/localEncryptedBlobStore.d.ts +120 -0
- package/backend/dist/platform/storage/localEncryptedBlobStore.d.ts.map +1 -0
- package/backend/dist/platform/storage/localEncryptedBlobStore.js +1205 -0
- package/backend/dist/platform/storage/localEncryptedBlobStore.js.map +1 -0
- package/backend/dist/platform/storage/personaMemoryIndexing.d.ts +12 -0
- package/backend/dist/platform/storage/personaMemoryIndexing.d.ts.map +1 -0
- package/backend/dist/platform/storage/personaMemoryIndexing.js +25 -0
- package/backend/dist/platform/storage/personaMemoryIndexing.js.map +1 -0
- package/backend/dist/platform/storage/pgVectorStore.d.ts +37 -0
- package/backend/dist/platform/storage/pgVectorStore.d.ts.map +1 -0
- package/backend/dist/platform/storage/pgVectorStore.js +493 -0
- package/backend/dist/platform/storage/pgVectorStore.js.map +1 -0
- package/backend/dist/platform/storage/platformDomainRepositories.d.ts +244 -0
- package/backend/dist/platform/storage/platformDomainRepositories.d.ts.map +1 -0
- package/backend/dist/platform/storage/platformDomainRepositories.js +7 -0
- package/backend/dist/platform/storage/platformDomainRepositories.js.map +1 -0
- package/backend/dist/platform/storage/platformStorageRuntime.d.ts +35 -0
- package/backend/dist/platform/storage/platformStorageRuntime.d.ts.map +1 -0
- package/backend/dist/platform/storage/platformStorageRuntime.js +299 -0
- package/backend/dist/platform/storage/platformStorageRuntime.js.map +1 -0
- package/backend/dist/platform/storage/postgresPlatformDomainRepositories.d.ts +4 -0
- package/backend/dist/platform/storage/postgresPlatformDomainRepositories.d.ts.map +1 -0
- package/backend/dist/platform/storage/postgresPlatformDomainRepositories.js +1017 -0
- package/backend/dist/platform/storage/postgresPlatformDomainRepositories.js.map +1 -0
- package/backend/dist/platform/storage/postgresResourceDeletionLifecycleMigration.d.ts +4 -0
- package/backend/dist/platform/storage/postgresResourceDeletionLifecycleMigration.d.ts.map +1 -0
- package/backend/dist/platform/storage/postgresResourceDeletionLifecycleMigration.js +40 -0
- package/backend/dist/platform/storage/postgresResourceDeletionLifecycleMigration.js.map +1 -0
- package/backend/dist/platform/storage/resourceContentCleanup.d.ts +29 -0
- package/backend/dist/platform/storage/resourceContentCleanup.d.ts.map +1 -0
- package/backend/dist/platform/storage/resourceContentCleanup.js +100 -0
- package/backend/dist/platform/storage/resourceContentCleanup.js.map +1 -0
- package/backend/dist/platform/storage/resourceDeletionLifecycle.d.ts +14 -0
- package/backend/dist/platform/storage/resourceDeletionLifecycle.d.ts.map +1 -0
- package/backend/dist/platform/storage/resourceDeletionLifecycle.js +31 -0
- package/backend/dist/platform/storage/resourceDeletionLifecycle.js.map +1 -0
- package/backend/dist/platform/storage/s3EncryptedBlobStore.d.ts +140 -0
- package/backend/dist/platform/storage/s3EncryptedBlobStore.d.ts.map +1 -0
- package/backend/dist/platform/storage/s3EncryptedBlobStore.js +1309 -0
- package/backend/dist/platform/storage/s3EncryptedBlobStore.js.map +1 -0
- package/backend/dist/platform/storage/sqliteEncryptedVectorStore.d.ts +71 -0
- package/backend/dist/platform/storage/sqliteEncryptedVectorStore.d.ts.map +1 -0
- package/backend/dist/platform/storage/sqliteEncryptedVectorStore.js +766 -0
- package/backend/dist/platform/storage/sqliteEncryptedVectorStore.js.map +1 -0
- package/backend/dist/platform/storage/sqlitePlatformDomainRepositories.d.ts +4 -0
- package/backend/dist/platform/storage/sqlitePlatformDomainRepositories.d.ts.map +1 -0
- package/backend/dist/platform/storage/sqlitePlatformDomainRepositories.js +1037 -0
- package/backend/dist/platform/storage/sqlitePlatformDomainRepositories.js.map +1 -0
- package/backend/dist/platform/storage/sqliteToTeamStorageMigration.d.ts +25 -0
- package/backend/dist/platform/storage/sqliteToTeamStorageMigration.d.ts.map +1 -0
- package/backend/dist/platform/storage/sqliteToTeamStorageMigration.js +1292 -0
- package/backend/dist/platform/storage/sqliteToTeamStorageMigration.js.map +1 -0
- package/backend/dist/platform/storage/storageFactory.d.ts +83 -0
- package/backend/dist/platform/storage/storageFactory.d.ts.map +1 -0
- package/backend/dist/platform/storage/storageFactory.js +530 -0
- package/backend/dist/platform/storage/storageFactory.js.map +1 -0
- package/backend/dist/platform/storage/storageSchemaContracts.d.ts +22 -0
- package/backend/dist/platform/storage/storageSchemaContracts.d.ts.map +1 -0
- package/backend/dist/platform/storage/storageSchemaContracts.js +299 -0
- package/backend/dist/platform/storage/storageSchemaContracts.js.map +1 -0
- package/backend/dist/platform/storage/vectorStore.d.ts +117 -0
- package/backend/dist/platform/storage/vectorStore.d.ts.map +1 -0
- package/backend/dist/platform/storage/vectorStore.js +40 -0
- package/backend/dist/platform/storage/vectorStore.js.map +1 -0
- package/backend/dist/platform/workPersistence/index.d.ts +9 -0
- package/backend/dist/platform/workPersistence/index.d.ts.map +1 -0
- package/backend/dist/platform/workPersistence/index.js +14 -0
- package/backend/dist/platform/workPersistence/index.js.map +1 -0
- package/backend/dist/platform/workPersistence/postgresWorkLifecycleMigration.d.ts +9 -0
- package/backend/dist/platform/workPersistence/postgresWorkLifecycleMigration.d.ts.map +1 -0
- package/backend/dist/platform/workPersistence/postgresWorkLifecycleMigration.js +47 -0
- package/backend/dist/platform/workPersistence/postgresWorkLifecycleMigration.js.map +1 -0
- package/backend/dist/platform/workPersistence/postgresWorkMessageContentMigration.d.ts +3 -0
- package/backend/dist/platform/workPersistence/postgresWorkMessageContentMigration.d.ts.map +1 -0
- package/backend/dist/platform/workPersistence/postgresWorkMessageContentMigration.js +28 -0
- package/backend/dist/platform/workPersistence/postgresWorkMessageContentMigration.js.map +1 -0
- package/backend/dist/platform/workPersistence/postgresWorkMigration.d.ts +3 -0
- package/backend/dist/platform/workPersistence/postgresWorkMigration.d.ts.map +1 -0
- package/backend/dist/platform/workPersistence/postgresWorkMigration.js +101 -0
- package/backend/dist/platform/workPersistence/postgresWorkMigration.js.map +1 -0
- package/backend/dist/platform/workPersistence/postgresWorkPersistence.d.ts +79 -0
- package/backend/dist/platform/workPersistence/postgresWorkPersistence.d.ts.map +1 -0
- package/backend/dist/platform/workPersistence/postgresWorkPersistence.js +521 -0
- package/backend/dist/platform/workPersistence/postgresWorkPersistence.js.map +1 -0
- package/backend/dist/platform/workPersistence/service.d.ts +6 -0
- package/backend/dist/platform/workPersistence/service.d.ts.map +1 -0
- package/backend/dist/platform/workPersistence/service.js +27 -0
- package/backend/dist/platform/workPersistence/service.js.map +1 -0
- package/backend/dist/platform/workPersistence/sqliteWorkPersistence.d.ts +77 -0
- package/backend/dist/platform/workPersistence/sqliteWorkPersistence.d.ts.map +1 -0
- package/backend/dist/platform/workPersistence/sqliteWorkPersistence.js +463 -0
- package/backend/dist/platform/workPersistence/sqliteWorkPersistence.js.map +1 -0
- package/backend/dist/platform/workPersistence/types.d.ts +159 -0
- package/backend/dist/platform/workPersistence/types.d.ts.map +1 -0
- package/backend/dist/platform/workPersistence/types.js +13 -0
- package/backend/dist/platform/workPersistence/types.js.map +1 -0
- package/backend/dist/platform/workPersistence/workExecutionTypes.d.ts +13 -0
- package/backend/dist/platform/workPersistence/workExecutionTypes.d.ts.map +1 -0
- package/backend/dist/platform/workPersistence/workExecutionTypes.js +7 -0
- package/backend/dist/platform/workPersistence/workExecutionTypes.js.map +1 -0
- package/backend/dist/platform/workPersistence/workMessageContentCodec.d.ts +8 -0
- package/backend/dist/platform/workPersistence/workMessageContentCodec.d.ts.map +1 -0
- package/backend/dist/platform/workPersistence/workMessageContentCodec.js +25 -0
- package/backend/dist/platform/workPersistence/workMessageContentCodec.js.map +1 -0
- package/backend/dist/platform/workPersistence/workTextSafety.d.ts +3 -0
- package/backend/dist/platform/workPersistence/workTextSafety.d.ts.map +1 -0
- package/backend/dist/platform/workPersistence/workTextSafety.js +8 -0
- package/backend/dist/platform/workPersistence/workTextSafety.js.map +1 -0
- package/backend/dist/routes/agentCli.js +1 -1
- package/backend/dist/routes/agentCli.js.map +1 -1
- package/backend/dist/routes/artifacts.d.ts.map +1 -1
- package/backend/dist/routes/artifacts.js +2 -1
- package/backend/dist/routes/artifacts.js.map +1 -1
- package/backend/dist/routes/auth.d.ts.map +1 -1
- package/backend/dist/routes/auth.js +64 -18
- package/backend/dist/routes/auth.js.map +1 -1
- package/backend/dist/routes/chat.d.ts.map +1 -1
- package/backend/dist/routes/chat.js +519 -85
- package/backend/dist/routes/chat.js.map +1 -1
- package/backend/dist/routes/documents.d.ts.map +1 -1
- package/backend/dist/routes/documents.js +134 -28
- package/backend/dist/routes/documents.js.map +1 -1
- package/backend/dist/routes/embeddings.d.ts.map +1 -1
- package/backend/dist/routes/embeddings.js +2 -1
- package/backend/dist/routes/embeddings.js.map +1 -1
- package/backend/dist/routes/health.d.ts +3 -0
- package/backend/dist/routes/health.d.ts.map +1 -0
- package/backend/dist/routes/health.js +52 -0
- package/backend/dist/routes/health.js.map +1 -0
- package/backend/dist/routes/huggingfaceHub.d.ts.map +1 -1
- package/backend/dist/routes/huggingfaceHub.js +2 -1
- package/backend/dist/routes/huggingfaceHub.js.map +1 -1
- package/backend/dist/routes/imageGen.d.ts.map +1 -1
- package/backend/dist/routes/imageGen.js +42 -10
- package/backend/dist/routes/imageGen.js.map +1 -1
- package/backend/dist/routes/jobs.d.ts +3 -0
- package/backend/dist/routes/jobs.d.ts.map +1 -0
- package/backend/dist/routes/jobs.js +126 -0
- package/backend/dist/routes/jobs.js.map +1 -0
- package/backend/dist/routes/libreClaw.d.ts.map +1 -1
- package/backend/dist/routes/libreClaw.js +10 -7
- package/backend/dist/routes/libreClaw.js.map +1 -1
- package/backend/dist/routes/media.d.ts.map +1 -1
- package/backend/dist/routes/media.js +523 -77
- 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 +21 -21
- package/backend/dist/routes/notes.js.map +1 -1
- package/backend/dist/routes/ollama.d.ts.map +1 -1
- package/backend/dist/routes/ollama.js +40 -12
- package/backend/dist/routes/ollama.js.map +1 -1
- package/backend/dist/routes/personas.d.ts.map +1 -1
- package/backend/dist/routes/personas.js +4 -2
- package/backend/dist/routes/personas.js.map +1 -1
- package/backend/dist/routes/plugins.d.ts.map +1 -1
- package/backend/dist/routes/plugins.js +79 -51
- package/backend/dist/routes/plugins.js.map +1 -1
- package/backend/dist/routes/preferences.d.ts.map +1 -1
- package/backend/dist/routes/preferences.js +290 -25
- package/backend/dist/routes/preferences.js.map +1 -1
- package/backend/dist/routes/search.d.ts.map +1 -1
- package/backend/dist/routes/search.js +23 -14
- package/backend/dist/routes/search.js.map +1 -1
- package/backend/dist/routes/stt.d.ts +9 -0
- package/backend/dist/routes/stt.d.ts.map +1 -0
- package/backend/dist/routes/stt.js +217 -0
- package/backend/dist/routes/stt.js.map +1 -0
- package/backend/dist/routes/systemDiagnostics.d.ts.map +1 -1
- package/backend/dist/routes/systemDiagnostics.js +2 -1
- package/backend/dist/routes/systemDiagnostics.js.map +1 -1
- package/backend/dist/routes/tts.d.ts.map +1 -1
- package/backend/dist/routes/tts.js +577 -28
- 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 +16 -25
- package/backend/dist/routes/users.js.map +1 -1
- package/backend/dist/routes/work.d.ts.map +1 -1
- package/backend/dist/routes/work.js +167 -113
- package/backend/dist/routes/work.js.map +1 -1
- package/backend/dist/services/agentAccessService.d.ts +2 -2
- package/backend/dist/services/agentAccessService.d.ts.map +1 -1
- package/backend/dist/services/agentAccessService.js +5 -14
- package/backend/dist/services/agentAccessService.js.map +1 -1
- package/backend/dist/services/agentCliService.d.ts +3 -2
- package/backend/dist/services/agentCliService.d.ts.map +1 -1
- package/backend/dist/services/agentCliService.js +44 -16
- package/backend/dist/services/agentCliService.js.map +1 -1
- package/backend/dist/services/assistantCompletionService.d.ts +2 -1
- package/backend/dist/services/assistantCompletionService.d.ts.map +1 -1
- package/backend/dist/services/assistantCompletionService.js +7 -3
- package/backend/dist/services/assistantCompletionService.js.map +1 -1
- package/backend/dist/services/authService.d.ts +7 -2
- package/backend/dist/services/authService.d.ts.map +1 -1
- package/backend/dist/services/authService.js +23 -10
- package/backend/dist/services/authService.js.map +1 -1
- package/backend/dist/services/chatGenerationService.d.ts +5 -4
- package/backend/dist/services/chatGenerationService.d.ts.map +1 -1
- package/backend/dist/services/chatGenerationService.js +23 -17
- package/backend/dist/services/chatGenerationService.js.map +1 -1
- package/backend/dist/services/chatRequestService.d.ts +4 -3
- package/backend/dist/services/chatRequestService.d.ts.map +1 -1
- package/backend/dist/services/chatRequestService.js +3 -3
- package/backend/dist/services/chatRequestService.js.map +1 -1
- package/backend/dist/services/chatService.d.ts +66 -16
- package/backend/dist/services/chatService.d.ts.map +1 -1
- package/backend/dist/services/chatService.js +402 -111
- package/backend/dist/services/chatService.js.map +1 -1
- package/backend/dist/services/codexOAuthService.d.ts +2 -2
- package/backend/dist/services/codexOAuthService.d.ts.map +1 -1
- package/backend/dist/services/codexOAuthService.js +63 -7
- package/backend/dist/services/codexOAuthService.js.map +1 -1
- package/backend/dist/services/dataArchiveService.d.ts +88 -0
- package/backend/dist/services/dataArchiveService.d.ts.map +1 -0
- package/backend/dist/services/dataArchiveService.js +1027 -0
- package/backend/dist/services/dataArchiveService.js.map +1 -0
- package/backend/dist/services/documentService.d.ts +67 -14
- package/backend/dist/services/documentService.d.ts.map +1 -1
- package/backend/dist/services/documentService.js +938 -193
- package/backend/dist/services/documentService.js.map +1 -1
- package/backend/dist/services/durableChatCompletion.d.ts +16 -0
- package/backend/dist/services/durableChatCompletion.d.ts.map +1 -0
- package/backend/dist/services/durableChatCompletion.js +34 -0
- package/backend/dist/services/durableChatCompletion.js.map +1 -0
- package/backend/dist/services/durableChatGenerationService.d.ts +19 -0
- package/backend/dist/services/durableChatGenerationService.d.ts.map +1 -0
- package/backend/dist/services/durableChatGenerationService.js +341 -0
- package/backend/dist/services/durableChatGenerationService.js.map +1 -0
- package/backend/dist/services/embeddingService.d.ts +1 -1
- package/backend/dist/services/embeddingService.d.ts.map +1 -1
- package/backend/dist/services/embeddingService.js +16 -13
- package/backend/dist/services/embeddingService.js.map +1 -1
- package/backend/dist/services/encryptionService.d.ts +28 -1
- package/backend/dist/services/encryptionService.d.ts.map +1 -1
- package/backend/dist/services/encryptionService.js +131 -51
- package/backend/dist/services/encryptionService.js.map +1 -1
- package/backend/dist/services/followUpService.d.ts +1 -1
- package/backend/dist/services/followUpService.d.ts.map +1 -1
- package/backend/dist/services/followUpService.js +1 -1
- package/backend/dist/services/followUpService.js.map +1 -1
- package/backend/dist/services/galleryService.d.ts +38 -37
- package/backend/dist/services/galleryService.d.ts.map +1 -1
- package/backend/dist/services/galleryService.js +356 -251
- package/backend/dist/services/galleryService.js.map +1 -1
- package/backend/dist/services/healthService.d.ts +65 -0
- package/backend/dist/services/healthService.d.ts.map +1 -0
- package/backend/dist/services/healthService.js +379 -0
- package/backend/dist/services/healthService.js.map +1 -0
- package/backend/dist/services/legacyCiphertextIntegrity.d.ts +40 -0
- package/backend/dist/services/legacyCiphertextIntegrity.d.ts.map +1 -0
- package/backend/dist/services/legacyCiphertextIntegrity.js +406 -0
- package/backend/dist/services/legacyCiphertextIntegrity.js.map +1 -0
- package/backend/dist/services/mediaGenerationJobService.d.ts +27 -5
- package/backend/dist/services/mediaGenerationJobService.d.ts.map +1 -1
- package/backend/dist/services/mediaGenerationJobService.js +185 -55
- package/backend/dist/services/mediaGenerationJobService.js.map +1 -1
- package/backend/dist/services/memoryService.d.ts +7 -83
- package/backend/dist/services/memoryService.d.ts.map +1 -1
- package/backend/dist/services/memoryService.js +428 -573
- package/backend/dist/services/memoryService.js.map +1 -1
- package/backend/dist/services/memoryUtils.d.ts.map +1 -1
- package/backend/dist/services/memoryUtils.js +8 -1
- package/backend/dist/services/memoryUtils.js.map +1 -1
- package/backend/dist/services/modelAccessService.d.ts +3 -3
- package/backend/dist/services/modelAccessService.d.ts.map +1 -1
- package/backend/dist/services/modelAccessService.js +8 -16
- package/backend/dist/services/modelAccessService.js.map +1 -1
- package/backend/dist/services/mutationEngineService.d.ts +0 -7
- package/backend/dist/services/mutationEngineService.d.ts.map +1 -1
- package/backend/dist/services/mutationEngineService.js +7 -78
- package/backend/dist/services/mutationEngineService.js.map +1 -1
- package/backend/dist/services/ollamaService.d.ts +4 -4
- package/backend/dist/services/ollamaService.d.ts.map +1 -1
- package/backend/dist/services/ollamaService.js +62 -31
- package/backend/dist/services/ollamaService.js.map +1 -1
- package/backend/dist/services/personaService.d.ts.map +1 -1
- package/backend/dist/services/personaService.js +19 -54
- package/backend/dist/services/personaService.js.map +1 -1
- package/backend/dist/services/pluginActivationService.d.ts +6 -5
- package/backend/dist/services/pluginActivationService.d.ts.map +1 -1
- package/backend/dist/services/pluginActivationService.js +36 -76
- package/backend/dist/services/pluginActivationService.js.map +1 -1
- package/backend/dist/services/pluginAudioGenerationService.d.ts +11 -6
- package/backend/dist/services/pluginAudioGenerationService.d.ts.map +1 -1
- package/backend/dist/services/pluginAudioGenerationService.js +74 -16
- package/backend/dist/services/pluginAudioGenerationService.js.map +1 -1
- package/backend/dist/services/pluginCacheInvalidation.d.ts +38 -0
- package/backend/dist/services/pluginCacheInvalidation.d.ts.map +1 -0
- package/backend/dist/services/pluginCacheInvalidation.js +198 -0
- package/backend/dist/services/pluginCacheInvalidation.js.map +1 -0
- package/backend/dist/services/pluginCapabilityRegistryService.d.ts +3 -3
- package/backend/dist/services/pluginCapabilityRegistryService.d.ts.map +1 -1
- package/backend/dist/services/pluginCapabilityRegistryService.js +11 -7
- package/backend/dist/services/pluginCapabilityRegistryService.js.map +1 -1
- package/backend/dist/services/pluginCredentialsService.d.ts +9 -8
- package/backend/dist/services/pluginCredentialsService.d.ts.map +1 -1
- package/backend/dist/services/pluginCredentialsService.js +48 -89
- package/backend/dist/services/pluginCredentialsService.js.map +1 -1
- package/backend/dist/services/pluginEmbeddingService.d.ts +8 -7
- package/backend/dist/services/pluginEmbeddingService.d.ts.map +1 -1
- package/backend/dist/services/pluginEmbeddingService.js +13 -11
- package/backend/dist/services/pluginEmbeddingService.js.map +1 -1
- package/backend/dist/services/pluginImageGenerationService.d.ts +10 -8
- package/backend/dist/services/pluginImageGenerationService.d.ts.map +1 -1
- package/backend/dist/services/pluginImageGenerationService.js +119 -14
- package/backend/dist/services/pluginImageGenerationService.js.map +1 -1
- package/backend/dist/services/pluginSTTService.d.ts +47 -0
- package/backend/dist/services/pluginSTTService.d.ts.map +1 -0
- package/backend/dist/services/pluginSTTService.js +199 -0
- package/backend/dist/services/pluginSTTService.js.map +1 -0
- package/backend/dist/services/pluginService.d.ts +94 -40
- package/backend/dist/services/pluginService.d.ts.map +1 -1
- package/backend/dist/services/pluginService.js +833 -275
- package/backend/dist/services/pluginService.js.map +1 -1
- package/backend/dist/services/pluginTTSService.d.ts +26 -8
- package/backend/dist/services/pluginTTSService.d.ts.map +1 -1
- package/backend/dist/services/pluginTTSService.js +277 -25
- package/backend/dist/services/pluginTTSService.js.map +1 -1
- package/backend/dist/services/pluginUsageService.d.ts +4 -4
- package/backend/dist/services/pluginUsageService.d.ts.map +1 -1
- package/backend/dist/services/pluginUsageService.js +44 -93
- package/backend/dist/services/pluginUsageService.js.map +1 -1
- package/backend/dist/services/pluginVariablesService.d.ts +14 -7
- package/backend/dist/services/pluginVariablesService.d.ts.map +1 -1
- package/backend/dist/services/pluginVariablesService.js +116 -81
- package/backend/dist/services/pluginVariablesService.js.map +1 -1
- package/backend/dist/services/pluginVideoGenerationService.d.ts +19 -8
- package/backend/dist/services/pluginVideoGenerationService.d.ts.map +1 -1
- package/backend/dist/services/pluginVideoGenerationService.js +116 -20
- package/backend/dist/services/pluginVideoGenerationService.js.map +1 -1
- package/backend/dist/services/preferencesService.d.ts +31 -21
- package/backend/dist/services/preferencesService.d.ts.map +1 -1
- package/backend/dist/services/preferencesService.js +104 -102
- package/backend/dist/services/preferencesService.js.map +1 -1
- package/backend/dist/services/recoveryInventoryService.d.ts +244 -0
- package/backend/dist/services/recoveryInventoryService.d.ts.map +1 -0
- package/backend/dist/services/recoveryInventoryService.js +1436 -0
- package/backend/dist/services/recoveryInventoryService.js.map +1 -0
- package/backend/dist/services/simpleGitHubOAuth.js +3 -3
- package/backend/dist/services/simpleGitHubOAuth.js.map +1 -1
- package/backend/dist/services/simpleHuggingFaceOAuth.d.ts.map +1 -1
- package/backend/dist/services/simpleHuggingFaceOAuth.js +3 -3
- package/backend/dist/services/simpleHuggingFaceOAuth.js.map +1 -1
- package/backend/dist/services/systemDiagnosticsService.d.ts.map +1 -1
- package/backend/dist/services/systemDiagnosticsService.js +2 -2
- package/backend/dist/services/systemDiagnosticsService.js.map +1 -1
- package/backend/dist/services/systemSettingsService.d.ts +5 -0
- package/backend/dist/services/systemSettingsService.d.ts.map +1 -0
- package/backend/dist/services/systemSettingsService.js +24 -0
- package/backend/dist/services/systemSettingsService.js.map +1 -0
- package/backend/dist/services/titleGenerationService.d.ts +1 -1
- package/backend/dist/services/titleGenerationService.d.ts.map +1 -1
- package/backend/dist/services/titleGenerationService.js +1 -1
- package/backend/dist/services/titleGenerationService.js.map +1 -1
- package/backend/dist/services/voiceProfileService.d.ts +44 -0
- package/backend/dist/services/voiceProfileService.d.ts.map +1 -0
- package/backend/dist/services/voiceProfileService.js +195 -0
- package/backend/dist/services/voiceProfileService.js.map +1 -0
- package/backend/dist/services/webSearchService.d.ts +7 -7
- package/backend/dist/services/webSearchService.d.ts.map +1 -1
- package/backend/dist/services/webSearchService.js +40 -35
- package/backend/dist/services/webSearchService.js.map +1 -1
- package/backend/dist/services/websocketTicketService.d.ts +39 -0
- package/backend/dist/services/websocketTicketService.d.ts.map +1 -0
- package/backend/dist/services/websocketTicketService.js +183 -0
- package/backend/dist/services/websocketTicketService.js.map +1 -0
- package/backend/dist/services/workAccessService.d.ts +3 -3
- package/backend/dist/services/workAccessService.d.ts.map +1 -1
- package/backend/dist/services/workAccessService.js +8 -16
- package/backend/dist/services/workAccessService.js.map +1 -1
- package/backend/dist/services/workAdminService.d.ts +3 -3
- package/backend/dist/services/workAdminService.d.ts.map +1 -1
- package/backend/dist/services/workAdminService.js +4 -4
- package/backend/dist/services/workAdminService.js.map +1 -1
- package/backend/dist/services/workAgentService.d.ts +25 -1
- package/backend/dist/services/workAgentService.d.ts.map +1 -1
- package/backend/dist/services/workAgentService.js +294 -126
- package/backend/dist/services/workAgentService.js.map +1 -1
- package/backend/dist/services/workEventService.d.ts +15 -2
- package/backend/dist/services/workEventService.d.ts.map +1 -1
- package/backend/dist/services/workEventService.js +201 -3
- package/backend/dist/services/workEventService.js.map +1 -1
- package/backend/dist/services/workKubernetesDriver.d.ts +8 -7
- package/backend/dist/services/workKubernetesDriver.d.ts.map +1 -1
- package/backend/dist/services/workKubernetesDriver.js +65 -21
- package/backend/dist/services/workKubernetesDriver.js.map +1 -1
- package/backend/dist/services/workModelProviderService.d.ts +2 -2
- package/backend/dist/services/workModelProviderService.d.ts.map +1 -1
- package/backend/dist/services/workModelProviderService.js +38 -34
- package/backend/dist/services/workModelProviderService.js.map +1 -1
- package/backend/dist/services/workPolicyService.d.ts +7 -7
- package/backend/dist/services/workPolicyService.d.ts.map +1 -1
- package/backend/dist/services/workPolicyService.js +50 -51
- package/backend/dist/services/workPolicyService.js.map +1 -1
- package/backend/dist/services/workPreviewProxyService.d.ts +8 -8
- package/backend/dist/services/workPreviewProxyService.d.ts.map +1 -1
- package/backend/dist/services/workPreviewProxyService.js +47 -44
- package/backend/dist/services/workPreviewProxyService.js.map +1 -1
- package/backend/dist/services/workRuntimeDriver.d.ts +21 -18
- package/backend/dist/services/workRuntimeDriver.d.ts.map +1 -1
- package/backend/dist/services/workRuntimeDriver.js +69 -20
- package/backend/dist/services/workRuntimeDriver.js.map +1 -1
- package/backend/dist/services/workRuntimeService.d.ts +18 -13
- package/backend/dist/services/workRuntimeService.d.ts.map +1 -1
- package/backend/dist/services/workRuntimeService.js +438 -194
- package/backend/dist/services/workRuntimeService.js.map +1 -1
- package/backend/dist/services/workRuntimeShared.d.ts +2 -0
- package/backend/dist/services/workRuntimeShared.d.ts.map +1 -1
- package/backend/dist/services/workRuntimeShared.js.map +1 -1
- package/backend/dist/services/workTaskService.d.ts +42 -36
- package/backend/dist/services/workTaskService.d.ts.map +1 -1
- package/backend/dist/services/workTaskService.js +445 -350
- package/backend/dist/services/workTaskService.js.map +1 -1
- package/backend/dist/services/workTerminalService.d.ts +1 -1
- package/backend/dist/services/workTerminalService.d.ts.map +1 -1
- package/backend/dist/services/workTerminalService.js +33 -6
- package/backend/dist/services/workTerminalService.js.map +1 -1
- package/backend/dist/storage.d.ts +72 -40
- package/backend/dist/storage.d.ts.map +1 -1
- package/backend/dist/storage.js +312 -660
- package/backend/dist/storage.js.map +1 -1
- package/backend/dist/types/index.d.ts +28 -0
- package/backend/dist/types/index.d.ts.map +1 -1
- package/backend/dist/types/index.js.map +1 -1
- package/backend/dist/types/work.d.ts +7 -2
- package/backend/dist/types/work.d.ts.map +1 -1
- package/backend/dist/utils/chatCancellation.d.ts +48 -0
- package/backend/dist/utils/chatCancellation.d.ts.map +1 -0
- package/backend/dist/utils/chatCancellation.js +152 -0
- package/backend/dist/utils/chatCancellation.js.map +1 -0
- package/backend/dist/utils/chatDocumentContext.d.ts +1 -1
- package/backend/dist/utils/chatDocumentContext.d.ts.map +1 -1
- package/backend/dist/utils/chatDocumentContext.js +11 -5
- package/backend/dist/utils/chatDocumentContext.js.map +1 -1
- package/backend/dist/utils/chatStreamCoalescer.d.ts +15 -0
- package/backend/dist/utils/chatStreamCoalescer.d.ts.map +1 -0
- package/backend/dist/utils/chatStreamCoalescer.js +59 -0
- package/backend/dist/utils/chatStreamCoalescer.js.map +1 -0
- package/backend/dist/utils/dataDirectory.d.ts +49 -0
- package/backend/dist/utils/dataDirectory.d.ts.map +1 -0
- package/backend/dist/utils/dataDirectory.js +291 -0
- package/backend/dist/utils/dataDirectory.js.map +1 -0
- package/backend/dist/utils/generationUtils.d.ts.map +1 -1
- package/backend/dist/utils/generationUtils.js +18 -6
- package/backend/dist/utils/generationUtils.js.map +1 -1
- package/backend/dist/utils/jwt.d.ts.map +1 -1
- package/backend/dist/utils/jwt.js +2 -3
- package/backend/dist/utils/jwt.js.map +1 -1
- package/backend/dist/utils/ollamaModelDefaults.d.ts.map +1 -1
- package/backend/dist/utils/ollamaModelDefaults.js +2 -7
- package/backend/dist/utils/ollamaModelDefaults.js.map +1 -1
- package/backend/dist/utils/ollamaStreaming.d.ts +2 -1
- package/backend/dist/utils/ollamaStreaming.d.ts.map +1 -1
- package/backend/dist/utils/ollamaStreaming.js +49 -11
- package/backend/dist/utils/ollamaStreaming.js.map +1 -1
- package/backend/dist/utils/pluginConnectionVariables.d.ts.map +1 -1
- package/backend/dist/utils/pluginConnectionVariables.js +11 -3
- package/backend/dist/utils/pluginConnectionVariables.js.map +1 -1
- package/backend/dist/utils/pluginDefinitionTrust.d.ts.map +1 -1
- package/backend/dist/utils/pluginDefinitionTrust.js +4 -3
- package/backend/dist/utils/pluginDefinitionTrust.js.map +1 -1
- package/backend/dist/utils/pluginStreaming.d.ts +2 -1
- package/backend/dist/utils/pluginStreaming.d.ts.map +1 -1
- package/backend/dist/utils/pluginStreaming.js +5 -1
- package/backend/dist/utils/pluginStreaming.js.map +1 -1
- package/backend/dist/utils/pluginUpload.d.ts +1 -0
- package/backend/dist/utils/pluginUpload.d.ts.map +1 -1
- package/backend/dist/utils/pluginUpload.js +14 -4
- package/backend/dist/utils/pluginUpload.js.map +1 -1
- package/backend/dist/utils/pluginValidation.d.ts.map +1 -1
- package/backend/dist/utils/pluginValidation.js +29 -1
- package/backend/dist/utils/pluginValidation.js.map +1 -1
- package/backend/dist/utils/sttAudioUpload.d.ts +24 -0
- package/backend/dist/utils/sttAudioUpload.d.ts.map +1 -0
- package/backend/dist/utils/sttAudioUpload.js +500 -0
- package/backend/dist/utils/sttAudioUpload.js.map +1 -0
- package/backend/dist/utils/ttsVoiceCloneUpload.d.ts +34 -0
- package/backend/dist/utils/ttsVoiceCloneUpload.d.ts.map +1 -0
- package/backend/dist/utils/ttsVoiceCloneUpload.js +224 -0
- package/backend/dist/utils/ttsVoiceCloneUpload.js.map +1 -0
- package/backend/dist/utils/websocketMessages.d.ts +2 -1
- package/backend/dist/utils/websocketMessages.d.ts.map +1 -1
- package/backend/dist/utils/websocketMessages.js +16 -2
- package/backend/dist/utils/websocketMessages.js.map +1 -1
- package/backend/dist/websocketServer.d.ts +4 -1
- package/backend/dist/websocketServer.d.ts.map +1 -1
- package/backend/dist/websocketServer.js +290 -44
- package/backend/dist/websocketServer.js.map +1 -1
- package/backend/dist/workTerminalServer.d.ts +9 -0
- package/backend/dist/workTerminalServer.d.ts.map +1 -1
- package/backend/dist/workTerminalServer.js +221 -52
- package/backend/dist/workTerminalServer.js.map +1 -1
- package/backend/dist/worker.d.ts +2 -0
- package/backend/dist/worker.d.ts.map +1 -0
- package/backend/dist/worker.js +250 -0
- package/backend/dist/worker.js.map +1 -0
- package/backend/package.json +19 -9
- package/bin/cli.js +126 -99
- package/bin/runtime-paths.js +108 -0
- package/frontend/dist/artifact-runtime/.build-fingerprint +1 -1
- package/frontend/dist/artifact-runtime/framer-motion.js +5 -5
- package/frontend/dist/artifact-runtime/lucide-react.js +1 -1
- package/frontend/dist/assets/index-Jtx9Amhz.js +16 -0
- package/frontend/dist/assets/index-kxtZQLV6.css +1 -0
- package/frontend/dist/assets/{markdown-math-ClQbWekD.css → markdown-math-ClTedVi7.css} +1 -1
- package/frontend/dist/index.html +18 -18
- package/frontend/dist/js/{ArtifactContainer-Dy2Q0iIL.js → ArtifactContainer-fZegtmva.js} +1 -1
- package/frontend/dist/js/{ArtifactDemoPage-K79dWshB.js → ArtifactDemoPage-BDBXYv9l.js} +1 -1
- package/frontend/dist/js/{ArtifactSandboxFrame-DhqLNUtY.js → ArtifactSandboxFrame-BN16NtKe.js} +3 -3
- package/frontend/dist/js/{ArtifactSlideOutPanel-Cl8JG7bz.js → ArtifactSlideOutPanel-x1fS85Ww.js} +1 -1
- package/frontend/dist/js/{Button-CTXHujOd.js → Button-BV44cTnj.js} +1 -1
- package/frontend/dist/js/ChatPage-C_pXh7WN.js +23 -0
- package/frontend/dist/js/{FirstTimeSetup-kHhRzGJ8.js → FirstTimeSetup-oRrlZnBm.js} +1 -1
- package/frontend/dist/js/GalleryPage-CV_vbE7X.js +1 -0
- package/frontend/dist/js/{HomePage-C1L8WAXL.js → HomePage-P-e7Huco.js} +1 -1
- package/frontend/dist/js/{LibreClawPage-Dyua8IUu.js → LibreClawPage-Cee06SUM.js} +1 -1
- package/frontend/dist/js/{ModelManagerModals-CuMRGUd-.js → ModelManagerModals-D7jIT3v1.js} +1 -1
- package/frontend/dist/js/{ModelSelector-CqHc-Ln4.js → ModelSelector-DXucbvrz.js} +1 -1
- package/frontend/dist/js/ModelsPage-C3_fZpMM.js +2 -0
- package/frontend/dist/js/NotesPage-rEIPWGIb.js +1 -0
- package/frontend/dist/js/{OptimizedSyntaxHighlighter-CWYUn9Oe.js → OptimizedSyntaxHighlighter-XwqZEoTf.js} +1 -1
- package/frontend/dist/js/{PersonasPage-D_6WBCH5.js → PersonasPage-BXtepFuD.js} +2 -2
- package/frontend/dist/js/PluginUsagePage-hhu8cxsu.js +1 -0
- package/frontend/dist/js/RichMessageContent-DkiEvdSg.js +3 -0
- package/frontend/dist/js/SettingsModal-BNxCtJ51.js +1 -0
- package/frontend/dist/js/{SettingsToggle-DLHlCDBl.js → SettingsToggle-WTa0rgvY.js} +1 -1
- package/frontend/dist/js/{SystemPage-BMWEet9e.js → SystemPage-BvvKFuoG.js} +1 -1
- package/frontend/dist/js/UserManagementPage-BccyR7si.js +1 -0
- package/frontend/dist/js/WorkPage-BgxrvY3I.js +6 -0
- package/frontend/dist/js/api-BvPDxsUQ.js +14 -0
- package/frontend/dist/js/appStore-DRSSjaHn.js +2 -0
- package/frontend/dist/js/ar-xTSvTq05.js +2 -0
- package/frontend/dist/js/authStore-BVykIRec.js +4 -0
- package/frontend/dist/js/{babel-CmENXdZw.js → babel-D24oDI9y.js} +1 -1
- package/frontend/dist/js/bn-iYAsohEA.js +2 -0
- package/frontend/dist/js/chatStore-UPp5TV_t.js +3 -0
- package/frontend/dist/js/{config-DkV1ka7I.js → config-BTlPm-cZ.js} +1 -1
- package/frontend/dist/js/cs-YBHN88Hb.js +2 -0
- package/frontend/dist/js/da-D7qlLy_k.js +2 -0
- package/frontend/dist/js/de-DV671k2c.js +2 -0
- package/frontend/dist/js/es-kyBgzNq-.js +2 -0
- package/frontend/dist/js/{estree-D5KP-qbw.js → estree-CfYahF_d.js} +1 -1
- package/frontend/dist/js/fr-Cw1vut7I.js +2 -0
- package/frontend/dist/js/hi-DWEFC024.js +2 -0
- package/frontend/dist/js/{html-Cm9c2akd.js → html-BJsDX1UZ.js} +1 -1
- package/frontend/dist/js/id-DlMHjjy0.js +2 -0
- package/frontend/dist/js/is-B9E_a7dG.js +2 -0
- package/frontend/dist/js/it-DawmVbLC.js +2 -0
- package/frontend/dist/js/ja-DmVvwY3W.js +2 -0
- package/frontend/dist/js/ko-DzOPpkVs.js +2 -0
- package/frontend/dist/js/{markdown-B2u7CnKN.js → markdown-GLyuUNig.js} +1 -1
- package/frontend/dist/js/markdown-core-BmPPZiuD.js +31 -0
- package/frontend/dist/js/{markdown-math-B7FlYsmr.js → markdown-math-mH7AZ_mb.js} +6 -6
- package/frontend/dist/js/{messageCodeStyles-DalG2j6U.js → messageCodeStyles-BHddEFP1.js} +1 -1
- package/frontend/dist/js/ms-DpsaOPht.js +2 -0
- package/frontend/dist/js/nl-B1d8W22n.js +2 -0
- package/frontend/dist/js/pl-nCncRVgt.js +2 -0
- package/frontend/dist/js/{postcss-B5WIGhAY.js → postcss-Bx9CTcIc.js} +1 -1
- package/frontend/dist/js/pt-DHw8fGuu.js +2 -0
- package/frontend/dist/js/{react-vendor-DPNXrO-8.js → react-vendor-C5yShNlM.js} +1 -1
- package/frontend/dist/js/{router-vendor-Ca9zqOqT.js → router-vendor-CbPDks39.js} +1 -1
- package/frontend/dist/js/ru-Derl_YP_.js +2 -0
- package/frontend/dist/js/{standalone-Dwm1daef.js → standalone-Dh56V0YW.js} +1 -1
- package/frontend/dist/js/sv-pJAfBVo0.js +2 -0
- package/frontend/dist/js/{syntax-highlight-Ca9bHsaz.js → syntax-highlight-3EEX6CGL.js} +3 -3
- package/frontend/dist/js/th-C_7fIGnk.js +2 -0
- package/frontend/dist/js/tr-CPqsJvaP.js +2 -0
- package/frontend/dist/js/{typescript-CwieSptM.js → typescript-DRy7A9w4.js} +1 -1
- package/frontend/dist/js/{ui-BnDZpjfc.js → ui-CuY4IifA.js} +2 -2
- package/frontend/dist/js/ui-vendor-D3Ahl1x5.js +177 -0
- package/frontend/dist/js/uk-3o8P_kFi.js +2 -0
- package/frontend/dist/js/{useTranslation-CXY69qNk.js → useTranslation-C3ei3mJq.js} +1 -1
- package/frontend/dist/js/{utils-BM0_0iFj.js → utils-CuC1beTY.js} +1 -1
- package/frontend/dist/js/{utils-vendor-1zob7nK9.js → utils-vendor-BBa8OxtB.js} +1 -1
- package/frontend/dist/js/vi-Dv6N7OVD.js +2 -0
- package/frontend/dist/js/{workApi-BOS7crs9.js → workApi-DHuYNHLf.js} +2 -2
- package/frontend/dist/js/{yaml-QD4znPfZ.js → yaml-DioTxVLA.js} +1 -1
- package/frontend/dist/js/zh-qg5iqtPi.js +2 -0
- package/package.json +16 -5
- package/plugins/huggingface.json +12 -3
- package/plugins/llama-cpp.json +18 -6
- package/plugins/longcat-audiodit.json +114 -0
- package/plugins/openai.json +18 -0
- package/frontend/dist/assets/index-BBpNbfKW.js +0 -16
- package/frontend/dist/assets/index-BLATyFmD.css +0 -1
- package/frontend/dist/js/ChatPage-BBayfMLS.js +0 -16
- package/frontend/dist/js/GalleryPage-By8VI39Q.js +0 -1
- package/frontend/dist/js/ModelsPage-ODeXhDDk.js +0 -2
- package/frontend/dist/js/NotesPage-CApzDjOq.js +0 -1
- package/frontend/dist/js/PluginUsagePage-kutS6SOd.js +0 -1
- package/frontend/dist/js/RichMessageContent-g6WT75Te.js +0 -3
- package/frontend/dist/js/SettingsModal-B7z51BCx.js +0 -1
- package/frontend/dist/js/UserManagementPage-D08lM51c.js +0 -1
- package/frontend/dist/js/WorkPage-DdVLefsK.js +0 -6
- package/frontend/dist/js/api-CLEmGcEh.js +0 -14
- package/frontend/dist/js/appStore-CGdhvp9P.js +0 -2
- package/frontend/dist/js/ar-BTmB2WVm.js +0 -2
- package/frontend/dist/js/authStore-Dh3beEOq.js +0 -4
- package/frontend/dist/js/bn-CQnH7YVT.js +0 -2
- package/frontend/dist/js/chatStore-CDeYWL_q.js +0 -3
- package/frontend/dist/js/cs-oTTZkQXr.js +0 -2
- package/frontend/dist/js/da-Br0gB6J6.js +0 -2
- package/frontend/dist/js/de-DE2iztTF.js +0 -2
- package/frontend/dist/js/es-DgthfVtl.js +0 -2
- package/frontend/dist/js/fr-BjxjFwPh.js +0 -2
- package/frontend/dist/js/hi-D1q7E-9N.js +0 -2
- package/frontend/dist/js/id-CDJZqLTF.js +0 -2
- package/frontend/dist/js/is-BU7Um_Tq.js +0 -2
- package/frontend/dist/js/it-CdAxr8P_.js +0 -2
- package/frontend/dist/js/ja-BLbQwz9u.js +0 -2
- package/frontend/dist/js/ko-CY5lpkAe.js +0 -2
- package/frontend/dist/js/markdown-core-DHnR5Hw8.js +0 -16
- package/frontend/dist/js/ms-tQ8AYXEQ.js +0 -2
- package/frontend/dist/js/nl-CRNi2oWT.js +0 -2
- package/frontend/dist/js/pl-CWtFRS_W.js +0 -2
- package/frontend/dist/js/pt-C7SPoLF8.js +0 -2
- package/frontend/dist/js/ru-Dz4AX8xc.js +0 -2
- package/frontend/dist/js/sv-ETy5-boa.js +0 -2
- package/frontend/dist/js/th-DpuMqvxU.js +0 -2
- package/frontend/dist/js/tr-C-1AYAqg.js +0 -2
- package/frontend/dist/js/ui-vendor-xUzKqAXI.js +0 -177
- package/frontend/dist/js/uk-CyS680gr.js +0 -2
- package/frontend/dist/js/vi-BG8HuiTz.js +0 -2
- package/frontend/dist/js/zh-NCs2mkHj.js +0 -2
|
@@ -0,0 +1,1436 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Libre WebUI
|
|
3
|
+
* Copyright (C) 2025 Kroonen AI, Inc.
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import { execFile } from 'node:child_process';
|
|
18
|
+
import crypto from 'node:crypto';
|
|
19
|
+
import fs from 'node:fs';
|
|
20
|
+
import os from 'node:os';
|
|
21
|
+
import path from 'node:path';
|
|
22
|
+
import { promisify, TextDecoder } from 'node:util';
|
|
23
|
+
import Database from 'better-sqlite3';
|
|
24
|
+
import { inspectSQLiteSchema } from '../persistence/sqliteMigrations.js';
|
|
25
|
+
import { parseAesGcmEnvelope, } from '../platform/storage/aesGcmKeyring.js';
|
|
26
|
+
import { BlobStoreError } from '../platform/storage/blobStore.js';
|
|
27
|
+
import { LocalEncryptedBlobStore, } from '../platform/storage/localEncryptedBlobStore.js';
|
|
28
|
+
import { SqliteEncryptedVectorStore, } from '../platform/storage/sqliteEncryptedVectorStore.js';
|
|
29
|
+
import { createStorageKeyringFromEnvironment, inspectStorageKeyConfiguration, } from '../platform/storage/storageFactory.js';
|
|
30
|
+
import { VectorStoreError } from '../platform/storage/vectorStore.js';
|
|
31
|
+
import { loadAppPackage } from '../utils/packagePaths.js';
|
|
32
|
+
import { BACKEND_DIRECTORY, PROJECT_DIRECTORY, assertNoLegacyDataDirectoryConflict, resolveDataDirectory, resolveLegacyPluginsDirectories, resolvePluginsDirectory, } from '../utils/dataDirectory.js';
|
|
33
|
+
import { LegacyCiphertextIntegrityError, verifyLegacyCiphertextIntegrity, } from './legacyCiphertextIntegrity.js';
|
|
34
|
+
const execFileAsync = promisify(execFile);
|
|
35
|
+
const MAX_DIRECTORY_SCAN_ENTRIES = 1000000;
|
|
36
|
+
const MAX_DIRECTORY_SCAN_BYTES = 1024 * 1024 * 1024 * 1024;
|
|
37
|
+
const DURABLE_JOB_STATES = [
|
|
38
|
+
'queued',
|
|
39
|
+
'running',
|
|
40
|
+
'succeeded',
|
|
41
|
+
'cancelled',
|
|
42
|
+
'dead_letter',
|
|
43
|
+
];
|
|
44
|
+
const DURABLE_ATTEMPT_OUTCOMES = [
|
|
45
|
+
'running',
|
|
46
|
+
'succeeded',
|
|
47
|
+
'retry_scheduled',
|
|
48
|
+
'cancelled',
|
|
49
|
+
'dead_letter',
|
|
50
|
+
'abandoned',
|
|
51
|
+
];
|
|
52
|
+
const MAX_DURABLE_IDENTIFIER_BYTES = 256;
|
|
53
|
+
const MAX_DURABLE_REFERENCE_BYTES = 2048;
|
|
54
|
+
const MAX_DURABLE_PLAINTEXT_BYTES = 64 * 1024;
|
|
55
|
+
const MAX_DURABLE_ENVELOPE_BYTES = 128 * 1024;
|
|
56
|
+
const MAX_DURABLE_JSON_DEPTH = 32;
|
|
57
|
+
const MAX_DURABLE_JSON_NODES = 10000;
|
|
58
|
+
const DEFAULT_MAX_DURABLE_PAYLOAD_RECORDS = 250000;
|
|
59
|
+
const DEFAULT_MAX_DURABLE_CIPHERTEXT_BYTES = 4 * 1024 * 1024 * 1024;
|
|
60
|
+
const DEFAULT_MAX_DURABLE_PLAINTEXT_AGGREGATE_BYTES = 16 * 1024 * 1024 * 1024;
|
|
61
|
+
class DurablePayloadIntegrityError extends Error {
|
|
62
|
+
constructor(code) {
|
|
63
|
+
super('Durable payload recovery verification failed');
|
|
64
|
+
this.code = code;
|
|
65
|
+
this.name = 'DurablePayloadIntegrityError';
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const unique = (values) => [...new Set(values)];
|
|
69
|
+
const safeStat = (target) => {
|
|
70
|
+
try {
|
|
71
|
+
return fs.statSync(target);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const safeLstat = (target) => {
|
|
78
|
+
try {
|
|
79
|
+
return fs.lstatSync(target);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
const inspectSQLiteSource = (kind, sourcePath, rejectHardLinks) => {
|
|
86
|
+
let stat;
|
|
87
|
+
try {
|
|
88
|
+
// lstat is deliberate: stat would silently accept a database or
|
|
89
|
+
// companion symlink and could copy a file outside the recovery source.
|
|
90
|
+
stat = fs.lstatSync(sourcePath);
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
return {
|
|
94
|
+
kind,
|
|
95
|
+
path: sourcePath,
|
|
96
|
+
status: error.code === 'ENOENT'
|
|
97
|
+
? 'missing'
|
|
98
|
+
: 'unreadable',
|
|
99
|
+
stat: null,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
if (stat.isSymbolicLink()) {
|
|
103
|
+
return { kind, path: sourcePath, status: 'symbolic-link', stat };
|
|
104
|
+
}
|
|
105
|
+
if (!stat.isFile()) {
|
|
106
|
+
return { kind, path: sourcePath, status: 'non-regular', stat };
|
|
107
|
+
}
|
|
108
|
+
if (rejectHardLinks && stat.nlink !== 1) {
|
|
109
|
+
return { kind, path: sourcePath, status: 'hard-linked', stat };
|
|
110
|
+
}
|
|
111
|
+
return { kind, path: sourcePath, status: 'regular', stat };
|
|
112
|
+
};
|
|
113
|
+
const sqliteSourceBlocker = (source) => {
|
|
114
|
+
const label = source.kind === 'database' ? 'database' : `${source.kind} companion file`;
|
|
115
|
+
switch (source.status) {
|
|
116
|
+
case 'unreadable':
|
|
117
|
+
return `The SQLite ${label} path could not be inspected without following links.`;
|
|
118
|
+
case 'symbolic-link':
|
|
119
|
+
return `The SQLite ${label} path is a symbolic link; recovery refuses to follow it.`;
|
|
120
|
+
case 'non-regular':
|
|
121
|
+
return `The SQLite ${label} path is not a regular file.`;
|
|
122
|
+
case 'hard-linked':
|
|
123
|
+
return `The SQLite ${label} has multiple hard links; a volume-only snapshot cannot prove that it owns the source file.`;
|
|
124
|
+
default:
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
const copyInspectedSQLiteSource = (source, destination, rejectHardLinks) => {
|
|
129
|
+
if (source.status !== 'regular' || !source.stat) {
|
|
130
|
+
throw new Error('SQLite recovery source was not validated');
|
|
131
|
+
}
|
|
132
|
+
const noFollow = fs.constants.O_NOFOLLOW || 0;
|
|
133
|
+
const sourceDescriptor = fs.openSync(source.path, fs.constants.O_RDONLY | noFollow);
|
|
134
|
+
let destinationDescriptor;
|
|
135
|
+
const buffer = Buffer.allocUnsafe(1024 * 1024);
|
|
136
|
+
try {
|
|
137
|
+
const openedStat = fs.fstatSync(sourceDescriptor);
|
|
138
|
+
if (!openedStat.isFile() ||
|
|
139
|
+
openedStat.dev !== source.stat.dev ||
|
|
140
|
+
openedStat.ino !== source.stat.ino ||
|
|
141
|
+
(rejectHardLinks && openedStat.nlink !== 1)) {
|
|
142
|
+
throw new Error('SQLite recovery source changed during inspection');
|
|
143
|
+
}
|
|
144
|
+
destinationDescriptor = fs.openSync(destination, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL, 0o600);
|
|
145
|
+
let bytesRead;
|
|
146
|
+
while ((bytesRead = fs.readSync(sourceDescriptor, buffer, 0, buffer.length, null)) > 0) {
|
|
147
|
+
let offset = 0;
|
|
148
|
+
while (offset < bytesRead) {
|
|
149
|
+
offset += fs.writeSync(destinationDescriptor, buffer, offset, bytesRead - offset);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
fs.fsyncSync(destinationDescriptor);
|
|
153
|
+
}
|
|
154
|
+
finally {
|
|
155
|
+
buffer.fill(0);
|
|
156
|
+
if (destinationDescriptor !== undefined) {
|
|
157
|
+
fs.closeSync(destinationDescriptor);
|
|
158
|
+
}
|
|
159
|
+
fs.closeSync(sourceDescriptor);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
const physicalOrResolvedPath = (target) => {
|
|
163
|
+
try {
|
|
164
|
+
return fs.realpathSync.native(target);
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
return path.resolve(target);
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
const isPathWithin = (root, candidate) => {
|
|
171
|
+
const relative = path.relative(physicalOrResolvedPath(root), physicalOrResolvedPath(candidate));
|
|
172
|
+
return (relative === '' ||
|
|
173
|
+
(relative !== '..' &&
|
|
174
|
+
!relative.startsWith(`..${path.sep}`) &&
|
|
175
|
+
!path.isAbsolute(relative)));
|
|
176
|
+
};
|
|
177
|
+
const containsSymlinkPathComponent = (target) => {
|
|
178
|
+
const resolved = path.resolve(target);
|
|
179
|
+
const parsed = path.parse(resolved);
|
|
180
|
+
let current = parsed.root;
|
|
181
|
+
for (const component of resolved.slice(parsed.root.length).split(path.sep)) {
|
|
182
|
+
if (!component)
|
|
183
|
+
continue;
|
|
184
|
+
current = path.join(current, component);
|
|
185
|
+
const stat = safeLstat(current);
|
|
186
|
+
if (!stat)
|
|
187
|
+
return false;
|
|
188
|
+
if (stat.isSymbolicLink())
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
return false;
|
|
192
|
+
};
|
|
193
|
+
const containsSymlinkPathComponentFromRoot = (root, target) => {
|
|
194
|
+
const resolvedRoot = path.resolve(root);
|
|
195
|
+
const resolvedTarget = path.resolve(target);
|
|
196
|
+
const relative = path.relative(resolvedRoot, resolvedTarget);
|
|
197
|
+
if (relative === '' ||
|
|
198
|
+
relative === '..' ||
|
|
199
|
+
relative.startsWith(`..${path.sep}`) ||
|
|
200
|
+
path.isAbsolute(relative)) {
|
|
201
|
+
return containsSymlinkPathComponent(resolvedTarget);
|
|
202
|
+
}
|
|
203
|
+
let current = resolvedRoot;
|
|
204
|
+
for (const component of relative.split(path.sep)) {
|
|
205
|
+
if (!component)
|
|
206
|
+
continue;
|
|
207
|
+
current = path.join(current, component);
|
|
208
|
+
const stat = safeLstat(current);
|
|
209
|
+
if (!stat)
|
|
210
|
+
return false;
|
|
211
|
+
if (stat.isSymbolicLink())
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
return false;
|
|
215
|
+
};
|
|
216
|
+
const canAccess = (target, mode) => {
|
|
217
|
+
try {
|
|
218
|
+
fs.accessSync(target, mode);
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
const scanDirectory = (root) => {
|
|
226
|
+
const totals = { files: 0, bytes: 0, errors: 0 };
|
|
227
|
+
const pending = [root];
|
|
228
|
+
let entriesSeen = 0;
|
|
229
|
+
let limitExceeded = false;
|
|
230
|
+
while (pending.length > 0) {
|
|
231
|
+
const current = pending.pop();
|
|
232
|
+
if (!current)
|
|
233
|
+
continue;
|
|
234
|
+
let directory;
|
|
235
|
+
try {
|
|
236
|
+
directory = fs.opendirSync(current);
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
totals.errors += 1;
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
try {
|
|
243
|
+
let entry;
|
|
244
|
+
while ((entry = directory.readSync()) !== null) {
|
|
245
|
+
entriesSeen += 1;
|
|
246
|
+
if (entriesSeen > MAX_DIRECTORY_SCAN_ENTRIES) {
|
|
247
|
+
totals.errors += 1;
|
|
248
|
+
limitExceeded = true;
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
const entryPath = path.join(current, entry.name);
|
|
252
|
+
try {
|
|
253
|
+
// Resolve the named entry without following symlinks so a concurrent
|
|
254
|
+
// replacement cannot redirect recovery inventory outside its root.
|
|
255
|
+
const entryStat = fs.lstatSync(entryPath);
|
|
256
|
+
if (entryStat.isDirectory() && !entryStat.isSymbolicLink()) {
|
|
257
|
+
pending.push(entryPath);
|
|
258
|
+
}
|
|
259
|
+
else if (entryStat.isFile() && !entryStat.isSymbolicLink()) {
|
|
260
|
+
totals.files += 1;
|
|
261
|
+
totals.bytes += entryStat.size;
|
|
262
|
+
if (!Number.isSafeInteger(totals.bytes) ||
|
|
263
|
+
totals.bytes > MAX_DIRECTORY_SCAN_BYTES) {
|
|
264
|
+
totals.errors += 1;
|
|
265
|
+
limitExceeded = true;
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
totals.errors += 1;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
totals.errors += 1;
|
|
277
|
+
}
|
|
278
|
+
finally {
|
|
279
|
+
try {
|
|
280
|
+
directory.closeSync();
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
totals.errors += 1;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
if (limitExceeded)
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
return totals;
|
|
290
|
+
};
|
|
291
|
+
const inspectPluginDirectory = (directoryPath, trustedRoot) => {
|
|
292
|
+
const result = {
|
|
293
|
+
path: directoryPath,
|
|
294
|
+
present: false,
|
|
295
|
+
definitions: 0,
|
|
296
|
+
bytes: 0,
|
|
297
|
+
invalidEntries: 0,
|
|
298
|
+
unsafePath: false,
|
|
299
|
+
};
|
|
300
|
+
if (containsSymlinkPathComponentFromRoot(trustedRoot, directoryPath)) {
|
|
301
|
+
result.present = true;
|
|
302
|
+
result.invalidEntries = 1;
|
|
303
|
+
result.unsafePath = true;
|
|
304
|
+
return result;
|
|
305
|
+
}
|
|
306
|
+
const directoryStat = safeLstat(directoryPath);
|
|
307
|
+
if (!directoryStat)
|
|
308
|
+
return result;
|
|
309
|
+
result.present = true;
|
|
310
|
+
if (!directoryStat.isDirectory() || directoryStat.isSymbolicLink()) {
|
|
311
|
+
result.invalidEntries = 1;
|
|
312
|
+
return result;
|
|
313
|
+
}
|
|
314
|
+
try {
|
|
315
|
+
const entries = fs.readdirSync(directoryPath, { withFileTypes: true });
|
|
316
|
+
for (const entry of entries) {
|
|
317
|
+
if (!entry.name.endsWith('.json'))
|
|
318
|
+
continue;
|
|
319
|
+
const entryPath = path.join(directoryPath, entry.name);
|
|
320
|
+
const entryStat = safeLstat(entryPath);
|
|
321
|
+
if (!entryStat || !entryStat.isFile() || entryStat.isSymbolicLink()) {
|
|
322
|
+
result.invalidEntries += 1;
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
result.definitions += 1;
|
|
326
|
+
result.bytes += entryStat.size;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
catch {
|
|
330
|
+
result.invalidEntries += 1;
|
|
331
|
+
}
|
|
332
|
+
return result;
|
|
333
|
+
};
|
|
334
|
+
const keyFingerprint = (hexKey) => crypto
|
|
335
|
+
.createHash('sha256')
|
|
336
|
+
.update(Buffer.from(hexKey, 'hex'))
|
|
337
|
+
.digest('hex')
|
|
338
|
+
.slice(0, 16);
|
|
339
|
+
const validEncryptionKey = (value) => Boolean(value && /^[a-fA-F0-9]{64}$/.test(value.trim()));
|
|
340
|
+
/** Read only key identity metadata. The selected key is never returned. */
|
|
341
|
+
export const inspectEncryptionKey = (dataDir, env = process.env) => {
|
|
342
|
+
const environmentKey = env.ENCRYPTION_KEY?.trim();
|
|
343
|
+
const persistentPath = path.join(dataDir, '.encryption_key');
|
|
344
|
+
const persistentStat = safeLstat(persistentPath);
|
|
345
|
+
const persistentFilePresent = Boolean(persistentStat);
|
|
346
|
+
let persistentKey;
|
|
347
|
+
const persistentFileSafe = Boolean(persistentStat?.isFile() &&
|
|
348
|
+
!persistentStat.isSymbolicLink() &&
|
|
349
|
+
persistentStat.nlink === 1);
|
|
350
|
+
if (persistentFilePresent && !persistentFileSafe) {
|
|
351
|
+
return {
|
|
352
|
+
status: 'invalid',
|
|
353
|
+
source: 'data-file',
|
|
354
|
+
persistentFilePresent: true,
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
if (persistentStat && persistentFileSafe) {
|
|
358
|
+
let descriptor;
|
|
359
|
+
let keyBytes;
|
|
360
|
+
try {
|
|
361
|
+
descriptor = fs.openSync(persistentPath, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW || 0));
|
|
362
|
+
const openedStat = fs.fstatSync(descriptor);
|
|
363
|
+
if (!openedStat.isFile() ||
|
|
364
|
+
openedStat.nlink !== 1 ||
|
|
365
|
+
openedStat.dev !== persistentStat.dev ||
|
|
366
|
+
openedStat.ino !== persistentStat.ino) {
|
|
367
|
+
return {
|
|
368
|
+
status: 'invalid',
|
|
369
|
+
source: 'data-file',
|
|
370
|
+
persistentFilePresent: true,
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
keyBytes = fs.readFileSync(descriptor);
|
|
374
|
+
persistentKey = keyBytes.toString('utf8').trim();
|
|
375
|
+
}
|
|
376
|
+
catch {
|
|
377
|
+
return {
|
|
378
|
+
status: 'invalid',
|
|
379
|
+
source: 'data-file',
|
|
380
|
+
persistentFilePresent: true,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
finally {
|
|
384
|
+
keyBytes?.fill(0);
|
|
385
|
+
if (descriptor !== undefined)
|
|
386
|
+
fs.closeSync(descriptor);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
if (environmentKey) {
|
|
390
|
+
if (!validEncryptionKey(environmentKey)) {
|
|
391
|
+
return {
|
|
392
|
+
status: 'invalid',
|
|
393
|
+
source: 'environment',
|
|
394
|
+
persistentFilePresent,
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
const matches = persistentFilePresent
|
|
398
|
+
? Boolean(persistentKey &&
|
|
399
|
+
validEncryptionKey(persistentKey) &&
|
|
400
|
+
crypto.timingSafeEqual(Buffer.from(persistentKey, 'hex'), Buffer.from(environmentKey, 'hex')))
|
|
401
|
+
: undefined;
|
|
402
|
+
return {
|
|
403
|
+
status: matches === false ? 'conflict' : 'available',
|
|
404
|
+
source: 'environment',
|
|
405
|
+
fingerprint: keyFingerprint(environmentKey),
|
|
406
|
+
persistentFilePresent,
|
|
407
|
+
...(matches === undefined
|
|
408
|
+
? {}
|
|
409
|
+
: { persistentFileMatchesSelected: matches }),
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
if (persistentFilePresent && !persistentKey) {
|
|
413
|
+
return {
|
|
414
|
+
status: 'invalid',
|
|
415
|
+
source: 'data-file',
|
|
416
|
+
persistentFilePresent: true,
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
if (persistentKey) {
|
|
420
|
+
if (!validEncryptionKey(persistentKey)) {
|
|
421
|
+
return {
|
|
422
|
+
status: 'invalid',
|
|
423
|
+
source: 'data-file',
|
|
424
|
+
persistentFilePresent: true,
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
return {
|
|
428
|
+
status: 'available',
|
|
429
|
+
source: 'data-file',
|
|
430
|
+
fingerprint: keyFingerprint(persistentKey),
|
|
431
|
+
persistentFilePresent: true,
|
|
432
|
+
persistentFileMatchesSelected: true,
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
return {
|
|
436
|
+
status: 'missing',
|
|
437
|
+
source: 'missing',
|
|
438
|
+
persistentFilePresent: false,
|
|
439
|
+
};
|
|
440
|
+
};
|
|
441
|
+
const defaultWorkResourceInspector = async (input) => {
|
|
442
|
+
if (input.resources.length === 0) {
|
|
443
|
+
return { available: true, matches: {} };
|
|
444
|
+
}
|
|
445
|
+
if (input.backend !== 'docker') {
|
|
446
|
+
return {
|
|
447
|
+
available: false,
|
|
448
|
+
matches: {},
|
|
449
|
+
message: 'Kubernetes PVC presence requires an in-cluster recovery check.',
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
const command = input.env.WORK_DOCKER_COMMAND?.trim() || 'docker';
|
|
453
|
+
try {
|
|
454
|
+
const { stdout: volumeList } = await execFileAsync(command, [
|
|
455
|
+
'volume',
|
|
456
|
+
'ls',
|
|
457
|
+
'--filter',
|
|
458
|
+
'label=ai.libre-webui.managed=true',
|
|
459
|
+
'--format',
|
|
460
|
+
'{{.Name}}',
|
|
461
|
+
], { timeout: 10000, maxBuffer: 2 * 1024 * 1024 });
|
|
462
|
+
const managedNames = new Set(volumeList
|
|
463
|
+
.split(/\r?\n/)
|
|
464
|
+
.map(name => name.trim())
|
|
465
|
+
.filter(Boolean));
|
|
466
|
+
const candidates = input.resources.filter(resource => managedNames.has(resource.name));
|
|
467
|
+
const discovered = new Map();
|
|
468
|
+
if (candidates.length > 0) {
|
|
469
|
+
const { stdout: volumeDetails } = await execFileAsync(command, [
|
|
470
|
+
'volume',
|
|
471
|
+
'inspect',
|
|
472
|
+
'--format',
|
|
473
|
+
'{{.Name}}\t{{index .Labels "ai.libre-webui.managed"}}\t{{index .Labels "ai.libre-webui.task"}}',
|
|
474
|
+
...candidates.map(resource => resource.name),
|
|
475
|
+
], { timeout: 10000, maxBuffer: 2 * 1024 * 1024 });
|
|
476
|
+
for (const line of volumeDetails.split(/\r?\n/)) {
|
|
477
|
+
const [name, managed, taskId, ...unexpected] = line.split('\t');
|
|
478
|
+
if (unexpected.length === 0 &&
|
|
479
|
+
name?.trim() &&
|
|
480
|
+
managed?.trim() === 'true' &&
|
|
481
|
+
taskId?.trim()) {
|
|
482
|
+
discovered.set(name.trim(), taskId.trim());
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return {
|
|
487
|
+
available: true,
|
|
488
|
+
matches: Object.fromEntries(input.resources.map(resource => [
|
|
489
|
+
resource.taskId,
|
|
490
|
+
discovered.get(resource.name) === resource.taskId,
|
|
491
|
+
])),
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
catch {
|
|
495
|
+
return {
|
|
496
|
+
available: false,
|
|
497
|
+
matches: {},
|
|
498
|
+
message: 'The Docker volume inventory could not be read.',
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
const countAndBytes = (database, sql) => {
|
|
503
|
+
const row = database.prepare(sql).get();
|
|
504
|
+
return {
|
|
505
|
+
records: Number(row?.records || 0),
|
|
506
|
+
bytes: Number(row?.bytes || 0),
|
|
507
|
+
};
|
|
508
|
+
};
|
|
509
|
+
const groupedCounts = (database, table) => {
|
|
510
|
+
const rows = database
|
|
511
|
+
.prepare(`SELECT status, COUNT(*) AS count FROM ${table} GROUP BY status`)
|
|
512
|
+
.all();
|
|
513
|
+
return Object.fromEntries(rows.map(row => [row.status, Number(row.count)]));
|
|
514
|
+
};
|
|
515
|
+
const emptyCountAndBytes = () => ({ records: 0, bytes: 0 });
|
|
516
|
+
const zeroCounts = (values) => Object.fromEntries(values.map(value => [value, 0]));
|
|
517
|
+
const groupedKnownCounts = (database, table, column, values) => {
|
|
518
|
+
const result = zeroCounts(values);
|
|
519
|
+
const known = new Set(values);
|
|
520
|
+
const rows = database
|
|
521
|
+
.prepare(`SELECT ${column} AS value, COUNT(*) AS count FROM ${table} GROUP BY ${column}`)
|
|
522
|
+
.all();
|
|
523
|
+
for (const row of rows) {
|
|
524
|
+
if (!known.has(row.value)) {
|
|
525
|
+
throw new DurablePayloadIntegrityError('integrity');
|
|
526
|
+
}
|
|
527
|
+
result[row.value] = Number(row.count);
|
|
528
|
+
}
|
|
529
|
+
return result;
|
|
530
|
+
};
|
|
531
|
+
const requireRecoveryLimit = (value, fallback) => {
|
|
532
|
+
const selected = value ?? fallback;
|
|
533
|
+
if (!Number.isSafeInteger(selected) || selected < 1) {
|
|
534
|
+
throw new DurablePayloadIntegrityError('verification-limit');
|
|
535
|
+
}
|
|
536
|
+
return selected;
|
|
537
|
+
};
|
|
538
|
+
const validateDurableText = (value, maxBytes = MAX_DURABLE_IDENTIFIER_BYTES) => {
|
|
539
|
+
if (typeof value !== 'string' ||
|
|
540
|
+
value.length === 0 ||
|
|
541
|
+
Buffer.byteLength(value, 'utf8') > maxBytes) {
|
|
542
|
+
return false;
|
|
543
|
+
}
|
|
544
|
+
return ![...value].some(character => {
|
|
545
|
+
const codePoint = character.codePointAt(0) ?? 0;
|
|
546
|
+
return codePoint <= 31 || codePoint === 127;
|
|
547
|
+
});
|
|
548
|
+
};
|
|
549
|
+
/** Mirrors the immutable v3 durable-payload wire contract for recovery. */
|
|
550
|
+
const canonicalDurableJson = (value) => {
|
|
551
|
+
let nodes = 0;
|
|
552
|
+
const ancestors = new Set();
|
|
553
|
+
const visit = (candidate, depth) => {
|
|
554
|
+
nodes += 1;
|
|
555
|
+
if (nodes > MAX_DURABLE_JSON_NODES || depth > MAX_DURABLE_JSON_DEPTH) {
|
|
556
|
+
throw new DurablePayloadIntegrityError('integrity');
|
|
557
|
+
}
|
|
558
|
+
if (candidate === null)
|
|
559
|
+
return 'null';
|
|
560
|
+
if (typeof candidate === 'string')
|
|
561
|
+
return JSON.stringify(candidate);
|
|
562
|
+
if (typeof candidate === 'boolean')
|
|
563
|
+
return candidate ? 'true' : 'false';
|
|
564
|
+
if (typeof candidate === 'number') {
|
|
565
|
+
if (!Number.isFinite(candidate)) {
|
|
566
|
+
throw new DurablePayloadIntegrityError('integrity');
|
|
567
|
+
}
|
|
568
|
+
return JSON.stringify(candidate);
|
|
569
|
+
}
|
|
570
|
+
if (typeof candidate !== 'object' || ancestors.has(candidate)) {
|
|
571
|
+
throw new DurablePayloadIntegrityError('integrity');
|
|
572
|
+
}
|
|
573
|
+
ancestors.add(candidate);
|
|
574
|
+
try {
|
|
575
|
+
if (Array.isArray(candidate)) {
|
|
576
|
+
return `[${Array.from(candidate, item => visit(item, depth + 1)).join(',')}]`;
|
|
577
|
+
}
|
|
578
|
+
const prototype = Object.getPrototypeOf(candidate);
|
|
579
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
580
|
+
throw new DurablePayloadIntegrityError('integrity');
|
|
581
|
+
}
|
|
582
|
+
const record = candidate;
|
|
583
|
+
return `{${Object.keys(record)
|
|
584
|
+
.sort()
|
|
585
|
+
.map(key => `${JSON.stringify(key)}:${visit(record[key], depth + 1)}`)
|
|
586
|
+
.join(',')}}`;
|
|
587
|
+
}
|
|
588
|
+
finally {
|
|
589
|
+
ancestors.delete(candidate);
|
|
590
|
+
}
|
|
591
|
+
};
|
|
592
|
+
const serialized = visit(value, 0);
|
|
593
|
+
if (Buffer.byteLength(serialized, 'utf8') > MAX_DURABLE_PLAINTEXT_BYTES) {
|
|
594
|
+
throw new DurablePayloadIntegrityError('integrity');
|
|
595
|
+
}
|
|
596
|
+
return serialized;
|
|
597
|
+
};
|
|
598
|
+
const verifyDurablePayloadIntegrity = (database, keyring, options = {}) => {
|
|
599
|
+
const maxRecords = requireRecoveryLimit(options.maxRecords, DEFAULT_MAX_DURABLE_PAYLOAD_RECORDS);
|
|
600
|
+
const maxCiphertextBytes = requireRecoveryLimit(options.maxCiphertextBytes, DEFAULT_MAX_DURABLE_CIPHERTEXT_BYTES);
|
|
601
|
+
const maxPlaintextBytes = requireRecoveryLimit(options.maxPlaintextBytes, DEFAULT_MAX_DURABLE_PLAINTEXT_AGGREGATE_BYTES);
|
|
602
|
+
const aggregate = database
|
|
603
|
+
.prepare(`WITH payloads AS (
|
|
604
|
+
SELECT payload_format, LENGTH(CAST(payload AS BLOB)) AS bytes
|
|
605
|
+
FROM platform_jobs
|
|
606
|
+
UNION ALL
|
|
607
|
+
SELECT payload_format, LENGTH(CAST(payload AS BLOB)) AS bytes
|
|
608
|
+
FROM platform_events
|
|
609
|
+
)
|
|
610
|
+
SELECT COUNT(*) AS records,
|
|
611
|
+
COALESCE(SUM(payload_format = 'encrypted'), 0) AS encrypted_records,
|
|
612
|
+
COALESCE(SUM(payload_format = 'reference'), 0) AS reference_records,
|
|
613
|
+
COALESCE(SUM(CASE WHEN payload_format = 'encrypted' THEN bytes ELSE 0 END), 0) AS ciphertext_bytes,
|
|
614
|
+
COALESCE(SUM(CASE WHEN payload_format = 'reference' THEN bytes ELSE 0 END), 0) AS reference_bytes,
|
|
615
|
+
COALESCE(MAX(CASE WHEN payload_format = 'encrypted' THEN bytes ELSE 0 END), 0) AS largest_envelope
|
|
616
|
+
FROM payloads`)
|
|
617
|
+
.get();
|
|
618
|
+
const records = Number(aggregate.records);
|
|
619
|
+
const encryptedRecords = Number(aggregate.encrypted_records);
|
|
620
|
+
const referenceRecords = Number(aggregate.reference_records);
|
|
621
|
+
const ciphertextBytes = Number(aggregate.ciphertext_bytes);
|
|
622
|
+
const referenceBytes = Number(aggregate.reference_bytes);
|
|
623
|
+
const largestEnvelope = Number(aggregate.largest_envelope);
|
|
624
|
+
const aggregateValues = [
|
|
625
|
+
records,
|
|
626
|
+
encryptedRecords,
|
|
627
|
+
referenceRecords,
|
|
628
|
+
ciphertextBytes,
|
|
629
|
+
referenceBytes,
|
|
630
|
+
largestEnvelope,
|
|
631
|
+
];
|
|
632
|
+
if (aggregateValues.some(value => !Number.isSafeInteger(value) || value < 0) ||
|
|
633
|
+
records !== encryptedRecords + referenceRecords) {
|
|
634
|
+
throw new DurablePayloadIntegrityError('integrity');
|
|
635
|
+
}
|
|
636
|
+
if (records > maxRecords ||
|
|
637
|
+
ciphertextBytes > maxCiphertextBytes ||
|
|
638
|
+
largestEnvelope > MAX_DURABLE_ENVELOPE_BYTES ||
|
|
639
|
+
encryptedRecords >
|
|
640
|
+
Math.floor(maxPlaintextBytes / MAX_DURABLE_PLAINTEXT_BYTES)) {
|
|
641
|
+
throw new DurablePayloadIntegrityError('verification-limit');
|
|
642
|
+
}
|
|
643
|
+
if (encryptedRecords > 0 && !keyring) {
|
|
644
|
+
throw new DurablePayloadIntegrityError('integrity');
|
|
645
|
+
}
|
|
646
|
+
let plaintextBytes = 0;
|
|
647
|
+
const decoder = new TextDecoder('utf-8', { fatal: true });
|
|
648
|
+
const verify = (payloadFormat, payload, aad) => {
|
|
649
|
+
if (payloadFormat === 'reference') {
|
|
650
|
+
if (!validateDurableText(payload, MAX_DURABLE_REFERENCE_BYTES)) {
|
|
651
|
+
throw new DurablePayloadIntegrityError('integrity');
|
|
652
|
+
}
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
if (payloadFormat !== 'encrypted' || !keyring) {
|
|
656
|
+
throw new DurablePayloadIntegrityError('integrity');
|
|
657
|
+
}
|
|
658
|
+
let plaintext;
|
|
659
|
+
try {
|
|
660
|
+
plaintext = keyring.decrypt(parseAesGcmEnvelope(JSON.parse(payload)), aad);
|
|
661
|
+
if (plaintext.byteLength > MAX_DURABLE_PLAINTEXT_BYTES) {
|
|
662
|
+
throw new DurablePayloadIntegrityError('integrity');
|
|
663
|
+
}
|
|
664
|
+
plaintextBytes += plaintext.byteLength;
|
|
665
|
+
if (!Number.isSafeInteger(plaintextBytes) ||
|
|
666
|
+
plaintextBytes > maxPlaintextBytes) {
|
|
667
|
+
throw new DurablePayloadIntegrityError('verification-limit');
|
|
668
|
+
}
|
|
669
|
+
const decoded = decoder.decode(plaintext);
|
|
670
|
+
const parsed = JSON.parse(decoded);
|
|
671
|
+
if (canonicalDurableJson(parsed) !== decoded) {
|
|
672
|
+
throw new DurablePayloadIntegrityError('integrity');
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
catch (error) {
|
|
676
|
+
if (error instanceof DurablePayloadIntegrityError)
|
|
677
|
+
throw error;
|
|
678
|
+
throw new DurablePayloadIntegrityError('integrity');
|
|
679
|
+
}
|
|
680
|
+
finally {
|
|
681
|
+
plaintext?.fill(0);
|
|
682
|
+
}
|
|
683
|
+
};
|
|
684
|
+
const jobs = database
|
|
685
|
+
.prepare(`SELECT id, job_type, actor_user_id, payload_format, payload
|
|
686
|
+
FROM platform_jobs`)
|
|
687
|
+
.iterate();
|
|
688
|
+
for (const row of jobs) {
|
|
689
|
+
if (!validateDurableText(row.id) ||
|
|
690
|
+
!validateDurableText(row.job_type) ||
|
|
691
|
+
!validateDurableText(row.actor_user_id)) {
|
|
692
|
+
throw new DurablePayloadIntegrityError('integrity');
|
|
693
|
+
}
|
|
694
|
+
verify(row.payload_format, row.payload, Buffer.from(`durable-job:v1\0${row.id}\0${row.job_type}\0${row.actor_user_id}`));
|
|
695
|
+
}
|
|
696
|
+
const events = database
|
|
697
|
+
.prepare(`SELECT event_id, stream_id, event_type, subject_id, actor_user_id,
|
|
698
|
+
payload_format, payload
|
|
699
|
+
FROM platform_events`)
|
|
700
|
+
.iterate();
|
|
701
|
+
for (const row of events) {
|
|
702
|
+
if (!validateDurableText(row.event_id) ||
|
|
703
|
+
!validateDurableText(row.stream_id) ||
|
|
704
|
+
!validateDurableText(row.event_type) ||
|
|
705
|
+
!validateDurableText(row.subject_id) ||
|
|
706
|
+
(row.actor_user_id !== null && !validateDurableText(row.actor_user_id))) {
|
|
707
|
+
throw new DurablePayloadIntegrityError('integrity');
|
|
708
|
+
}
|
|
709
|
+
verify(row.payload_format, row.payload, Buffer.from(`durable-event:v1\0${row.event_id}\0${row.stream_id}\0${row.event_type}\0${row.subject_id}\0${row.actor_user_id ?? ''}`));
|
|
710
|
+
}
|
|
711
|
+
const inconsistentHeads = Number(database
|
|
712
|
+
.prepare(`SELECT COUNT(*) AS count
|
|
713
|
+
FROM platform_event_stream_heads AS heads
|
|
714
|
+
LEFT JOIN (
|
|
715
|
+
SELECT stream_id,
|
|
716
|
+
MIN(stream_sequence) AS first_sequence,
|
|
717
|
+
MAX(stream_sequence) AS last_sequence,
|
|
718
|
+
COUNT(*) AS event_count
|
|
719
|
+
FROM platform_events GROUP BY stream_id
|
|
720
|
+
) AS events ON events.stream_id = heads.stream_id
|
|
721
|
+
WHERE heads.last_sequence != COALESCE(events.last_sequence, 0)
|
|
722
|
+
OR (heads.last_sequence = 0 AND COALESCE(events.event_count, 0) != 0)
|
|
723
|
+
OR (heads.last_sequence > 0 AND (
|
|
724
|
+
events.first_sequence != 1
|
|
725
|
+
OR events.event_count != heads.last_sequence
|
|
726
|
+
))`)
|
|
727
|
+
.get().count);
|
|
728
|
+
if (inconsistentHeads !== 0) {
|
|
729
|
+
throw new DurablePayloadIntegrityError('integrity');
|
|
730
|
+
}
|
|
731
|
+
return {
|
|
732
|
+
verified: true,
|
|
733
|
+
records,
|
|
734
|
+
encryptedRecords,
|
|
735
|
+
referenceRecords,
|
|
736
|
+
ciphertextBytes,
|
|
737
|
+
referenceBytes,
|
|
738
|
+
plaintextBytes,
|
|
739
|
+
};
|
|
740
|
+
};
|
|
741
|
+
export class RecoveryInventoryService {
|
|
742
|
+
constructor(defaults = {}) {
|
|
743
|
+
this.defaults = defaults;
|
|
744
|
+
}
|
|
745
|
+
async collect(options = {}) {
|
|
746
|
+
const env = options.env || process.env;
|
|
747
|
+
const dataDir = options.dataDir
|
|
748
|
+
? path.resolve(options.dataDir)
|
|
749
|
+
: options.databasePath
|
|
750
|
+
? path.dirname(path.resolve(options.databasePath))
|
|
751
|
+
: options.cwd && !env.DATA_DIR?.trim()
|
|
752
|
+
? path.resolve(options.cwd, 'backend', 'data')
|
|
753
|
+
: resolveDataDirectory(env);
|
|
754
|
+
const databasePath = path.resolve(options.databasePath || path.join(dataDir, 'data.sqlite'));
|
|
755
|
+
const now = options.now || new Date();
|
|
756
|
+
const blockers = [];
|
|
757
|
+
const warnings = [];
|
|
758
|
+
const exclusions = [
|
|
759
|
+
'External model files and provider-managed data are not stored in the Libre WebUI data directory.',
|
|
760
|
+
'Secret values are excluded; only configuration presence and the encryption-key fingerprint are reported.',
|
|
761
|
+
];
|
|
762
|
+
if (!options.dataDir && !options.databasePath) {
|
|
763
|
+
try {
|
|
764
|
+
assertNoLegacyDataDirectoryConflict(env, options.cwd
|
|
765
|
+
? {
|
|
766
|
+
defaultDataDirectory: path.resolve(options.cwd, 'backend', 'data'),
|
|
767
|
+
legacyDataDirectory: path.resolve(options.cwd, 'backend', 'backend', 'data'),
|
|
768
|
+
}
|
|
769
|
+
: undefined);
|
|
770
|
+
}
|
|
771
|
+
catch (error) {
|
|
772
|
+
blockers.push(error instanceof Error
|
|
773
|
+
? error.message
|
|
774
|
+
: 'A legacy data-directory conflict was detected.');
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
const dataStat = safeStat(dataDir);
|
|
778
|
+
const dataPresent = Boolean(dataStat?.isDirectory());
|
|
779
|
+
const dataReadable = dataPresent && canAccess(dataDir, fs.constants.R_OK);
|
|
780
|
+
const dataWritable = dataPresent && canAccess(dataDir, fs.constants.W_OK | fs.constants.X_OK);
|
|
781
|
+
const dataSize = dataPresent
|
|
782
|
+
? scanDirectory(dataDir)
|
|
783
|
+
: { files: 0, bytes: 0, errors: 0 };
|
|
784
|
+
if (!dataPresent)
|
|
785
|
+
blockers.push('The configured data directory is missing.');
|
|
786
|
+
else {
|
|
787
|
+
if (!dataReadable)
|
|
788
|
+
blockers.push('The configured data directory is not readable.');
|
|
789
|
+
if (!dataWritable) {
|
|
790
|
+
warnings.push('The configured data directory is read-only; this is valid for a quiesced recovery source but not for a running application.');
|
|
791
|
+
}
|
|
792
|
+
if (dataSize.errors > 0) {
|
|
793
|
+
blockers.push(`${dataSize.errors} data-directory entr${dataSize.errors === 1 ? 'y could' : 'ies could'} not be inventoried.`);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
const encryption = inspectEncryptionKey(dataDir, env);
|
|
797
|
+
const storageEnvironment = {
|
|
798
|
+
...env,
|
|
799
|
+
DATA_DIR: dataDir,
|
|
800
|
+
};
|
|
801
|
+
const storageEncryption = inspectStorageKeyConfiguration(storageEnvironment);
|
|
802
|
+
let storageKeyring;
|
|
803
|
+
if (encryption.status === 'missing') {
|
|
804
|
+
blockers.push('No persistent encryption key was found; encrypted records cannot be recovered reliably.');
|
|
805
|
+
}
|
|
806
|
+
else if (encryption.status === 'invalid') {
|
|
807
|
+
blockers.push('The selected encryption key is not a 64-character hexadecimal key.');
|
|
808
|
+
}
|
|
809
|
+
else if (encryption.status === 'conflict') {
|
|
810
|
+
blockers.push('The environment encryption key differs from the key file in the data directory.');
|
|
811
|
+
}
|
|
812
|
+
if (storageEncryption.status === 'invalid') {
|
|
813
|
+
blockers.push('The platform storage encryption keyring configuration is invalid.');
|
|
814
|
+
}
|
|
815
|
+
else if (storageEncryption.status === 'missing') {
|
|
816
|
+
warnings.push('The platform storage keyring is not configured; encrypted platform blob and vector adapters cannot be enabled.');
|
|
817
|
+
}
|
|
818
|
+
else {
|
|
819
|
+
try {
|
|
820
|
+
storageKeyring =
|
|
821
|
+
createStorageKeyringFromEnvironment(storageEnvironment);
|
|
822
|
+
}
|
|
823
|
+
catch {
|
|
824
|
+
blockers.push('The platform storage encryption keyring changed or became unavailable during recovery inspection.');
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
if (env.NODE_ENV === 'production' && !env.JWT_SECRET?.trim()) {
|
|
828
|
+
blockers.push('JWT_SECRET is not configured persistently; restored sessions would not survive a restart.');
|
|
829
|
+
}
|
|
830
|
+
else if (!env.JWT_SECRET?.trim()) {
|
|
831
|
+
warnings.push('JWT_SECRET is not configured persistently.');
|
|
832
|
+
}
|
|
833
|
+
const walPath = `${databasePath}-wal`;
|
|
834
|
+
const shmPath = `${databasePath}-shm`;
|
|
835
|
+
const volumeDatabaseSelection = options.databasePath === undefined;
|
|
836
|
+
const databaseSource = inspectSQLiteSource('database', databasePath, volumeDatabaseSelection);
|
|
837
|
+
const walSource = inspectSQLiteSource('WAL', walPath, volumeDatabaseSelection);
|
|
838
|
+
const shmSource = inspectSQLiteSource('SHM', shmPath, volumeDatabaseSelection);
|
|
839
|
+
const sqliteSources = [databaseSource, walSource, shmSource];
|
|
840
|
+
for (const source of sqliteSources) {
|
|
841
|
+
const blocker = sqliteSourceBlocker(source);
|
|
842
|
+
if (blocker)
|
|
843
|
+
blockers.push(blocker);
|
|
844
|
+
}
|
|
845
|
+
let canonicalDatabaseLocation = true;
|
|
846
|
+
if (volumeDatabaseSelection) {
|
|
847
|
+
const expectedDatabasePath = path.resolve(dataDir, 'data.sqlite');
|
|
848
|
+
if (databasePath !== expectedDatabasePath) {
|
|
849
|
+
canonicalDatabaseLocation = false;
|
|
850
|
+
}
|
|
851
|
+
else if (databaseSource.status === 'regular' && dataPresent) {
|
|
852
|
+
try {
|
|
853
|
+
const expectedPhysicalPath = path.join(fs.realpathSync.native(dataDir), 'data.sqlite');
|
|
854
|
+
canonicalDatabaseLocation =
|
|
855
|
+
fs.realpathSync.native(databasePath) === expectedPhysicalPath;
|
|
856
|
+
}
|
|
857
|
+
catch {
|
|
858
|
+
canonicalDatabaseLocation = false;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
if (!canonicalDatabaseLocation) {
|
|
862
|
+
blockers.push('The default SQLite database is not the canonical DATA_DIR/data.sqlite file.');
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
const databaseStat = databaseSource.stat;
|
|
866
|
+
const walStat = walSource.stat;
|
|
867
|
+
const shmStat = shmSource.stat;
|
|
868
|
+
let databaseOpen = false;
|
|
869
|
+
let quickCheck = 'not-run';
|
|
870
|
+
let foreignKeyViolations = null;
|
|
871
|
+
let userVersion = null;
|
|
872
|
+
let schemaFingerprint;
|
|
873
|
+
let actualTableCount = 0;
|
|
874
|
+
let ledgerPresent = false;
|
|
875
|
+
let currentSchemaVersion = 0;
|
|
876
|
+
let targetSchemaVersion = 0;
|
|
877
|
+
let minimumSupportedSchemaVersion = 0;
|
|
878
|
+
let appliedMigrations = [];
|
|
879
|
+
let missingSchema = [];
|
|
880
|
+
let generatedMedia = emptyCountAndBytes();
|
|
881
|
+
let voiceReferences = emptyCountAndBytes();
|
|
882
|
+
let documentText = emptyCountAndBytes();
|
|
883
|
+
let legacyDocumentVectors = emptyCountAndBytes();
|
|
884
|
+
let platformVectors = emptyCountAndBytes();
|
|
885
|
+
let platformVectorAclRecords = 0;
|
|
886
|
+
let platformVectorAttributeRecords = 0;
|
|
887
|
+
let workTasks = [];
|
|
888
|
+
let tasksByStatus = {};
|
|
889
|
+
let activeRuns = 0;
|
|
890
|
+
let activePreviews = 0;
|
|
891
|
+
let mediaJobsByStatus = {};
|
|
892
|
+
let mediaJobTotal = 0;
|
|
893
|
+
let mediaJobActive = 0;
|
|
894
|
+
let durableSubstrateAvailable = false;
|
|
895
|
+
let durableJobsByState = zeroCounts(DURABLE_JOB_STATES);
|
|
896
|
+
let durableJobTotal = 0;
|
|
897
|
+
let durableJobRunning = 0;
|
|
898
|
+
let durableAttemptsByOutcome = zeroCounts(DURABLE_ATTEMPT_OUTCOMES);
|
|
899
|
+
let durableAttemptTotal = 0;
|
|
900
|
+
let durableAttemptActive = 0;
|
|
901
|
+
let durableEventStreams = 0;
|
|
902
|
+
let durableEventTotal = 0;
|
|
903
|
+
let durableEventLastCursor = 0;
|
|
904
|
+
let durablePayloadIntegrity = {
|
|
905
|
+
verified: false,
|
|
906
|
+
encryptedAuthenticated: false,
|
|
907
|
+
referenceTargetsVerified: false,
|
|
908
|
+
records: 0,
|
|
909
|
+
encryptedRecords: 0,
|
|
910
|
+
referenceRecords: 0,
|
|
911
|
+
ciphertextBytes: 0,
|
|
912
|
+
referenceBytes: 0,
|
|
913
|
+
plaintextBytes: 0,
|
|
914
|
+
};
|
|
915
|
+
let legacyCiphertext = {
|
|
916
|
+
verified: false,
|
|
917
|
+
encryptedAuthenticated: false,
|
|
918
|
+
records: 0,
|
|
919
|
+
textRecords: 0,
|
|
920
|
+
binaryRecords: 0,
|
|
921
|
+
ciphertextBytes: 0,
|
|
922
|
+
plaintextBytes: 0,
|
|
923
|
+
};
|
|
924
|
+
const sqliteSourcesValid = canonicalDatabaseLocation &&
|
|
925
|
+
databaseSource.status === 'regular' &&
|
|
926
|
+
[walSource, shmSource].every(source => ['regular', 'missing'].includes(source.status));
|
|
927
|
+
if (databaseSource.status === 'missing') {
|
|
928
|
+
blockers.push('The SQLite database file is missing.');
|
|
929
|
+
}
|
|
930
|
+
else if (sqliteSourcesValid) {
|
|
931
|
+
let database;
|
|
932
|
+
let inspectionDirectory;
|
|
933
|
+
try {
|
|
934
|
+
// A readonly WAL connection can still update shared-memory metadata.
|
|
935
|
+
// Always inspect a private DB/WAL/SHM snapshot so the inventory never
|
|
936
|
+
// mutates a writable or read-only recovery source.
|
|
937
|
+
inspectionDirectory = fs.mkdtempSync(path.join(os.tmpdir(), 'libre-recovery-sqlite-'));
|
|
938
|
+
const inspectionDatabasePath = path.join(inspectionDirectory, path.basename(databasePath));
|
|
939
|
+
for (const [source, suffix] of [
|
|
940
|
+
[databaseSource, ''],
|
|
941
|
+
[walSource, '-wal'],
|
|
942
|
+
[shmSource, '-shm'],
|
|
943
|
+
]) {
|
|
944
|
+
if (source.status === 'regular') {
|
|
945
|
+
copyInspectedSQLiteSource(source, `${inspectionDatabasePath}${suffix}`, volumeDatabaseSelection);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
database = new Database(inspectionDatabasePath, {
|
|
949
|
+
readonly: true,
|
|
950
|
+
fileMustExist: true,
|
|
951
|
+
});
|
|
952
|
+
databaseOpen = true;
|
|
953
|
+
const integrityRows = database.pragma('quick_check');
|
|
954
|
+
const integrityValues = integrityRows.flatMap(row => Object.values(row).map(value => String(value)));
|
|
955
|
+
quickCheck =
|
|
956
|
+
integrityValues.length === 1 &&
|
|
957
|
+
integrityValues[0].toLowerCase() === 'ok'
|
|
958
|
+
? 'ok'
|
|
959
|
+
: 'failed';
|
|
960
|
+
if (quickCheck !== 'ok') {
|
|
961
|
+
blockers.push('SQLite quick-check reported database corruption.');
|
|
962
|
+
}
|
|
963
|
+
foreignKeyViolations = database.pragma('foreign_key_check').length;
|
|
964
|
+
if (foreignKeyViolations > 0) {
|
|
965
|
+
blockers.push(`SQLite reported ${foreignKeyViolations} foreign-key violation${foreignKeyViolations === 1 ? '' : 's'}.`);
|
|
966
|
+
}
|
|
967
|
+
const versionRows = database.pragma('user_version');
|
|
968
|
+
userVersion = Number(versionRows[0]?.user_version || 0);
|
|
969
|
+
const schemaRows = database
|
|
970
|
+
.prepare("SELECT name, sql FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' ORDER BY name")
|
|
971
|
+
.all();
|
|
972
|
+
actualTableCount = schemaRows.length;
|
|
973
|
+
const actualTables = new Set(schemaRows.map(row => row.name));
|
|
974
|
+
const schemaInspection = inspectSQLiteSchema(database);
|
|
975
|
+
ledgerPresent = schemaInspection.ledgerPresent;
|
|
976
|
+
currentSchemaVersion = schemaInspection.currentVersion;
|
|
977
|
+
targetSchemaVersion = schemaInspection.targetVersion;
|
|
978
|
+
minimumSupportedSchemaVersion =
|
|
979
|
+
schemaInspection.minimumSupportedVersion;
|
|
980
|
+
appliedMigrations = schemaInspection.appliedMigrations.map(migration => ({
|
|
981
|
+
version: migration.version,
|
|
982
|
+
name: migration.name,
|
|
983
|
+
checksumMatches: migration.checksumMatches,
|
|
984
|
+
}));
|
|
985
|
+
missingSchema = schemaInspection.missing;
|
|
986
|
+
if (!schemaInspection.compatible) {
|
|
987
|
+
blockers.push(schemaInspection.reason ||
|
|
988
|
+
'The SQLite schema is not compatible with this application version.');
|
|
989
|
+
}
|
|
990
|
+
schemaFingerprint = crypto
|
|
991
|
+
.createHash('sha256')
|
|
992
|
+
.update(schemaRows.map(row => `${row.name}:${row.sql || ''}`).join('\n'))
|
|
993
|
+
.digest('hex');
|
|
994
|
+
let legacyKey;
|
|
995
|
+
if (encryption.status === 'available') {
|
|
996
|
+
const environmentKey = env.ENCRYPTION_KEY?.trim();
|
|
997
|
+
const selectedKey = environmentKey
|
|
998
|
+
? environmentKey
|
|
999
|
+
: fs
|
|
1000
|
+
.readFileSync(path.join(dataDir, '.encryption_key'), 'utf8')
|
|
1001
|
+
.trim();
|
|
1002
|
+
legacyKey = Buffer.from(selectedKey, 'hex');
|
|
1003
|
+
}
|
|
1004
|
+
try {
|
|
1005
|
+
legacyCiphertext = verifyLegacyCiphertextIntegrity(database, legacyKey, options.legacyCiphertextIntegrityLimits);
|
|
1006
|
+
}
|
|
1007
|
+
catch (error) {
|
|
1008
|
+
blockers.push(error instanceof LegacyCiphertextIntegrityError &&
|
|
1009
|
+
error.code === 'verification-limit'
|
|
1010
|
+
? 'Legacy application ciphertext exceeds bounded recovery integrity verification limits.'
|
|
1011
|
+
: error instanceof LegacyCiphertextIntegrityError &&
|
|
1012
|
+
error.code === 'key-unavailable'
|
|
1013
|
+
? 'Legacy application ciphertext is present but no usable application encryption key is available.'
|
|
1014
|
+
: 'Legacy application ciphertext failed read-only integrity verification with the configured application encryption key.');
|
|
1015
|
+
}
|
|
1016
|
+
finally {
|
|
1017
|
+
legacyKey?.fill(0);
|
|
1018
|
+
}
|
|
1019
|
+
if (actualTables.has('generated_images')) {
|
|
1020
|
+
generatedMedia = countAndBytes(database, 'SELECT COUNT(*) AS records, COALESCE(SUM(LENGTH(image_data)), 0) AS bytes FROM generated_images');
|
|
1021
|
+
}
|
|
1022
|
+
if (actualTables.has('voice_profiles')) {
|
|
1023
|
+
voiceReferences = countAndBytes(database, 'SELECT COUNT(*) AS records, COALESCE(SUM(LENGTH(reference_audio)), 0) AS bytes FROM voice_profiles');
|
|
1024
|
+
}
|
|
1025
|
+
if (actualTables.has('documents')) {
|
|
1026
|
+
documentText = countAndBytes(database, 'SELECT COUNT(*) AS records, COALESCE(SUM(LENGTH(content)), 0) AS bytes FROM documents');
|
|
1027
|
+
}
|
|
1028
|
+
if (actualTables.has('document_chunks')) {
|
|
1029
|
+
legacyDocumentVectors = countAndBytes(database, 'SELECT COUNT(*) AS records, COALESCE(SUM(LENGTH(embedding)), 0) AS bytes FROM document_chunks WHERE embedding IS NOT NULL');
|
|
1030
|
+
}
|
|
1031
|
+
if (actualTables.has('platform_vector_entries')) {
|
|
1032
|
+
platformVectors = countAndBytes(database, 'SELECT COUNT(*) AS records, COALESCE(SUM(LENGTH(embedding)), 0) AS bytes FROM platform_vector_entries');
|
|
1033
|
+
}
|
|
1034
|
+
if (actualTables.has('platform_vector_acl')) {
|
|
1035
|
+
platformVectorAclRecords = Number(database
|
|
1036
|
+
.prepare('SELECT COUNT(*) AS count FROM platform_vector_acl')
|
|
1037
|
+
.get().count);
|
|
1038
|
+
}
|
|
1039
|
+
if (actualTables.has('platform_vector_attributes')) {
|
|
1040
|
+
platformVectorAttributeRecords = Number(database
|
|
1041
|
+
.prepare('SELECT COUNT(*) AS count FROM platform_vector_attributes')
|
|
1042
|
+
.get().count);
|
|
1043
|
+
}
|
|
1044
|
+
if (platformVectors.records > 0 && schemaInspection.compatible) {
|
|
1045
|
+
if (!storageKeyring) {
|
|
1046
|
+
blockers.push('Platform vector ciphertext is present but no usable storage keyring is available.');
|
|
1047
|
+
}
|
|
1048
|
+
else {
|
|
1049
|
+
try {
|
|
1050
|
+
new SqliteEncryptedVectorStore({
|
|
1051
|
+
database,
|
|
1052
|
+
keyring: storageKeyring,
|
|
1053
|
+
}).verifyIntegrity(options.vectorIntegrityLimits);
|
|
1054
|
+
}
|
|
1055
|
+
catch (error) {
|
|
1056
|
+
blockers.push(error instanceof VectorStoreError &&
|
|
1057
|
+
error.code === 'verification-limit'
|
|
1058
|
+
? 'Platform vector storage exceeds bounded recovery integrity verification limits.'
|
|
1059
|
+
: 'Platform vector ciphertext failed read-only integrity verification with the configured storage keys.');
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
if (actualTables.has('work_tasks')) {
|
|
1064
|
+
workTasks = database
|
|
1065
|
+
.prepare('SELECT id, status, volume_name, host_path FROM work_tasks ORDER BY id')
|
|
1066
|
+
.all();
|
|
1067
|
+
tasksByStatus = groupedCounts(database, 'work_tasks');
|
|
1068
|
+
activePreviews = Number(database
|
|
1069
|
+
.prepare("SELECT COUNT(*) AS count FROM work_tasks WHERE preview_status IN ('starting', 'running')")
|
|
1070
|
+
.get().count);
|
|
1071
|
+
if (activePreviews > 0) {
|
|
1072
|
+
blockers.push(`${activePreviews} Work preview${activePreviews === 1 ? ' is' : 's are'} active; stop previews before taking a recovery snapshot.`);
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
if (actualTables.has('work_runs')) {
|
|
1076
|
+
activeRuns = Number(database
|
|
1077
|
+
.prepare("SELECT COUNT(*) AS count FROM work_runs WHERE status IN ('queued', 'preparing', 'running')")
|
|
1078
|
+
.get().count);
|
|
1079
|
+
if (activeRuns > 0) {
|
|
1080
|
+
blockers.push(`${activeRuns} Work run${activeRuns === 1 ? ' is' : 's are'} active; quiesce Work before taking a recovery snapshot.`);
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
if (actualTables.has('media_generation_jobs')) {
|
|
1084
|
+
mediaJobsByStatus = groupedCounts(database, 'media_generation_jobs');
|
|
1085
|
+
mediaJobTotal = Object.values(mediaJobsByStatus).reduce((sum, count) => sum + count, 0);
|
|
1086
|
+
mediaJobActive =
|
|
1087
|
+
(mediaJobsByStatus.pending || 0) +
|
|
1088
|
+
(mediaJobsByStatus.in_progress || 0);
|
|
1089
|
+
if (mediaJobActive > 0) {
|
|
1090
|
+
blockers.push(`${mediaJobActive} media generation job${mediaJobActive === 1 ? ' is' : 's are'} active; wait for completion before taking a recovery snapshot.`);
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
const durableTables = [
|
|
1094
|
+
'platform_jobs',
|
|
1095
|
+
'platform_job_attempts',
|
|
1096
|
+
'platform_event_stream_heads',
|
|
1097
|
+
'platform_events',
|
|
1098
|
+
];
|
|
1099
|
+
durableSubstrateAvailable =
|
|
1100
|
+
schemaInspection.compatible &&
|
|
1101
|
+
durableTables.every(table => actualTables.has(table));
|
|
1102
|
+
if (durableSubstrateAvailable) {
|
|
1103
|
+
try {
|
|
1104
|
+
const durableDatabase = database;
|
|
1105
|
+
durableDatabase.transaction(() => {
|
|
1106
|
+
durableJobsByState = groupedKnownCounts(durableDatabase, 'platform_jobs', 'state', DURABLE_JOB_STATES);
|
|
1107
|
+
durableJobTotal = Object.values(durableJobsByState).reduce((sum, count) => sum + count, 0);
|
|
1108
|
+
durableJobRunning = durableJobsByState.running;
|
|
1109
|
+
durableAttemptsByOutcome = groupedKnownCounts(durableDatabase, 'platform_job_attempts', 'outcome', DURABLE_ATTEMPT_OUTCOMES);
|
|
1110
|
+
durableAttemptTotal = Object.values(durableAttemptsByOutcome).reduce((sum, count) => sum + count, 0);
|
|
1111
|
+
durableAttemptActive = durableAttemptsByOutcome.running;
|
|
1112
|
+
const eventCounts = durableDatabase
|
|
1113
|
+
.prepare(`SELECT COUNT(*) AS total,
|
|
1114
|
+
COALESCE(MAX(global_cursor), 0) AS last_cursor
|
|
1115
|
+
FROM platform_events`)
|
|
1116
|
+
.get();
|
|
1117
|
+
durableEventTotal = Number(eventCounts.total);
|
|
1118
|
+
durableEventLastCursor = Number(eventCounts.last_cursor);
|
|
1119
|
+
durableEventStreams = Number(durableDatabase
|
|
1120
|
+
.prepare('SELECT COUNT(*) AS count FROM platform_event_stream_heads')
|
|
1121
|
+
.get().count);
|
|
1122
|
+
const verified = verifyDurablePayloadIntegrity(durableDatabase, storageKeyring, options.durablePayloadIntegrityLimits);
|
|
1123
|
+
durablePayloadIntegrity = {
|
|
1124
|
+
...verified,
|
|
1125
|
+
encryptedAuthenticated: true,
|
|
1126
|
+
referenceTargetsVerified: false,
|
|
1127
|
+
};
|
|
1128
|
+
})();
|
|
1129
|
+
}
|
|
1130
|
+
catch (error) {
|
|
1131
|
+
blockers.push(error instanceof DurablePayloadIntegrityError &&
|
|
1132
|
+
error.code === 'verification-limit'
|
|
1133
|
+
? 'Durable job and event payloads exceed bounded recovery integrity verification limits.'
|
|
1134
|
+
: 'Durable job or event payload integrity verification failed with the configured storage keys.');
|
|
1135
|
+
}
|
|
1136
|
+
if (durableJobRunning > 0) {
|
|
1137
|
+
blockers.push(`${durableJobRunning} durable job${durableJobRunning === 1 ? ' is' : 's are'} running; quiesce durable workers before taking a recovery snapshot.`);
|
|
1138
|
+
}
|
|
1139
|
+
if (durableAttemptActive !== durableJobRunning) {
|
|
1140
|
+
blockers.push('Durable running-job and active-attempt counts are inconsistent.');
|
|
1141
|
+
}
|
|
1142
|
+
if (durablePayloadIntegrity.referenceRecords > 0) {
|
|
1143
|
+
warnings.push('Opaque durable payload references were syntax-checked, but their target existence and authorization could not be verified.');
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
catch {
|
|
1148
|
+
blockers.push('The SQLite database could not be opened or checked read-only.');
|
|
1149
|
+
quickCheck = 'failed';
|
|
1150
|
+
}
|
|
1151
|
+
finally {
|
|
1152
|
+
database?.close();
|
|
1153
|
+
if (inspectionDirectory) {
|
|
1154
|
+
fs.rmSync(inspectionDirectory, { recursive: true, force: true });
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
const configuredRuntimeBackend = env.WORK_RUNTIME_BACKEND?.trim() || 'docker';
|
|
1159
|
+
const runtimeBackend = configuredRuntimeBackend === 'kubernetes' ? 'kubernetes' : 'docker';
|
|
1160
|
+
if (!['docker', 'kubernetes'].includes(configuredRuntimeBackend)) {
|
|
1161
|
+
blockers.push(`WORK_RUNTIME_BACKEND is invalid; expected docker or kubernetes.`);
|
|
1162
|
+
}
|
|
1163
|
+
const externalWorkTasks = workTasks.filter(task => !task.host_path);
|
|
1164
|
+
const inspectWorkResources = options.inspectWorkResources || defaultWorkResourceInspector;
|
|
1165
|
+
const resourceInspection = await inspectWorkResources({
|
|
1166
|
+
backend: runtimeBackend,
|
|
1167
|
+
resources: externalWorkTasks.map(task => ({
|
|
1168
|
+
taskId: task.id,
|
|
1169
|
+
name: task.volume_name,
|
|
1170
|
+
})),
|
|
1171
|
+
env,
|
|
1172
|
+
});
|
|
1173
|
+
const workspaces = workTasks.map(task => {
|
|
1174
|
+
if (task.host_path) {
|
|
1175
|
+
return {
|
|
1176
|
+
taskId: task.id,
|
|
1177
|
+
kind: 'host-path',
|
|
1178
|
+
pathFingerprint: crypto
|
|
1179
|
+
.createHash('sha256')
|
|
1180
|
+
.update(path.resolve(task.host_path))
|
|
1181
|
+
.digest('hex')
|
|
1182
|
+
.slice(0, 16),
|
|
1183
|
+
present: Boolean(safeStat(task.host_path)),
|
|
1184
|
+
includedInDataDirectory: false,
|
|
1185
|
+
};
|
|
1186
|
+
}
|
|
1187
|
+
return {
|
|
1188
|
+
taskId: task.id,
|
|
1189
|
+
kind: runtimeBackend === 'kubernetes'
|
|
1190
|
+
? 'kubernetes-pvc'
|
|
1191
|
+
: 'docker-volume',
|
|
1192
|
+
name: task.volume_name,
|
|
1193
|
+
present: resourceInspection.available
|
|
1194
|
+
? Boolean(resourceInspection.matches[task.id])
|
|
1195
|
+
: null,
|
|
1196
|
+
includedInDataDirectory: false,
|
|
1197
|
+
};
|
|
1198
|
+
});
|
|
1199
|
+
const invalidWorkspaces = workspaces.filter(workspace => workspace.present === false).length;
|
|
1200
|
+
if (invalidWorkspaces > 0) {
|
|
1201
|
+
blockers.push(`${invalidWorkspaces} expected Work workspace${invalidWorkspaces === 1 ? ' is missing or has' : 's are missing or have'} mismatched ownership metadata.`);
|
|
1202
|
+
}
|
|
1203
|
+
if (externalWorkTasks.length > 0 && !resourceInspection.available) {
|
|
1204
|
+
blockers.push(resourceInspection.message ||
|
|
1205
|
+
'External Work workspace presence could not be verified.');
|
|
1206
|
+
}
|
|
1207
|
+
const hostWorkspaces = workspaces.filter(workspace => workspace.kind === 'host-path');
|
|
1208
|
+
if (hostWorkspaces.length > 0) {
|
|
1209
|
+
exclusions.push(`${hostWorkspaces.length} host-bound Work workspace${hostWorkspaces.length === 1 ? ' is' : 's are'} outside the Libre WebUI data directory and require a separate backup.`);
|
|
1210
|
+
}
|
|
1211
|
+
if (externalWorkTasks.length > 0) {
|
|
1212
|
+
exclusions.push(`${externalWorkTasks.length} Work ${runtimeBackend === 'kubernetes' ? 'PVC' : 'Docker volume'} workspace${externalWorkTasks.length === 1 ? ' is' : 's are'} outside the Libre WebUI data directory and require a coordinated snapshot.`);
|
|
1213
|
+
}
|
|
1214
|
+
const pluginLocations = options.pluginPathLocations || this.defaults.pluginPathLocations || {};
|
|
1215
|
+
const backendDirectory = pluginLocations.backendDirectory || BACKEND_DIRECTORY;
|
|
1216
|
+
const projectDirectory = pluginLocations.projectDirectory || PROJECT_DIRECTORY;
|
|
1217
|
+
const pluginsPath = resolvePluginsDirectory(env, dataDir, backendDirectory);
|
|
1218
|
+
const bundledPluginsDirectory = path.resolve(pluginLocations.bundledDirectory || path.join(projectDirectory, 'plugins'));
|
|
1219
|
+
const pluginSourcePaths = [
|
|
1220
|
+
{ kind: 'configured', path: path.resolve(pluginsPath) },
|
|
1221
|
+
...(options.legacyPluginsDirectories ??
|
|
1222
|
+
this.defaults.legacyPluginsDirectories ??
|
|
1223
|
+
resolveLegacyPluginsDirectories(env, {
|
|
1224
|
+
backendDirectory,
|
|
1225
|
+
projectDirectory,
|
|
1226
|
+
historicalWorkingDirectory: pluginLocations.historicalWorkingDirectory || process.cwd(),
|
|
1227
|
+
})).map(legacyPath => ({
|
|
1228
|
+
kind: 'legacy',
|
|
1229
|
+
path: path.resolve(legacyPath),
|
|
1230
|
+
})),
|
|
1231
|
+
].filter(source => source.path !== bundledPluginsDirectory);
|
|
1232
|
+
const seenPluginPaths = new Set();
|
|
1233
|
+
const pluginSources = [];
|
|
1234
|
+
for (const source of pluginSourcePaths) {
|
|
1235
|
+
if (seenPluginPaths.has(source.path))
|
|
1236
|
+
continue;
|
|
1237
|
+
seenPluginPaths.add(source.path);
|
|
1238
|
+
const lexicalInsideDataDirectory = (() => {
|
|
1239
|
+
const relative = path.relative(dataDir, source.path);
|
|
1240
|
+
return (relative === '' ||
|
|
1241
|
+
(relative !== '..' &&
|
|
1242
|
+
!relative.startsWith(`..${path.sep}`) &&
|
|
1243
|
+
!path.isAbsolute(relative)));
|
|
1244
|
+
})();
|
|
1245
|
+
const inspection = inspectPluginDirectory(source.path, lexicalInsideDataDirectory
|
|
1246
|
+
? dataDir
|
|
1247
|
+
: source.kind === 'legacy' && source.path.startsWith(backendDirectory)
|
|
1248
|
+
? backendDirectory
|
|
1249
|
+
: path.dirname(source.path));
|
|
1250
|
+
const includedInDataDirectory = !inspection.unsafePath && isPathWithin(dataDir, source.path);
|
|
1251
|
+
pluginSources.push({
|
|
1252
|
+
kind: source.kind,
|
|
1253
|
+
...inspection,
|
|
1254
|
+
includedInDataDirectory,
|
|
1255
|
+
});
|
|
1256
|
+
if (inspection.invalidEntries > 0) {
|
|
1257
|
+
blockers.push(`${inspection.invalidEntries} custom plugin ${inspection.invalidEntries === 1 ? 'entry is' : 'entries are'} symlinked, non-regular, or unreadable; recovery refuses to follow or omit it.`);
|
|
1258
|
+
}
|
|
1259
|
+
if (!includedInDataDirectory && inspection.definitions > 0) {
|
|
1260
|
+
exclusions.push(`${source.kind === 'legacy' ? 'A legacy' : 'The configured'} custom plugin directory is outside the Libre WebUI data directory and requires a separate backup.`);
|
|
1261
|
+
blockers.push(`${inspection.definitions} custom plugin definition${inspection.definitions === 1 ? ' is' : 's are'} outside the configured data directory; coordinate a separate plugin snapshot before backup.`);
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
const configuredPluginSource = pluginSources.find(source => source.kind === 'configured' &&
|
|
1265
|
+
source.path === path.resolve(pluginsPath));
|
|
1266
|
+
const pluginsIncludedInDataDirectory = Boolean(configuredPluginSource?.includedInDataDirectory);
|
|
1267
|
+
const pluginsStat = safeLstat(pluginsPath);
|
|
1268
|
+
const pluginFiles = configuredPluginSource?.definitions || 0;
|
|
1269
|
+
const pluginBytes = configuredPluginSource?.bytes || 0;
|
|
1270
|
+
const localBlobStorePath = path.join(dataDir, 'blobs');
|
|
1271
|
+
// Do not follow a blob-root symlink even for inventory counts. The
|
|
1272
|
+
// integrity verifier below reports the non-physical root as a blocker.
|
|
1273
|
+
const localBlobStoreStat = safeLstat(localBlobStorePath);
|
|
1274
|
+
const localBlobStoreSize = localBlobStoreStat?.isDirectory()
|
|
1275
|
+
? scanDirectory(localBlobStorePath)
|
|
1276
|
+
: { files: 0, bytes: 0, errors: 0 };
|
|
1277
|
+
if (localBlobStoreSize.errors > 0) {
|
|
1278
|
+
blockers.push(`${localBlobStoreSize.errors} local blob-store entr${localBlobStoreSize.errors === 1 ? 'y could' : 'ies could'} not be inventoried.`);
|
|
1279
|
+
}
|
|
1280
|
+
if (localBlobStoreStat) {
|
|
1281
|
+
if (!storageKeyring) {
|
|
1282
|
+
if (localBlobStoreSize.files > 0) {
|
|
1283
|
+
blockers.push('Local encrypted blob data is present but no usable storage keyring is available.');
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
else {
|
|
1287
|
+
try {
|
|
1288
|
+
await new LocalEncryptedBlobStore({
|
|
1289
|
+
rootDirectory: localBlobStorePath,
|
|
1290
|
+
keyring: storageKeyring,
|
|
1291
|
+
}).verifyIntegrity(options.blobIntegrityLimits);
|
|
1292
|
+
}
|
|
1293
|
+
catch (error) {
|
|
1294
|
+
blockers.push(error instanceof BlobStoreError &&
|
|
1295
|
+
error.code === 'verification-limit'
|
|
1296
|
+
? 'Local blob storage exceeds bounded recovery integrity verification limits.'
|
|
1297
|
+
: 'Local blob ciphertext failed read-only integrity verification with the configured storage keys.');
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
const inventory = {
|
|
1302
|
+
format: 'libre-webui-recovery-inventory',
|
|
1303
|
+
version: 1,
|
|
1304
|
+
generatedAt: now.toISOString(),
|
|
1305
|
+
readOnly: true,
|
|
1306
|
+
restoreReady: false,
|
|
1307
|
+
application: {
|
|
1308
|
+
name: 'libre-webui',
|
|
1309
|
+
version: loadAppPackage(import.meta.url).version || '0.0.0',
|
|
1310
|
+
nodeVersion: process.version,
|
|
1311
|
+
platform: `${process.platform}/${process.arch}`,
|
|
1312
|
+
},
|
|
1313
|
+
database: {
|
|
1314
|
+
backend: 'sqlite',
|
|
1315
|
+
path: databasePath,
|
|
1316
|
+
present: Boolean(databaseStat?.isFile()),
|
|
1317
|
+
bytes: databaseStat?.size || 0,
|
|
1318
|
+
companionFiles: [
|
|
1319
|
+
{
|
|
1320
|
+
kind: 'wal',
|
|
1321
|
+
present: Boolean(walStat?.isFile()),
|
|
1322
|
+
bytes: walStat?.size || 0,
|
|
1323
|
+
},
|
|
1324
|
+
{
|
|
1325
|
+
kind: 'shm',
|
|
1326
|
+
present: Boolean(shmStat?.isFile()),
|
|
1327
|
+
bytes: shmStat?.size || 0,
|
|
1328
|
+
},
|
|
1329
|
+
],
|
|
1330
|
+
open: databaseOpen,
|
|
1331
|
+
quickCheck,
|
|
1332
|
+
foreignKeyViolations,
|
|
1333
|
+
schema: {
|
|
1334
|
+
userVersion,
|
|
1335
|
+
...(schemaFingerprint ? { fingerprint: schemaFingerprint } : {}),
|
|
1336
|
+
actualTableCount,
|
|
1337
|
+
ledgerPresent,
|
|
1338
|
+
currentVersion: currentSchemaVersion,
|
|
1339
|
+
targetVersion: targetSchemaVersion,
|
|
1340
|
+
minimumSupportedVersion: minimumSupportedSchemaVersion,
|
|
1341
|
+
appliedMigrations,
|
|
1342
|
+
missing: missingSchema,
|
|
1343
|
+
},
|
|
1344
|
+
},
|
|
1345
|
+
encryption: { ...encryption, legacyCiphertext },
|
|
1346
|
+
configuration: {
|
|
1347
|
+
storageEncryption,
|
|
1348
|
+
secretPresence: {
|
|
1349
|
+
encryptionKeyEnvironment: Boolean(env.ENCRYPTION_KEY?.trim()),
|
|
1350
|
+
jwtSecret: Boolean(env.JWT_SECRET?.trim()),
|
|
1351
|
+
sessionSecret: Boolean(env.SESSION_SECRET?.trim()),
|
|
1352
|
+
},
|
|
1353
|
+
},
|
|
1354
|
+
storage: {
|
|
1355
|
+
dataDirectory: {
|
|
1356
|
+
path: dataDir,
|
|
1357
|
+
present: dataPresent,
|
|
1358
|
+
readable: dataReadable,
|
|
1359
|
+
writable: dataWritable,
|
|
1360
|
+
files: dataSize.files,
|
|
1361
|
+
bytes: dataSize.bytes,
|
|
1362
|
+
scanErrors: dataSize.errors,
|
|
1363
|
+
},
|
|
1364
|
+
customPlugins: {
|
|
1365
|
+
path: pluginsPath,
|
|
1366
|
+
present: Boolean(pluginsStat?.isDirectory() && !pluginsStat.isSymbolicLink()),
|
|
1367
|
+
includedInDataDirectory: pluginsIncludedInDataDirectory,
|
|
1368
|
+
definitions: pluginFiles,
|
|
1369
|
+
bytes: pluginBytes,
|
|
1370
|
+
sources: pluginSources,
|
|
1371
|
+
},
|
|
1372
|
+
localBlobStore: {
|
|
1373
|
+
path: localBlobStorePath,
|
|
1374
|
+
present: Boolean(localBlobStoreStat?.isDirectory()),
|
|
1375
|
+
files: localBlobStoreSize.files,
|
|
1376
|
+
bytes: localBlobStoreSize.bytes,
|
|
1377
|
+
scanErrors: localBlobStoreSize.errors,
|
|
1378
|
+
},
|
|
1379
|
+
embeddedBlobs: { generatedMedia, voiceReferences, documentText },
|
|
1380
|
+
embeddedVectors: {
|
|
1381
|
+
legacyDocumentChunks: legacyDocumentVectors,
|
|
1382
|
+
platform: {
|
|
1383
|
+
...platformVectors,
|
|
1384
|
+
aclRecords: platformVectorAclRecords,
|
|
1385
|
+
attributeRecords: platformVectorAttributeRecords,
|
|
1386
|
+
},
|
|
1387
|
+
},
|
|
1388
|
+
},
|
|
1389
|
+
work: {
|
|
1390
|
+
runtimeBackend,
|
|
1391
|
+
tasks: workTasks.length,
|
|
1392
|
+
tasksByStatus,
|
|
1393
|
+
activeRuns,
|
|
1394
|
+
activePreviews,
|
|
1395
|
+
resourcesVerified: externalWorkTasks.length === 0 ||
|
|
1396
|
+
(resourceInspection.available &&
|
|
1397
|
+
externalWorkTasks.every(task => resourceInspection.matches[task.id] === true)),
|
|
1398
|
+
workspaces,
|
|
1399
|
+
},
|
|
1400
|
+
jobs: {
|
|
1401
|
+
implementation: 'durable-with-legacy-media-compatibility',
|
|
1402
|
+
total: mediaJobTotal,
|
|
1403
|
+
active: mediaJobActive,
|
|
1404
|
+
byStatus: mediaJobsByStatus,
|
|
1405
|
+
durableWorkerAvailable: durableSubstrateAvailable,
|
|
1406
|
+
durable: {
|
|
1407
|
+
substrateAvailable: durableSubstrateAvailable,
|
|
1408
|
+
handlerWorkerBootstrapped: durableSubstrateAvailable,
|
|
1409
|
+
externalWorkerAvailable: durableSubstrateAvailable,
|
|
1410
|
+
total: durableJobTotal,
|
|
1411
|
+
running: durableJobRunning,
|
|
1412
|
+
byState: durableJobsByState,
|
|
1413
|
+
attempts: {
|
|
1414
|
+
total: durableAttemptTotal,
|
|
1415
|
+
active: durableAttemptActive,
|
|
1416
|
+
byOutcome: durableAttemptsByOutcome,
|
|
1417
|
+
},
|
|
1418
|
+
events: {
|
|
1419
|
+
streams: durableEventStreams,
|
|
1420
|
+
total: durableEventTotal,
|
|
1421
|
+
lastCursor: durableEventLastCursor,
|
|
1422
|
+
},
|
|
1423
|
+
payloadIntegrity: durablePayloadIntegrity,
|
|
1424
|
+
},
|
|
1425
|
+
},
|
|
1426
|
+
blockers: unique(blockers),
|
|
1427
|
+
warnings: unique(warnings),
|
|
1428
|
+
exclusions: unique(exclusions),
|
|
1429
|
+
};
|
|
1430
|
+
inventory.restoreReady = inventory.blockers.length === 0;
|
|
1431
|
+
return inventory;
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
export const recoveryInventoryService = new RecoveryInventoryService();
|
|
1435
|
+
export default recoveryInventoryService;
|
|
1436
|
+
//# sourceMappingURL=recoveryInventoryService.js.map
|