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
|
@@ -23,24 +23,97 @@ import codexOAuthService, { CODEX_OAUTH_PLUGIN_ID, } from './codexOAuthService.j
|
|
|
23
23
|
import { userModel } from '../models/userModel.js';
|
|
24
24
|
import pluginCredentialsService from './pluginCredentialsService.js';
|
|
25
25
|
import pluginVariablesService from './pluginVariablesService.js';
|
|
26
|
+
import { ensurePluginCacheInvalidationSubscription, publishPluginCacheInvalidation, registerPluginCacheInvalidationListener, } from './pluginCacheInvalidation.js';
|
|
26
27
|
import pluginActivationService from './pluginActivationService.js';
|
|
27
28
|
import { PluginAudioGenerationService } from './pluginAudioGenerationService.js';
|
|
28
29
|
import { PluginCapabilityRegistryService } from './pluginCapabilityRegistryService.js';
|
|
29
30
|
import { PluginEmbeddingService } from './pluginEmbeddingService.js';
|
|
30
31
|
import { PluginImageGenerationService } from './pluginImageGenerationService.js';
|
|
32
|
+
import { PluginSTTService } from './pluginSTTService.js';
|
|
31
33
|
import { PluginTTSService } from './pluginTTSService.js';
|
|
32
34
|
import { PluginVideoGenerationService } from './pluginVideoGenerationService.js';
|
|
33
35
|
import { applyPluginDefinitionPolicy, buildPluginChatPayload, convertProviderResponse, getOpenAICompatibleSamplingParameters, resolvePluginChatParameters, toOpenAICompatibleMessages, } from '../utils/pluginChatAdapter.js';
|
|
34
36
|
import { streamAnthropicResponse, streamOpenAICompatibleResponse, streamOpenAIResponsesResponse, } from '../utils/pluginStreamAdapter.js';
|
|
35
37
|
import { applyModelEndpointTemplate, assertSafePluginEndpoint, buildPluginAuthHeaders, buildPluginModelDiscoveryHeaders, pluginRequiresApiKey, resolvePluginApiConfig, resolvePluginEndpoint, resolvePluginModelsEndpoint, validatePluginModel, } from '../utils/pluginValidation.js';
|
|
36
38
|
import { createLogger } from '../utils/logger.js';
|
|
39
|
+
import { isChatGenerationCancelled, throwIfChatGenerationCancelled, } from '../utils/chatCancellation.js';
|
|
37
40
|
import { resolveBundledPluginsDir } from '../utils/packagePaths.js';
|
|
38
|
-
import {
|
|
41
|
+
import { BACKEND_DIRECTORY, PROJECT_DIRECTORY, resolveDataDirectory, resolveLegacyPluginsDirectories, resolvePhysicalPathCandidate, resolvePluginsDirectory, } from '../utils/dataDirectory.js';
|
|
42
|
+
import { getPersistence } from '../persistence/index.js';
|
|
39
43
|
import { createOpenAIResponsesStateScope, createPluginCredentialFingerprint, OPENAI_RESPONSES_INCOMPLETE_REASON_METADATA_KEY, } from '../utils/openAIResponsesAdapter.js';
|
|
40
44
|
import { getPluginConnectionVariableNames } from '../utils/pluginConnectionVariables.js';
|
|
41
45
|
import pluginUsageService from './pluginUsageService.js';
|
|
42
46
|
import { getPluginDefinitionFingerprint, matchesBundledPluginTrustAnchor, } from '../utils/pluginDefinitionTrust.js';
|
|
47
|
+
import { encryptionService } from './encryptionService.js';
|
|
43
48
|
const logger = createLogger('plugins');
|
|
49
|
+
const hasSymlinkPathComponentFromRoot = (root, target) => {
|
|
50
|
+
const resolvedRoot = path.resolve(root);
|
|
51
|
+
const resolvedTarget = path.resolve(target);
|
|
52
|
+
const relative = path.relative(resolvedRoot, resolvedTarget);
|
|
53
|
+
if (relative === '..' ||
|
|
54
|
+
relative.startsWith(`..${path.sep}`) ||
|
|
55
|
+
path.isAbsolute(relative)) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
let current = resolvedRoot;
|
|
59
|
+
for (const component of relative.split(path.sep)) {
|
|
60
|
+
if (!component)
|
|
61
|
+
continue;
|
|
62
|
+
current = path.join(current, component);
|
|
63
|
+
try {
|
|
64
|
+
if (fs.lstatSync(current).isSymbolicLink())
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
if (error.code === 'ENOENT')
|
|
69
|
+
return false;
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return false;
|
|
74
|
+
};
|
|
75
|
+
const isRegularPluginDirectory = (directory) => {
|
|
76
|
+
try {
|
|
77
|
+
const stat = fs.lstatSync(directory);
|
|
78
|
+
return stat.isDirectory() && !stat.isSymbolicLink();
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
const isRegularPluginDefinition = (filePath) => {
|
|
85
|
+
try {
|
|
86
|
+
if (!isRegularPluginDirectory(path.dirname(filePath)))
|
|
87
|
+
return false;
|
|
88
|
+
const stat = fs.lstatSync(filePath);
|
|
89
|
+
return stat.isFile() && !stat.isSymbolicLink();
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
const readRegularPluginDefinition = (filePath) => {
|
|
96
|
+
if (!isRegularPluginDirectory(path.dirname(filePath))) {
|
|
97
|
+
throw new Error('Plugin definition directory is not a regular directory');
|
|
98
|
+
}
|
|
99
|
+
const namedStat = fs.lstatSync(filePath);
|
|
100
|
+
if (!namedStat.isFile() || namedStat.isSymbolicLink()) {
|
|
101
|
+
throw new Error('Plugin definition is not a regular file');
|
|
102
|
+
}
|
|
103
|
+
const descriptor = fs.openSync(filePath, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW || 0));
|
|
104
|
+
try {
|
|
105
|
+
const openedStat = fs.fstatSync(descriptor);
|
|
106
|
+
if (!openedStat.isFile() ||
|
|
107
|
+
openedStat.dev !== namedStat.dev ||
|
|
108
|
+
openedStat.ino !== namedStat.ino) {
|
|
109
|
+
throw new Error('Plugin definition changed during inspection');
|
|
110
|
+
}
|
|
111
|
+
return fs.readFileSync(descriptor, 'utf8');
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
fs.closeSync(descriptor);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
44
117
|
const readPositiveIntEnv = (name, fallback) => {
|
|
45
118
|
const parsed = Number(process.env[name]);
|
|
46
119
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
@@ -84,6 +157,9 @@ function getPluginRoutingAuthProjection(plugin) {
|
|
|
84
157
|
endpoint: capability.endpoint ?? null,
|
|
85
158
|
models_endpoint: capability.models_endpoint ?? null,
|
|
86
159
|
endpoint_variable: config.endpoint_variable ?? capability.endpoint_variable ?? null,
|
|
160
|
+
models_endpoint_variable: config.models_endpoint_variable ?? null,
|
|
161
|
+
voice_clone_endpoint: config.voice_clone_endpoint ?? null,
|
|
162
|
+
voice_clone_endpoint_variable: config.voice_clone_endpoint_variable ?? null,
|
|
87
163
|
};
|
|
88
164
|
});
|
|
89
165
|
const definitions = new Map((plugin.variables || []).map(definition => [definition.name, definition]));
|
|
@@ -119,7 +195,7 @@ function getPluginRoutingAuthProjection(plugin) {
|
|
|
119
195
|
});
|
|
120
196
|
}
|
|
121
197
|
export class PluginService {
|
|
122
|
-
constructor() {
|
|
198
|
+
constructor(options = {}) {
|
|
123
199
|
this.discoveredModelsCache = new Map();
|
|
124
200
|
this.discoveredModelsUpdatedAt = new Map();
|
|
125
201
|
this.discoveryAttemptedAt = new Map();
|
|
@@ -128,84 +204,229 @@ export class PluginService {
|
|
|
128
204
|
this.discoveredCapabilityModelsUpdatedAt = new Map();
|
|
129
205
|
this.capabilityDiscoveryAttemptedAt = new Map();
|
|
130
206
|
this.inflightCapabilityDiscovery = new Map();
|
|
207
|
+
this.discoveryCacheRevisions = new Map();
|
|
208
|
+
this.capabilityDiscoveryCacheRevisions = new Map();
|
|
131
209
|
this.bundledRoutingProjectionCache = new Map();
|
|
210
|
+
this.sharedDefinitionIds = new Set();
|
|
211
|
+
this.sharedPluginDefinitions =
|
|
212
|
+
getPersistence(encryptionService).dialect === 'postgres';
|
|
132
213
|
this.bundledPluginsDir = resolveBundledPluginsDir(import.meta.url);
|
|
133
|
-
this.
|
|
134
|
-
this.
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
214
|
+
this.pluginsDir = resolvePluginsDirectory();
|
|
215
|
+
this.legacyPluginsDirs =
|
|
216
|
+
options.legacyPluginsDirectories ?? resolveLegacyPluginsDirectories();
|
|
217
|
+
const activePluginDirectories = new Set([this.bundledPluginsDir, this.pluginsDir, ...this.legacyPluginsDirs].map(directory => resolvePhysicalPathCandidate(directory)));
|
|
218
|
+
this.historicalPluginConflictDirs = options.legacyPluginsDirectories
|
|
219
|
+
? []
|
|
220
|
+
: resolveLegacyPluginsDirectories(process.env, {
|
|
221
|
+
historicalWorkingDirectory: process.cwd(),
|
|
222
|
+
}).filter(directory => !activePluginDirectories.has(resolvePhysicalPathCandidate(directory)));
|
|
223
|
+
this.pluginReadDirs = this.sharedPluginDefinitions
|
|
224
|
+
? [this.bundledPluginsDir]
|
|
225
|
+
: Array.from(new Set([
|
|
226
|
+
this.bundledPluginsDir,
|
|
227
|
+
...this.legacyPluginsDirs,
|
|
228
|
+
this.pluginsDir,
|
|
229
|
+
]));
|
|
230
|
+
if (this.sharedPluginDefinitions) {
|
|
231
|
+
this.assertNoLocalCustomDefinitions();
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
this.ensurePluginsDirectory();
|
|
235
|
+
}
|
|
142
236
|
this.embeddingService = new PluginEmbeddingService({
|
|
143
|
-
getAllPlugins: userId => this.
|
|
237
|
+
getAllPlugins: userId => this.getActivePluginsUnchecked(userId),
|
|
144
238
|
getApiKey: (plugin, userId) => this.getApiKey(plugin, userId),
|
|
145
239
|
getPluginVariables: (plugin, userId) => this.getPluginVariables(plugin, userId),
|
|
146
240
|
validateEndpointUrl: endpoint => this.validateEndpointUrl(endpoint),
|
|
147
241
|
recordUsage: usage => pluginUsageService.record(usage),
|
|
148
242
|
});
|
|
149
243
|
this.ttsService = new PluginTTSService({
|
|
150
|
-
getAllPlugins: userId => this.
|
|
151
|
-
getPlugin: (id, userId) => this.
|
|
244
|
+
getAllPlugins: userId => this.getActivePluginsUnchecked(userId),
|
|
245
|
+
getPlugin: (id, userId) => this.getPluginUnchecked(id, userId),
|
|
246
|
+
getApiKey: (plugin, userId) => this.getApiKey(plugin, userId),
|
|
247
|
+
getPluginVariables: (plugin, userId) => this.getPluginVariables(plugin, userId),
|
|
248
|
+
validateEndpointUrl: endpoint => this.validateEndpointUrl(endpoint),
|
|
249
|
+
recordUsage: usage => pluginUsageService.record(usage),
|
|
250
|
+
});
|
|
251
|
+
this.sttService = new PluginSTTService({
|
|
252
|
+
getAllPlugins: userId => this.getActivePluginsUnchecked(userId),
|
|
253
|
+
getPlugin: (id, userId) => this.getPluginUnchecked(id, userId),
|
|
152
254
|
getApiKey: (plugin, userId) => this.getApiKey(plugin, userId),
|
|
153
255
|
getPluginVariables: (plugin, userId) => this.getPluginVariables(plugin, userId),
|
|
154
256
|
validateEndpointUrl: endpoint => this.validateEndpointUrl(endpoint),
|
|
155
257
|
recordUsage: usage => pluginUsageService.record(usage),
|
|
156
258
|
});
|
|
157
259
|
this.imageGenerationService = new PluginImageGenerationService({
|
|
158
|
-
getAllPlugins: userId => this.
|
|
159
|
-
getPlugin: (id, userId) => this.
|
|
260
|
+
getAllPlugins: userId => this.getActivePluginsUnchecked(userId),
|
|
261
|
+
getPlugin: (id, userId) => this.getPluginUnchecked(id, userId),
|
|
160
262
|
getApiKey: (plugin, userId) => this.getApiKey(plugin, userId),
|
|
161
263
|
getPluginVariables: (plugin, userId) => this.getPluginVariables(plugin, userId),
|
|
162
264
|
validateEndpointUrl: endpoint => this.validateEndpointUrl(endpoint),
|
|
163
265
|
recordUsage: usage => pluginUsageService.record(usage),
|
|
164
266
|
});
|
|
165
267
|
this.audioGenerationService = new PluginAudioGenerationService({
|
|
166
|
-
getAllPlugins: userId => this.
|
|
167
|
-
getPlugin: (id, userId) => this.
|
|
268
|
+
getAllPlugins: userId => this.getActivePluginsUnchecked(userId),
|
|
269
|
+
getPlugin: (id, userId) => this.getPluginUnchecked(id, userId),
|
|
168
270
|
getApiKey: (plugin, userId) => this.getApiKey(plugin, userId),
|
|
169
271
|
getPluginVariables: (plugin, userId) => this.getPluginVariables(plugin, userId),
|
|
170
272
|
validateEndpointUrl: endpoint => this.validateEndpointUrl(endpoint),
|
|
171
273
|
recordUsage: usage => pluginUsageService.record(usage),
|
|
172
274
|
});
|
|
173
275
|
this.videoGenerationService = new PluginVideoGenerationService({
|
|
174
|
-
getAllPlugins: userId => this.
|
|
175
|
-
getPlugin: (id, userId) => this.
|
|
276
|
+
getAllPlugins: userId => this.getActivePluginsUnchecked(userId),
|
|
277
|
+
getPlugin: (id, userId) => this.getPluginUnchecked(id, userId),
|
|
176
278
|
getApiKey: (plugin, userId) => this.getApiKey(plugin, userId),
|
|
177
279
|
getPluginVariables: (plugin, userId) => this.getPluginVariables(plugin, userId),
|
|
178
280
|
validateEndpointUrl: endpoint => this.validateEndpointUrl(endpoint),
|
|
179
281
|
recordUsage: usage => pluginUsageService.record(usage),
|
|
180
282
|
});
|
|
181
283
|
this.capabilityRegistryService = new PluginCapabilityRegistryService({
|
|
182
|
-
getAllPlugins: userId => this.
|
|
284
|
+
getAllPlugins: userId => this.getActivePluginsUnchecked(userId),
|
|
183
285
|
getApiKey: (plugin, userId) => this.getApiKey(plugin, userId),
|
|
184
286
|
});
|
|
185
287
|
}
|
|
288
|
+
repositories() {
|
|
289
|
+
return getPersistence(encryptionService).repositories.extensions;
|
|
290
|
+
}
|
|
291
|
+
async ensureCacheInvalidation() {
|
|
292
|
+
this.removeCacheInvalidationListener ?? (this.removeCacheInvalidationListener = registerPluginCacheInvalidationListener(invalidation => this.invalidateRuntimeCaches(invalidation)));
|
|
293
|
+
await ensurePluginCacheInvalidationSubscription();
|
|
294
|
+
}
|
|
295
|
+
bumpDiscoveryRevision(key) {
|
|
296
|
+
this.discoveryCacheRevisions.set(key, (this.discoveryCacheRevisions.get(key) ?? 0) + 1);
|
|
297
|
+
}
|
|
298
|
+
bumpCapabilityDiscoveryRevision(key) {
|
|
299
|
+
this.capabilityDiscoveryCacheRevisions.set(key, (this.capabilityDiscoveryCacheRevisions.get(key) ?? 0) + 1);
|
|
300
|
+
}
|
|
301
|
+
invalidateRuntimeCaches(invalidation) {
|
|
302
|
+
const completionKeys = new Set([
|
|
303
|
+
...this.discoveredModelsCache.keys(),
|
|
304
|
+
...this.discoveredModelsUpdatedAt.keys(),
|
|
305
|
+
...this.discoveryAttemptedAt.keys(),
|
|
306
|
+
...this.inflightDiscovery.keys(),
|
|
307
|
+
...this.discoveryCacheRevisions.keys(),
|
|
308
|
+
]);
|
|
309
|
+
const capabilityKeys = new Set([
|
|
310
|
+
...this.discoveredCapabilityModelsCache.keys(),
|
|
311
|
+
...this.discoveredCapabilityModelsUpdatedAt.keys(),
|
|
312
|
+
...this.capabilityDiscoveryAttemptedAt.keys(),
|
|
313
|
+
...this.inflightCapabilityDiscovery.keys(),
|
|
314
|
+
...this.capabilityDiscoveryCacheRevisions.keys(),
|
|
315
|
+
]);
|
|
316
|
+
const completionMatches = (key) => {
|
|
317
|
+
if (invalidation.scope === 'plugin-user') {
|
|
318
|
+
return (key ===
|
|
319
|
+
this.discoveredModelsCacheKey(invalidation.pluginId, invalidation.userId));
|
|
320
|
+
}
|
|
321
|
+
if (invalidation.scope === 'plugin') {
|
|
322
|
+
return key.endsWith(`:${invalidation.pluginId}`);
|
|
323
|
+
}
|
|
324
|
+
return key.startsWith(`${invalidation.userId}:`);
|
|
325
|
+
};
|
|
326
|
+
const capabilityMatches = (key) => {
|
|
327
|
+
if (invalidation.scope === 'plugin-user') {
|
|
328
|
+
return key.startsWith(`${invalidation.userId}:${invalidation.pluginId}:`);
|
|
329
|
+
}
|
|
330
|
+
if (invalidation.scope === 'plugin') {
|
|
331
|
+
return key.includes(`:${invalidation.pluginId}:`);
|
|
332
|
+
}
|
|
333
|
+
return key.startsWith(`${invalidation.userId}:`);
|
|
334
|
+
};
|
|
335
|
+
for (const key of completionKeys) {
|
|
336
|
+
if (!completionMatches(key))
|
|
337
|
+
continue;
|
|
338
|
+
this.discoveredModelsCache.delete(key);
|
|
339
|
+
this.discoveredModelsUpdatedAt.delete(key);
|
|
340
|
+
this.discoveryAttemptedAt.delete(key);
|
|
341
|
+
this.inflightDiscovery.delete(key);
|
|
342
|
+
this.bumpDiscoveryRevision(key);
|
|
343
|
+
}
|
|
344
|
+
for (const key of capabilityKeys) {
|
|
345
|
+
if (!capabilityMatches(key))
|
|
346
|
+
continue;
|
|
347
|
+
this.discoveredCapabilityModelsCache.delete(key);
|
|
348
|
+
this.discoveredCapabilityModelsUpdatedAt.delete(key);
|
|
349
|
+
this.capabilityDiscoveryAttemptedAt.delete(key);
|
|
350
|
+
this.inflightCapabilityDiscovery.delete(key);
|
|
351
|
+
this.bumpCapabilityDiscoveryRevision(key);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
ensureLegacyActivationMigration() {
|
|
355
|
+
if (this.sharedPluginDefinitions)
|
|
356
|
+
return Promise.resolve();
|
|
357
|
+
this.legacyActivationMigration ?? (this.legacyActivationMigration = pluginActivationService.migrateLegacyStatus([...this.pluginReadDirs]
|
|
358
|
+
.reverse()
|
|
359
|
+
.filter(pluginsDir => path.resolve(pluginsDir) !== path.resolve(this.bundledPluginsDir)), pluginId => this.canMigrateLegacyActivation(pluginId)));
|
|
360
|
+
return this.legacyActivationMigration;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Team replicas must never pick a provider definition from node-local disk.
|
|
364
|
+
* Refuse startup when an old writable definition is present so an operator
|
|
365
|
+
* has to migrate it deliberately instead of getting replica-dependent
|
|
366
|
+
* routing, credentials, or activation state.
|
|
367
|
+
*/
|
|
368
|
+
assertNoLocalCustomDefinitions() {
|
|
369
|
+
const bundledDirectory = path.resolve(this.bundledPluginsDir);
|
|
370
|
+
const directories = new Set([
|
|
371
|
+
this.pluginsDir,
|
|
372
|
+
...this.legacyPluginsDirs,
|
|
373
|
+
...this.historicalPluginConflictDirs,
|
|
374
|
+
]);
|
|
375
|
+
for (const directory of directories) {
|
|
376
|
+
if (path.resolve(directory) === bundledDirectory)
|
|
377
|
+
continue;
|
|
378
|
+
let entries;
|
|
379
|
+
try {
|
|
380
|
+
const stat = fs.lstatSync(directory);
|
|
381
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
382
|
+
throw new Error(`Plugin definition path ${directory} is not a physical directory`);
|
|
383
|
+
}
|
|
384
|
+
entries = fs.readdirSync(directory, { withFileTypes: true });
|
|
385
|
+
}
|
|
386
|
+
catch (error) {
|
|
387
|
+
if (error.code === 'ENOENT')
|
|
388
|
+
continue;
|
|
389
|
+
throw error;
|
|
390
|
+
}
|
|
391
|
+
if (entries.some(entry => entry.name.endsWith('.json'))) {
|
|
392
|
+
throw new Error(`Local custom plugin definitions exist at ${directory}. PostgreSQL mode requires definitions in the shared database; run the SQLite-to-PostgreSQL migration with the plugin directory before starting any replica.`);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
parseSharedPluginDefinition(row) {
|
|
397
|
+
this.sharedDefinitionIds.add(row.plugin_id);
|
|
398
|
+
if (row.approved_by_user_id === null || row.approved_at === null) {
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
try {
|
|
402
|
+
const plugin = JSON.parse(row.definition_json);
|
|
403
|
+
if (!this.validatePlugin(plugin) ||
|
|
404
|
+
plugin.id !== row.plugin_id ||
|
|
405
|
+
getPluginDefinitionFingerprint(plugin) !== row.definition_fingerprint) {
|
|
406
|
+
return null;
|
|
407
|
+
}
|
|
408
|
+
return applyPluginDefinitionPolicy(plugin);
|
|
409
|
+
}
|
|
410
|
+
catch {
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
186
414
|
discoveredModelsCacheKey(pluginId, userId) {
|
|
187
415
|
return `${userId}:${pluginId}`;
|
|
188
416
|
}
|
|
189
417
|
discoveredCapabilityModelsCacheKey(pluginId, capability, userId) {
|
|
190
418
|
return `${userId}:${pluginId}:${capability}`;
|
|
191
419
|
}
|
|
192
|
-
getDiscoveredCapabilityModels(pluginId, capability, userId) {
|
|
420
|
+
async getDiscoveredCapabilityModels(pluginId, capability, userId) {
|
|
421
|
+
await this.ensureCacheInvalidation();
|
|
193
422
|
const effectiveUserId = userId || 'default';
|
|
194
423
|
const cacheKey = this.discoveredCapabilityModelsCacheKey(pluginId, capability, effectiveUserId);
|
|
195
|
-
if (this.
|
|
424
|
+
if (!this.sharedPluginDefinitions &&
|
|
425
|
+
this.discoveredCapabilityModelsCache.has(cacheKey)) {
|
|
196
426
|
return this.discoveredCapabilityModelsCache.get(cacheKey) || undefined;
|
|
197
427
|
}
|
|
198
|
-
const db = getDatabaseSafe();
|
|
199
|
-
if (!db) {
|
|
200
|
-
this.discoveredCapabilityModelsCache.set(cacheKey, null);
|
|
201
|
-
return undefined;
|
|
202
|
-
}
|
|
203
428
|
try {
|
|
204
|
-
const row =
|
|
205
|
-
.prepare(`SELECT models_json, updated_at
|
|
206
|
-
FROM plugin_discovered_capability_models
|
|
207
|
-
WHERE user_id = ? AND plugin_id = ? AND capability = ?`)
|
|
208
|
-
.get(effectiveUserId, pluginId, capability);
|
|
429
|
+
const row = await this.repositories().pluginDiscovery.getCapability(pluginId, capability, effectiveUserId);
|
|
209
430
|
if (!row) {
|
|
210
431
|
this.discoveredCapabilityModelsCache.set(cacheKey, null);
|
|
211
432
|
return undefined;
|
|
@@ -228,38 +449,38 @@ export class PluginService {
|
|
|
228
449
|
return undefined;
|
|
229
450
|
}
|
|
230
451
|
}
|
|
231
|
-
storeDiscoveredCapabilityModels(pluginId, capability, models, userId) {
|
|
452
|
+
async storeDiscoveredCapabilityModels(pluginId, capability, models, userId) {
|
|
453
|
+
await this.ensureCacheInvalidation();
|
|
232
454
|
const effectiveUserId = userId || 'default';
|
|
233
455
|
const uniqueModels = Array.from(new Set(models));
|
|
234
456
|
const cacheKey = this.discoveredCapabilityModelsCacheKey(pluginId, capability, effectiveUserId);
|
|
235
457
|
const discoveredAt = Date.now();
|
|
458
|
+
await this.repositories().pluginDiscovery.upsertCapability({
|
|
459
|
+
user_id: effectiveUserId,
|
|
460
|
+
plugin_id: pluginId,
|
|
461
|
+
capability,
|
|
462
|
+
models_json: JSON.stringify(uniqueModels),
|
|
463
|
+
updated_at: discoveredAt,
|
|
464
|
+
});
|
|
236
465
|
this.discoveredCapabilityModelsCache.set(cacheKey, uniqueModels);
|
|
237
466
|
this.discoveredCapabilityModelsUpdatedAt.set(cacheKey, discoveredAt);
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
}
|
|
248
|
-
getDiscoveredModels(pluginId, userId) {
|
|
467
|
+
await publishPluginCacheInvalidation({
|
|
468
|
+
version: 1,
|
|
469
|
+
scope: 'plugin-user',
|
|
470
|
+
pluginId,
|
|
471
|
+
userId: effectiveUserId,
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
async getDiscoveredModels(pluginId, userId) {
|
|
475
|
+
await this.ensureCacheInvalidation();
|
|
249
476
|
const effectiveUserId = userId || 'default';
|
|
250
477
|
const cacheKey = this.discoveredModelsCacheKey(pluginId, effectiveUserId);
|
|
251
|
-
if (this.
|
|
478
|
+
if (!this.sharedPluginDefinitions &&
|
|
479
|
+
this.discoveredModelsCache.has(cacheKey)) {
|
|
252
480
|
return this.discoveredModelsCache.get(cacheKey) || undefined;
|
|
253
481
|
}
|
|
254
|
-
const db = getDatabaseSafe();
|
|
255
|
-
if (!db) {
|
|
256
|
-
this.discoveredModelsCache.set(cacheKey, null);
|
|
257
|
-
return undefined;
|
|
258
|
-
}
|
|
259
482
|
try {
|
|
260
|
-
const row =
|
|
261
|
-
.prepare('SELECT models_json, updated_at FROM plugin_discovered_models WHERE user_id = ? AND plugin_id = ?')
|
|
262
|
-
.get(effectiveUserId, pluginId);
|
|
483
|
+
const row = await this.repositories().pluginDiscovery.get(pluginId, effectiveUserId);
|
|
263
484
|
if (!row) {
|
|
264
485
|
this.discoveredModelsCache.set(cacheKey, null);
|
|
265
486
|
return undefined;
|
|
@@ -284,44 +505,52 @@ export class PluginService {
|
|
|
284
505
|
return undefined;
|
|
285
506
|
}
|
|
286
507
|
}
|
|
287
|
-
storeDiscoveredModels(pluginId, models, userId) {
|
|
508
|
+
async storeDiscoveredModels(pluginId, models, userId) {
|
|
509
|
+
await this.ensureCacheInvalidation();
|
|
288
510
|
const effectiveUserId = userId || 'default';
|
|
289
511
|
const uniqueModels = Array.from(new Set(models));
|
|
290
512
|
const cacheKey = this.discoveredModelsCacheKey(pluginId, effectiveUserId);
|
|
291
513
|
const discoveredAt = Date.now();
|
|
292
|
-
this.discoveredModelsCache.set(cacheKey, uniqueModels);
|
|
293
|
-
this.discoveredModelsUpdatedAt.set(cacheKey, discoveredAt);
|
|
294
|
-
const db = getDatabaseSafe();
|
|
295
|
-
if (!db)
|
|
296
|
-
return;
|
|
297
514
|
try {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
515
|
+
await this.repositories().pluginDiscovery.upsert({
|
|
516
|
+
user_id: effectiveUserId,
|
|
517
|
+
plugin_id: pluginId,
|
|
518
|
+
models_json: JSON.stringify(uniqueModels),
|
|
519
|
+
updated_at: discoveredAt,
|
|
520
|
+
});
|
|
521
|
+
this.discoveredModelsCache.set(cacheKey, uniqueModels);
|
|
522
|
+
this.discoveredModelsUpdatedAt.set(cacheKey, discoveredAt);
|
|
523
|
+
await publishPluginCacheInvalidation({
|
|
524
|
+
version: 1,
|
|
525
|
+
scope: 'plugin-user',
|
|
526
|
+
pluginId,
|
|
527
|
+
userId: effectiveUserId,
|
|
528
|
+
});
|
|
304
529
|
}
|
|
305
530
|
catch (error) {
|
|
306
531
|
logger.warn('Failed to persist discovered models for plugin %s:', pluginId, error);
|
|
307
532
|
}
|
|
308
533
|
}
|
|
309
|
-
clearDiscoveredModels(pluginId, userId) {
|
|
310
|
-
this.
|
|
311
|
-
|
|
534
|
+
async clearDiscoveredModels(pluginId, userId) {
|
|
535
|
+
await this.ensureCacheInvalidation();
|
|
536
|
+
await this.clearDiscoveredCapabilityModels(pluginId, userId);
|
|
312
537
|
if (userId) {
|
|
313
538
|
const cacheKey = this.discoveredModelsCacheKey(pluginId, userId);
|
|
314
539
|
this.discoveredModelsCache.set(cacheKey, null);
|
|
315
540
|
this.discoveredModelsUpdatedAt.delete(cacheKey);
|
|
316
541
|
this.discoveryAttemptedAt.delete(cacheKey);
|
|
317
|
-
if (!db)
|
|
318
|
-
return;
|
|
319
542
|
try {
|
|
320
|
-
|
|
543
|
+
await this.repositories().pluginDiscovery.delete(pluginId, userId);
|
|
321
544
|
}
|
|
322
545
|
catch (error) {
|
|
323
546
|
logger.warn('Failed to clear discovered models for plugin %s:', pluginId, error);
|
|
324
547
|
}
|
|
548
|
+
await publishPluginCacheInvalidation({
|
|
549
|
+
version: 1,
|
|
550
|
+
scope: 'plugin-user',
|
|
551
|
+
pluginId,
|
|
552
|
+
userId,
|
|
553
|
+
});
|
|
325
554
|
return;
|
|
326
555
|
}
|
|
327
556
|
for (const key of this.discoveredModelsCache.keys()) {
|
|
@@ -331,54 +560,65 @@ export class PluginService {
|
|
|
331
560
|
this.discoveryAttemptedAt.delete(key);
|
|
332
561
|
}
|
|
333
562
|
}
|
|
334
|
-
if (!db)
|
|
335
|
-
return;
|
|
336
563
|
try {
|
|
337
|
-
|
|
564
|
+
await this.repositories().pluginDiscovery.delete(pluginId);
|
|
338
565
|
}
|
|
339
566
|
catch (error) {
|
|
340
567
|
logger.warn('Failed to clear discovered models for plugin %s:', pluginId, error);
|
|
341
568
|
}
|
|
569
|
+
await publishPluginCacheInvalidation({
|
|
570
|
+
version: 1,
|
|
571
|
+
scope: 'plugin',
|
|
572
|
+
pluginId,
|
|
573
|
+
});
|
|
342
574
|
}
|
|
343
|
-
clearDiscoveredCapabilityModels(pluginId, userId) {
|
|
344
|
-
const db = getDatabaseSafe();
|
|
575
|
+
async clearDiscoveredCapabilityModels(pluginId, userId) {
|
|
345
576
|
if (userId) {
|
|
346
|
-
for (const capability of [
|
|
577
|
+
for (const capability of [
|
|
578
|
+
'image',
|
|
579
|
+
'stt',
|
|
580
|
+
'tts',
|
|
581
|
+
'audio',
|
|
582
|
+
'video',
|
|
583
|
+
]) {
|
|
347
584
|
const key = this.discoveredCapabilityModelsCacheKey(pluginId, capability, userId);
|
|
348
585
|
this.discoveredCapabilityModelsCache.delete(key);
|
|
349
586
|
this.discoveredCapabilityModelsUpdatedAt.delete(key);
|
|
350
587
|
this.capabilityDiscoveryAttemptedAt.delete(key);
|
|
351
588
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
}
|
|
589
|
+
// The discovery repository removes both completion and capability rows
|
|
590
|
+
// atomically, which prevents stale partial catalogs after reset.
|
|
591
|
+
await this.repositories().pluginDiscovery.delete(pluginId, userId);
|
|
356
592
|
return;
|
|
357
593
|
}
|
|
358
594
|
for (const key of this.discoveredCapabilityModelsCache.keys()) {
|
|
359
|
-
if (['image', 'tts', 'audio', 'video'].some(capability => key.endsWith(`:${pluginId}:${capability}`))) {
|
|
595
|
+
if (['image', 'stt', 'tts', 'audio', 'video'].some(capability => key.endsWith(`:${pluginId}:${capability}`))) {
|
|
360
596
|
this.discoveredCapabilityModelsCache.delete(key);
|
|
361
597
|
this.discoveredCapabilityModelsUpdatedAt.delete(key);
|
|
362
598
|
this.capabilityDiscoveryAttemptedAt.delete(key);
|
|
363
599
|
}
|
|
364
600
|
}
|
|
365
|
-
|
|
366
|
-
db.prepare('DELETE FROM plugin_discovered_capability_models WHERE plugin_id = ?').run(pluginId);
|
|
367
|
-
}
|
|
601
|
+
await this.repositories().pluginDiscovery.delete(pluginId);
|
|
368
602
|
}
|
|
369
|
-
applyDiscoveredModels(plugin, userId) {
|
|
370
|
-
if (!this.canUseStoredConnectionOverrides(userId) &&
|
|
371
|
-
pluginVariablesService.hasStoredConnectionOverride(plugin.id, userId, plugin.variables, getPluginConnectionVariableNames(plugin))) {
|
|
603
|
+
async applyDiscoveredModels(plugin, userId) {
|
|
604
|
+
if (!(await this.canUseStoredConnectionOverrides(userId)) &&
|
|
605
|
+
(await pluginVariablesService.hasStoredConnectionOverride(plugin.id, userId, plugin.variables, getPluginConnectionVariableNames(plugin)))) {
|
|
372
606
|
return plugin;
|
|
373
607
|
}
|
|
374
|
-
const models = this.getDiscoveredModels(plugin.id, userId);
|
|
608
|
+
const models = await this.getDiscoveredModels(plugin.id, userId);
|
|
375
609
|
const capabilities = plugin.capabilities
|
|
376
610
|
? { ...plugin.capabilities }
|
|
377
611
|
: undefined;
|
|
378
612
|
if (capabilities) {
|
|
379
|
-
for (const capability of [
|
|
613
|
+
for (const capability of [
|
|
614
|
+
'image',
|
|
615
|
+
'stt',
|
|
616
|
+
'tts',
|
|
617
|
+
'audio',
|
|
618
|
+
'video',
|
|
619
|
+
]) {
|
|
380
620
|
const definition = capabilities[capability];
|
|
381
|
-
const discovered = this.getDiscoveredCapabilityModels(plugin.id, capability, userId);
|
|
621
|
+
const discovered = await this.getDiscoveredCapabilityModels(plugin.id, capability, userId);
|
|
382
622
|
if (definition && discovered) {
|
|
383
623
|
capabilities[capability] = {
|
|
384
624
|
...definition,
|
|
@@ -394,8 +634,76 @@ export class PluginService {
|
|
|
394
634
|
};
|
|
395
635
|
}
|
|
396
636
|
ensurePluginsDirectory() {
|
|
397
|
-
|
|
637
|
+
const dataDirectory = resolveDataDirectory();
|
|
638
|
+
const configuredRelativeToData = path.relative(dataDirectory, this.pluginsDir);
|
|
639
|
+
const configuredPathRoot = configuredRelativeToData === '' ||
|
|
640
|
+
(configuredRelativeToData !== '..' &&
|
|
641
|
+
!configuredRelativeToData.startsWith(`..${path.sep}`) &&
|
|
642
|
+
!path.isAbsolute(configuredRelativeToData))
|
|
643
|
+
? dataDirectory
|
|
644
|
+
: process.env.PLUGINS_DIR?.trim() &&
|
|
645
|
+
!path.isAbsolute(process.env.PLUGINS_DIR.trim())
|
|
646
|
+
? PROJECT_DIRECTORY
|
|
647
|
+
: path.dirname(this.pluginsDir);
|
|
648
|
+
try {
|
|
649
|
+
if (hasSymlinkPathComponentFromRoot(configuredPathRoot, this.pluginsDir)) {
|
|
650
|
+
throw new Error('PLUGINS_DIR cannot contain a symbolic-link path component');
|
|
651
|
+
}
|
|
652
|
+
const pluginsStat = fs.lstatSync(this.pluginsDir);
|
|
653
|
+
if (!pluginsStat.isDirectory() || pluginsStat.isSymbolicLink()) {
|
|
654
|
+
throw new Error('PLUGINS_DIR must be a physical directory, not a symlink or special file');
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
catch (error) {
|
|
658
|
+
if (error.code !== 'ENOENT')
|
|
659
|
+
throw error;
|
|
398
660
|
fs.mkdirSync(this.pluginsDir, { recursive: true });
|
|
661
|
+
const createdStat = fs.lstatSync(this.pluginsDir);
|
|
662
|
+
if (!createdStat.isDirectory() || createdStat.isSymbolicLink()) {
|
|
663
|
+
throw new Error('PLUGINS_DIR could not be created safely');
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
for (const legacyDirectory of this.legacyPluginsDirs) {
|
|
667
|
+
const relativeToBackend = path.relative(BACKEND_DIRECTORY, legacyDirectory);
|
|
668
|
+
const legacyRoot = relativeToBackend === '' ||
|
|
669
|
+
(relativeToBackend !== '..' &&
|
|
670
|
+
!relativeToBackend.startsWith(`..${path.sep}`) &&
|
|
671
|
+
!path.isAbsolute(relativeToBackend))
|
|
672
|
+
? BACKEND_DIRECTORY
|
|
673
|
+
: path.dirname(legacyDirectory);
|
|
674
|
+
if (hasSymlinkPathComponentFromRoot(legacyRoot, legacyDirectory)) {
|
|
675
|
+
throw new Error('A legacy plugin path contains a symbolic-link component; refusing to follow it');
|
|
676
|
+
}
|
|
677
|
+
const legacyStat = (() => {
|
|
678
|
+
try {
|
|
679
|
+
return fs.lstatSync(legacyDirectory);
|
|
680
|
+
}
|
|
681
|
+
catch (error) {
|
|
682
|
+
if (error.code === 'ENOENT')
|
|
683
|
+
return null;
|
|
684
|
+
throw error;
|
|
685
|
+
}
|
|
686
|
+
})();
|
|
687
|
+
if (legacyStat &&
|
|
688
|
+
(!legacyStat.isDirectory() || legacyStat.isSymbolicLink())) {
|
|
689
|
+
throw new Error('A legacy plugin path is not a physical directory; refusing to follow it');
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
for (const historicalDirectory of this.historicalPluginConflictDirs) {
|
|
693
|
+
let historicalEntries;
|
|
694
|
+
try {
|
|
695
|
+
historicalEntries = fs.readdirSync(historicalDirectory, {
|
|
696
|
+
withFileTypes: true,
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
catch (error) {
|
|
700
|
+
if (error.code === 'ENOENT')
|
|
701
|
+
continue;
|
|
702
|
+
throw new Error(`Historical plugin directory ${historicalDirectory} cannot be inspected safely`);
|
|
703
|
+
}
|
|
704
|
+
if (historicalEntries.some(entry => entry.name.endsWith('.json'))) {
|
|
705
|
+
throw new Error(`Legacy plugin definitions exist at ${historicalDirectory}, where the relative PLUGINS_DIR previously resolved from the caller working directory. Move them into ${this.pluginsDir} or configure an absolute PLUGINS_DIR; Libre will not silently choose between them.`);
|
|
706
|
+
}
|
|
399
707
|
}
|
|
400
708
|
}
|
|
401
709
|
bundledPluginPath(id) {
|
|
@@ -405,19 +713,12 @@ export class PluginService {
|
|
|
405
713
|
return (path.resolve(filePath) === this.bundledPluginPath(plugin.id) &&
|
|
406
714
|
matchesBundledPluginTrustAnchor(plugin));
|
|
407
715
|
}
|
|
408
|
-
isPluginDefinitionApproved(plugin, filePath) {
|
|
716
|
+
async isPluginDefinitionApproved(plugin, filePath) {
|
|
409
717
|
if (this.isAnchoredBundledDefinition(plugin, filePath)) {
|
|
410
718
|
return true;
|
|
411
719
|
}
|
|
412
|
-
const db = getDatabaseSafe();
|
|
413
|
-
if (!db)
|
|
414
|
-
return false;
|
|
415
720
|
try {
|
|
416
|
-
const row =
|
|
417
|
-
.prepare(`SELECT definition_fingerprint, source_path
|
|
418
|
-
FROM plugin_definition_approvals
|
|
419
|
-
WHERE plugin_id = ?`)
|
|
420
|
-
.get(plugin.id);
|
|
721
|
+
const row = await this.repositories().pluginApprovals.find(plugin.id);
|
|
421
722
|
return (row?.definition_fingerprint ===
|
|
422
723
|
getPluginDefinitionFingerprint(plugin) &&
|
|
423
724
|
row.source_path === path.resolve(filePath));
|
|
@@ -427,46 +728,30 @@ export class PluginService {
|
|
|
427
728
|
return false;
|
|
428
729
|
}
|
|
429
730
|
}
|
|
430
|
-
approvePluginDefinition(plugin, filePath, userId) {
|
|
431
|
-
if (!this.canUseStoredConnectionOverrides(userId)) {
|
|
731
|
+
async approvePluginDefinition(plugin, filePath, userId) {
|
|
732
|
+
if (!(await this.canUseStoredConnectionOverrides(userId))) {
|
|
432
733
|
throw new Error('Administrator approval is required');
|
|
433
734
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
VALUES (?, ?, ?, ?, ?)
|
|
442
|
-
ON CONFLICT(plugin_id) DO UPDATE SET
|
|
443
|
-
definition_fingerprint = excluded.definition_fingerprint,
|
|
444
|
-
source_path = excluded.source_path,
|
|
445
|
-
approved_by_user_id = excluded.approved_by_user_id,
|
|
446
|
-
approved_at = excluded.approved_at`).run(plugin.id, getPluginDefinitionFingerprint(plugin), path.resolve(filePath), userId, Date.now());
|
|
447
|
-
}
|
|
448
|
-
removePluginDefinitionApproval(pluginId) {
|
|
449
|
-
const db = getDatabaseSafe();
|
|
450
|
-
if (!db)
|
|
451
|
-
return;
|
|
452
|
-
db.prepare('DELETE FROM plugin_definition_approvals WHERE plugin_id = ?').run(pluginId);
|
|
735
|
+
await this.repositories().pluginApprovals.upsert({
|
|
736
|
+
plugin_id: plugin.id,
|
|
737
|
+
definition_fingerprint: getPluginDefinitionFingerprint(plugin),
|
|
738
|
+
source_path: path.resolve(filePath),
|
|
739
|
+
approved_by_user_id: userId,
|
|
740
|
+
approved_at: Date.now(),
|
|
741
|
+
});
|
|
453
742
|
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
db.transaction(() => {
|
|
460
|
-
db.prepare('DELETE FROM plugin_activations WHERE plugin_id = ?').run(pluginId);
|
|
461
|
-
db.prepare('DELETE FROM plugin_definition_approvals WHERE plugin_id = ?').run(pluginId);
|
|
462
|
-
})();
|
|
743
|
+
async removePluginDefinitionApproval(pluginId) {
|
|
744
|
+
await this.repositories().pluginApprovals.delete(pluginId);
|
|
745
|
+
}
|
|
746
|
+
async revokePluginDefinitionConsent(pluginId) {
|
|
747
|
+
await this.repositories().pluginApprovals.revokeConsent(pluginId);
|
|
463
748
|
}
|
|
464
749
|
canMigrateLegacyActivation(pluginId) {
|
|
465
750
|
const effectivePath = this.resolveEffectivePluginFilePath(pluginId);
|
|
466
751
|
if (!effectivePath)
|
|
467
752
|
return false;
|
|
468
753
|
try {
|
|
469
|
-
const parsedPlugin = JSON.parse(
|
|
754
|
+
const parsedPlugin = JSON.parse(readRegularPluginDefinition(effectivePath));
|
|
470
755
|
return (this.validatePlugin(parsedPlugin) &&
|
|
471
756
|
parsedPlugin.id === pluginId &&
|
|
472
757
|
this.isAnchoredBundledDefinition(parsedPlugin, effectivePath));
|
|
@@ -482,9 +767,31 @@ export class PluginService {
|
|
|
482
767
|
for (const pluginsDir of [...this.pluginReadDirs].reverse()) {
|
|
483
768
|
const resolvedDirectory = path.resolve(pluginsDir);
|
|
484
769
|
const candidate = path.resolve(pluginsDir, `${sanitizedId}.json`);
|
|
485
|
-
if (candidate.startsWith(resolvedDirectory)
|
|
770
|
+
if (!candidate.startsWith(resolvedDirectory))
|
|
771
|
+
continue;
|
|
772
|
+
const directoryStat = (() => {
|
|
773
|
+
try {
|
|
774
|
+
return fs.lstatSync(pluginsDir);
|
|
775
|
+
}
|
|
776
|
+
catch {
|
|
777
|
+
return null;
|
|
778
|
+
}
|
|
779
|
+
})();
|
|
780
|
+
if (directoryStat &&
|
|
781
|
+
(!directoryStat.isDirectory() || directoryStat.isSymbolicLink())) {
|
|
782
|
+
// Preserve precedence while forcing the no-follow reader to reject the
|
|
783
|
+
// unsafe path instead of falling back to a lower-priority definition.
|
|
486
784
|
return candidate;
|
|
487
785
|
}
|
|
786
|
+
try {
|
|
787
|
+
fs.lstatSync(candidate);
|
|
788
|
+
// Return invalid named entries too. The strict reader will reject them,
|
|
789
|
+
// preventing a symlink/special-file shadow from revealing a bundled ID.
|
|
790
|
+
return candidate;
|
|
791
|
+
}
|
|
792
|
+
catch {
|
|
793
|
+
// Continue to lower-priority sources only when the named entry is absent.
|
|
794
|
+
}
|
|
488
795
|
}
|
|
489
796
|
return null;
|
|
490
797
|
}
|
|
@@ -503,7 +810,7 @@ export class PluginService {
|
|
|
503
810
|
return null;
|
|
504
811
|
}
|
|
505
812
|
try {
|
|
506
|
-
const parsedPlugin = JSON.parse(
|
|
813
|
+
const parsedPlugin = JSON.parse(readRegularPluginDefinition(bundledPath));
|
|
507
814
|
if (!this.validatePlugin(parsedPlugin) ||
|
|
508
815
|
parsedPlugin.id !== sanitizedId ||
|
|
509
816
|
!matchesBundledPluginTrustAnchor(parsedPlugin)) {
|
|
@@ -521,6 +828,10 @@ export class PluginService {
|
|
|
521
828
|
}
|
|
522
829
|
}
|
|
523
830
|
usesTrustedBundledRouting(plugin) {
|
|
831
|
+
if (this.sharedPluginDefinitions &&
|
|
832
|
+
this.sharedDefinitionIds.has(plugin.id)) {
|
|
833
|
+
return false;
|
|
834
|
+
}
|
|
524
835
|
const effectivePath = this.resolveEffectivePluginFilePath(plugin.id);
|
|
525
836
|
const bundledPath = this.bundledPluginPath(plugin.id);
|
|
526
837
|
if (!effectivePath || effectivePath !== bundledPath)
|
|
@@ -529,18 +840,12 @@ export class PluginService {
|
|
|
529
840
|
return (bundledProjection !== null &&
|
|
530
841
|
getPluginRoutingAuthProjection(plugin) === bundledProjection);
|
|
531
842
|
}
|
|
532
|
-
isPluginActive(pluginId, userId) {
|
|
533
|
-
return pluginActivationService.getActivePluginIds(userId).has(pluginId);
|
|
843
|
+
async isPluginActive(pluginId, userId) {
|
|
844
|
+
return (await pluginActivationService.getActivePluginIds(userId)).has(pluginId);
|
|
534
845
|
}
|
|
535
|
-
canUseStoredConnectionOverrides(userId) {
|
|
536
|
-
const db = getDatabaseSafe();
|
|
537
|
-
if (!db)
|
|
538
|
-
return false;
|
|
846
|
+
async canUseStoredConnectionOverrides(userId) {
|
|
539
847
|
try {
|
|
540
|
-
|
|
541
|
-
.prepare('SELECT role FROM users WHERE id = ?')
|
|
542
|
-
.get(userId || 'default');
|
|
543
|
-
return row?.role === 'admin';
|
|
848
|
+
return ((await userModel.getUserById(userId || 'default'))?.role === 'admin');
|
|
544
849
|
}
|
|
545
850
|
catch (error) {
|
|
546
851
|
logger.warn('Failed to resolve plugin routing permission:', error);
|
|
@@ -553,7 +858,7 @@ export class PluginService {
|
|
|
553
858
|
* @param userId Optional user ID for per-user credentials
|
|
554
859
|
* @returns The API key or null if not found
|
|
555
860
|
*/
|
|
556
|
-
getApiKey(plugin, userId) {
|
|
861
|
+
async getApiKey(plugin, userId) {
|
|
557
862
|
if (plugin.id === CODEX_OAUTH_PLUGIN_ID) {
|
|
558
863
|
// Resolved from the server's Codex CLI sign-in, never user credentials.
|
|
559
864
|
// A writable same-ID definition must never receive the server user's
|
|
@@ -563,13 +868,13 @@ export class PluginService {
|
|
|
563
868
|
? codexOAuthService.getCachedAccessToken()
|
|
564
869
|
: null;
|
|
565
870
|
}
|
|
566
|
-
const hasHonoredConnectionOverride = this.canUseStoredConnectionOverrides(userId) &&
|
|
567
|
-
pluginVariablesService.hasStoredConnectionOverride(plugin.id, userId, plugin.variables, getPluginConnectionVariableNames(plugin));
|
|
871
|
+
const hasHonoredConnectionOverride = (await this.canUseStoredConnectionOverrides(userId)) &&
|
|
872
|
+
(await pluginVariablesService.hasStoredConnectionOverride(plugin.id, userId, plugin.variables, getPluginConnectionVariableNames(plugin)));
|
|
568
873
|
const usesTrustedBundledRouting = this.usesTrustedBundledRouting(plugin);
|
|
569
874
|
const allowTrustedFallback = usesTrustedBundledRouting && !hasHonoredConnectionOverride;
|
|
570
875
|
return pluginCredentialsService.getApiKey(plugin.id, plugin.auth.key_env, userId, {
|
|
571
876
|
allowEnvironmentFallback: allowTrustedFallback,
|
|
572
|
-
expectedRoutingAuthFingerprint: this.getCredentialRoutingAuthFingerprint(plugin, userId),
|
|
877
|
+
expectedRoutingAuthFingerprint: await this.getCredentialRoutingAuthFingerprint(plugin, userId),
|
|
573
878
|
allowLegacyUnboundCredential: allowTrustedFallback,
|
|
574
879
|
});
|
|
575
880
|
}
|
|
@@ -577,8 +882,8 @@ export class PluginService {
|
|
|
577
882
|
* Bind a user credential to the exact routing/authentication contract in
|
|
578
883
|
* effect when they save it. Generation controls are intentionally excluded.
|
|
579
884
|
*/
|
|
580
|
-
getCredentialRoutingAuthFingerprint(plugin, userId) {
|
|
581
|
-
const variables = this.getPluginVariables(plugin, userId);
|
|
885
|
+
async getCredentialRoutingAuthFingerprint(plugin, userId) {
|
|
886
|
+
const variables = await this.getPluginVariables(plugin, userId);
|
|
582
887
|
const effectiveConnectionValues = Array.from(getPluginConnectionVariableNames(plugin), name => {
|
|
583
888
|
const definition = plugin.variables?.find(candidate => candidate.name === name);
|
|
584
889
|
if (!definition)
|
|
@@ -588,11 +893,18 @@ export class PluginService {
|
|
|
588
893
|
value: variables[name] ?? definition.default ?? '',
|
|
589
894
|
};
|
|
590
895
|
});
|
|
591
|
-
const
|
|
896
|
+
const sharedDefinition = this.sharedPluginDefinitions
|
|
897
|
+
? await this.repositories().pluginDefinitions.find(plugin.id)
|
|
898
|
+
: null;
|
|
899
|
+
if (sharedDefinition)
|
|
900
|
+
this.sharedDefinitionIds.add(plugin.id);
|
|
901
|
+
const effectivePath = sharedDefinition
|
|
902
|
+
? null
|
|
903
|
+
: this.resolveEffectivePluginFilePath(plugin.id);
|
|
592
904
|
let effectiveDefinitionFingerprint = getPluginDefinitionFingerprint(plugin);
|
|
593
905
|
if (effectivePath) {
|
|
594
906
|
try {
|
|
595
|
-
const effectiveDefinition = JSON.parse(
|
|
907
|
+
const effectiveDefinition = JSON.parse(readRegularPluginDefinition(effectivePath));
|
|
596
908
|
if (this.validatePlugin(effectiveDefinition) &&
|
|
597
909
|
effectiveDefinition.id === plugin.id) {
|
|
598
910
|
effectiveDefinitionFingerprint =
|
|
@@ -608,7 +920,11 @@ export class PluginService {
|
|
|
608
920
|
plugin_id: plugin.id,
|
|
609
921
|
plugin_type: plugin.type,
|
|
610
922
|
trusted_bundled_source: this.usesTrustedBundledRouting(plugin),
|
|
611
|
-
effective_source_path:
|
|
923
|
+
effective_source_path: sharedDefinition
|
|
924
|
+
? 'database:plugin_definitions'
|
|
925
|
+
: effectivePath
|
|
926
|
+
? path.resolve(effectivePath)
|
|
927
|
+
: null,
|
|
612
928
|
effective_definition_fingerprint: effectiveDefinitionFingerprint,
|
|
613
929
|
routing_auth_projection: getPluginRoutingAuthProjection(plugin),
|
|
614
930
|
effective_connection_values: effectiveConnectionValues,
|
|
@@ -618,12 +934,12 @@ export class PluginService {
|
|
|
618
934
|
/**
|
|
619
935
|
* Get resolved variable values for a plugin (decrypted, typed).
|
|
620
936
|
*/
|
|
621
|
-
getPluginVariables(plugin, userId) {
|
|
937
|
+
async getPluginVariables(plugin, userId) {
|
|
622
938
|
if (!plugin.variables || plugin.variables.length === 0) {
|
|
623
939
|
return {};
|
|
624
940
|
}
|
|
625
|
-
const variables = pluginVariablesService.getResolvedVariables(plugin.id, plugin.variables, userId);
|
|
626
|
-
if (this.canUseStoredConnectionOverrides(userId))
|
|
941
|
+
const variables = await pluginVariablesService.getResolvedVariables(plugin.id, plugin.variables, userId);
|
|
942
|
+
if (await this.canUseStoredConnectionOverrides(userId))
|
|
627
943
|
return variables;
|
|
628
944
|
return Object.fromEntries(Object.entries(variables).map(([name, value]) => {
|
|
629
945
|
if (!getPluginConnectionVariableNames(plugin).has(name)) {
|
|
@@ -646,8 +962,8 @@ export class PluginService {
|
|
|
646
962
|
* boundary ordered prevents a rejected override from ever selecting an
|
|
647
963
|
* environment credential.
|
|
648
964
|
*/
|
|
649
|
-
resolveOperationEndpoint(plugin, userId) {
|
|
650
|
-
return resolvePluginApiConfig(plugin, this.getPluginVariables(plugin, userId)).endpoint;
|
|
965
|
+
async resolveOperationEndpoint(plugin, userId) {
|
|
966
|
+
return resolvePluginApiConfig(plugin, await this.getPluginVariables(plugin, userId)).endpoint;
|
|
651
967
|
}
|
|
652
968
|
/**
|
|
653
969
|
* Attempt to auto-discover available models from a plugin's full API endpoint.
|
|
@@ -665,12 +981,16 @@ export class PluginService {
|
|
|
665
981
|
* is fresh.
|
|
666
982
|
*/
|
|
667
983
|
async discoverModelsResult(pluginId, userId) {
|
|
984
|
+
await this.ensureCacheInvalidation();
|
|
668
985
|
const cacheKey = this.discoveredModelsCacheKey(pluginId, userId || 'default');
|
|
669
986
|
const inflight = this.inflightDiscovery.get(cacheKey);
|
|
670
987
|
if (inflight)
|
|
671
988
|
return inflight;
|
|
672
|
-
const
|
|
673
|
-
|
|
989
|
+
const revision = this.discoveryCacheRevisions.get(cacheKey) ?? 0;
|
|
990
|
+
const attempt = this.runModelDiscovery(pluginId, userId, revision).finally(() => {
|
|
991
|
+
if (this.inflightDiscovery.get(cacheKey) === attempt) {
|
|
992
|
+
this.inflightDiscovery.delete(cacheKey);
|
|
993
|
+
}
|
|
674
994
|
});
|
|
675
995
|
this.inflightDiscovery.set(cacheKey, attempt);
|
|
676
996
|
return attempt;
|
|
@@ -680,13 +1000,13 @@ export class PluginService {
|
|
|
680
1000
|
* immediately; stored ones age out after the TTL. Both cases are gated by a
|
|
681
1001
|
* per-plugin backoff so a failing provider is not probed on every request.
|
|
682
1002
|
*/
|
|
683
|
-
isModelDiscoveryDue(pluginId, userId) {
|
|
1003
|
+
async isModelDiscoveryDue(pluginId, userId) {
|
|
684
1004
|
const cacheKey = this.discoveredModelsCacheKey(pluginId, userId || 'default');
|
|
685
1005
|
const attemptedAt = this.discoveryAttemptedAt.get(cacheKey);
|
|
686
1006
|
if (attemptedAt && Date.now() - attemptedAt < modelDiscoveryRetryMs()) {
|
|
687
1007
|
return false;
|
|
688
1008
|
}
|
|
689
|
-
const models = this.getDiscoveredModels(pluginId, userId);
|
|
1009
|
+
const models = await this.getDiscoveredModels(pluginId, userId);
|
|
690
1010
|
const updatedAt = this.discoveredModelsUpdatedAt.get(cacheKey);
|
|
691
1011
|
if (!models || !updatedAt)
|
|
692
1012
|
return true;
|
|
@@ -699,8 +1019,13 @@ export class PluginService {
|
|
|
699
1019
|
* the next request.
|
|
700
1020
|
*/
|
|
701
1021
|
async refreshStaleModels(userId) {
|
|
702
|
-
const due =
|
|
703
|
-
|
|
1022
|
+
const due = [];
|
|
1023
|
+
for (const plugin of await this.getActivePlugins(userId)) {
|
|
1024
|
+
if ((plugin.type === 'completion' || plugin.type === 'chat') &&
|
|
1025
|
+
(await this.isModelDiscoveryDue(plugin.id, userId))) {
|
|
1026
|
+
due.push(plugin);
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
704
1029
|
if (due.length === 0)
|
|
705
1030
|
return;
|
|
706
1031
|
const refreshes = Promise.all(due.map(plugin => this.discoverModels(plugin.id, userId).catch(() => [])));
|
|
@@ -710,30 +1035,39 @@ export class PluginService {
|
|
|
710
1035
|
]);
|
|
711
1036
|
}
|
|
712
1037
|
async discoverCapabilityModels(pluginId, capability, userId) {
|
|
1038
|
+
await this.ensureCacheInvalidation();
|
|
713
1039
|
const cacheKey = this.discoveredCapabilityModelsCacheKey(pluginId, capability, userId || 'default');
|
|
714
1040
|
const inflight = this.inflightCapabilityDiscovery.get(cacheKey);
|
|
715
1041
|
if (inflight)
|
|
716
1042
|
return inflight;
|
|
717
|
-
const
|
|
1043
|
+
const revision = this.capabilityDiscoveryCacheRevisions.get(cacheKey) ?? 0;
|
|
1044
|
+
const attempt = this.runCapabilityModelDiscovery(pluginId, capability, userId, revision).finally(() => {
|
|
1045
|
+
if (this.inflightCapabilityDiscovery.get(cacheKey) === attempt) {
|
|
1046
|
+
this.inflightCapabilityDiscovery.delete(cacheKey);
|
|
1047
|
+
}
|
|
1048
|
+
});
|
|
718
1049
|
this.inflightCapabilityDiscovery.set(cacheKey, attempt);
|
|
719
1050
|
return attempt;
|
|
720
1051
|
}
|
|
721
|
-
isCapabilityModelDiscoveryDue(pluginId, capability, userId) {
|
|
1052
|
+
async isCapabilityModelDiscoveryDue(pluginId, capability, userId) {
|
|
722
1053
|
const cacheKey = this.discoveredCapabilityModelsCacheKey(pluginId, capability, userId || 'default');
|
|
723
1054
|
const attemptedAt = this.capabilityDiscoveryAttemptedAt.get(cacheKey);
|
|
724
1055
|
if (attemptedAt && Date.now() - attemptedAt < modelDiscoveryRetryMs()) {
|
|
725
1056
|
return false;
|
|
726
1057
|
}
|
|
727
|
-
const models = this.getDiscoveredCapabilityModels(pluginId, capability, userId);
|
|
1058
|
+
const models = await this.getDiscoveredCapabilityModels(pluginId, capability, userId);
|
|
728
1059
|
const updatedAt = this.discoveredCapabilityModelsUpdatedAt.get(cacheKey);
|
|
729
1060
|
return (!models || !updatedAt || Date.now() - updatedAt >= modelDiscoveryTtlMs());
|
|
730
1061
|
}
|
|
731
1062
|
async refreshStaleCapabilityModels(capability, userId) {
|
|
732
|
-
const due =
|
|
1063
|
+
const due = [];
|
|
1064
|
+
for (const plugin of await this.getActivePlugins(userId)) {
|
|
733
1065
|
const definition = plugin.capabilities?.[capability];
|
|
734
|
-
|
|
735
|
-
this.isCapabilityModelDiscoveryDue(plugin.id, capability, userId))
|
|
736
|
-
|
|
1066
|
+
if (Boolean(definition?.models_endpoint) &&
|
|
1067
|
+
(await this.isCapabilityModelDiscoveryDue(plugin.id, capability, userId))) {
|
|
1068
|
+
due.push(plugin);
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
737
1071
|
if (due.length === 0)
|
|
738
1072
|
return;
|
|
739
1073
|
const refreshes = Promise.all(due.map(plugin => this.discoverCapabilityModels(plugin.id, capability, userId).catch(() => undefined)));
|
|
@@ -742,8 +1076,8 @@ export class PluginService {
|
|
|
742
1076
|
new Promise(resolve => setTimeout(resolve, modelDiscoveryRefreshDeadlineMs()).unref?.()),
|
|
743
1077
|
]);
|
|
744
1078
|
}
|
|
745
|
-
async runCapabilityModelDiscovery(pluginId, capability, userId) {
|
|
746
|
-
const plugin = this.getPlugin(pluginId, userId);
|
|
1079
|
+
async runCapabilityModelDiscovery(pluginId, capability, userId, expectedRevision) {
|
|
1080
|
+
const plugin = await this.getPlugin(pluginId, userId);
|
|
747
1081
|
const definition = plugin?.capabilities?.[capability];
|
|
748
1082
|
if (!plugin || !definition) {
|
|
749
1083
|
return {
|
|
@@ -757,9 +1091,20 @@ export class PluginService {
|
|
|
757
1091
|
}
|
|
758
1092
|
const cacheKey = this.discoveredCapabilityModelsCacheKey(pluginId, capability, userId || 'default');
|
|
759
1093
|
this.capabilityDiscoveryAttemptedAt.set(cacheKey, Date.now());
|
|
760
|
-
const
|
|
1094
|
+
const config = definition.config && typeof definition.config === 'object'
|
|
1095
|
+
? definition.config
|
|
1096
|
+
: {};
|
|
1097
|
+
const modelsEndpointVariable = config.models_endpoint_variable;
|
|
1098
|
+
const pluginVariables = await this.getPluginVariables(plugin, userId);
|
|
1099
|
+
const modelsEndpointOverride = typeof modelsEndpointVariable === 'string'
|
|
1100
|
+
? pluginVariables[modelsEndpointVariable]
|
|
1101
|
+
: undefined;
|
|
1102
|
+
const modelsEndpoint = typeof modelsEndpointOverride === 'string' &&
|
|
1103
|
+
modelsEndpointOverride.trim().length > 0
|
|
1104
|
+
? this.validateEndpointUrl(modelsEndpointOverride.trim())
|
|
1105
|
+
: definition.models_endpoint;
|
|
761
1106
|
assertSafePluginEndpoint(modelsEndpoint, `${capability} model discovery endpoint`);
|
|
762
|
-
const apiKey = this.getApiKey(plugin, userId);
|
|
1107
|
+
const apiKey = await this.getApiKey(plugin, userId);
|
|
763
1108
|
if (pluginRequiresApiKey(plugin) && !apiKey) {
|
|
764
1109
|
return {
|
|
765
1110
|
models: definition.model_map,
|
|
@@ -787,7 +1132,15 @@ export class PluginService {
|
|
|
787
1132
|
};
|
|
788
1133
|
}
|
|
789
1134
|
const previous = definition.model_map;
|
|
790
|
-
this.
|
|
1135
|
+
if ((this.capabilityDiscoveryCacheRevisions.get(cacheKey) ?? 0) !==
|
|
1136
|
+
expectedRevision) {
|
|
1137
|
+
return {
|
|
1138
|
+
models: previous,
|
|
1139
|
+
outcome: 'unavailable',
|
|
1140
|
+
reason: 'Provider configuration changed during discovery',
|
|
1141
|
+
};
|
|
1142
|
+
}
|
|
1143
|
+
await this.storeDiscoveredCapabilityModels(pluginId, capability, models, userId);
|
|
791
1144
|
return {
|
|
792
1145
|
models,
|
|
793
1146
|
outcome: JSON.stringify(previous) === JSON.stringify(models)
|
|
@@ -816,19 +1169,19 @@ export class PluginService {
|
|
|
816
1169
|
}
|
|
817
1170
|
return 'No API key is configured for this provider.';
|
|
818
1171
|
}
|
|
819
|
-
async runModelDiscovery(pluginId, userId) {
|
|
820
|
-
const plugin = this.getPlugin(pluginId, userId);
|
|
1172
|
+
async runModelDiscovery(pluginId, userId, expectedRevision) {
|
|
1173
|
+
const plugin = await this.getPlugin(pluginId, userId);
|
|
821
1174
|
if (!plugin) {
|
|
822
1175
|
return { models: [], outcome: 'unavailable', reason: 'Plugin not found' };
|
|
823
1176
|
}
|
|
824
1177
|
this.discoveryAttemptedAt.set(this.discoveredModelsCacheKey(pluginId, userId || 'default'), Date.now());
|
|
825
|
-
const pluginVars = this.getPluginVariables(plugin, userId);
|
|
1178
|
+
const pluginVars = await this.getPluginVariables(plugin, userId);
|
|
826
1179
|
const { endpoint: effectiveEndpoint } = resolvePluginApiConfig(plugin, pluginVars);
|
|
827
1180
|
const modelsEndpoint = resolvePluginModelsEndpoint(effectiveEndpoint, typeof pluginVars.models_endpoint === 'string'
|
|
828
1181
|
? pluginVars.models_endpoint
|
|
829
1182
|
: undefined);
|
|
830
1183
|
assertSafePluginEndpoint(modelsEndpoint, 'model discovery endpoint');
|
|
831
|
-
const apiKey = this.getApiKey(plugin, userId);
|
|
1184
|
+
const apiKey = await this.getApiKey(plugin, userId);
|
|
832
1185
|
if (pluginRequiresApiKey(plugin) && !apiKey) {
|
|
833
1186
|
logger.debug('[Plugin] Model discovery for %s has no usable API key; keeping the existing catalog', pluginId);
|
|
834
1187
|
return {
|
|
@@ -851,8 +1204,17 @@ export class PluginService {
|
|
|
851
1204
|
if (models.length > 0) {
|
|
852
1205
|
logger.debug('[Plugin] Auto-discovered %d models for %s:', models.length, pluginId, models);
|
|
853
1206
|
const previousModels = plugin.model_map;
|
|
854
|
-
this.
|
|
855
|
-
|
|
1207
|
+
const cacheKey = this.discoveredModelsCacheKey(pluginId, userId || 'default');
|
|
1208
|
+
if ((this.discoveryCacheRevisions.get(cacheKey) ?? 0) !==
|
|
1209
|
+
expectedRevision) {
|
|
1210
|
+
return {
|
|
1211
|
+
models: previousModels,
|
|
1212
|
+
outcome: 'unavailable',
|
|
1213
|
+
reason: 'Provider configuration changed during discovery',
|
|
1214
|
+
};
|
|
1215
|
+
}
|
|
1216
|
+
await this.storeDiscoveredModels(pluginId, models, userId);
|
|
1217
|
+
const stored = (await this.getDiscoveredModels(pluginId, userId)) || models;
|
|
856
1218
|
return {
|
|
857
1219
|
models: stored,
|
|
858
1220
|
outcome: JSON.stringify(stored) === JSON.stringify(previousModels)
|
|
@@ -874,26 +1236,46 @@ export class PluginService {
|
|
|
874
1236
|
}
|
|
875
1237
|
}
|
|
876
1238
|
// List all installed plugins
|
|
877
|
-
|
|
1239
|
+
async getAllPluginsUnchecked(userId) {
|
|
1240
|
+
await this.ensureLegacyActivationMigration();
|
|
878
1241
|
const plugins = new Map();
|
|
879
|
-
const activePluginIds = pluginActivationService.getActivePluginIds(userId);
|
|
1242
|
+
const activePluginIds = await pluginActivationService.getActivePluginIds(userId);
|
|
880
1243
|
for (const pluginsDir of this.pluginReadDirs) {
|
|
881
|
-
|
|
1244
|
+
const directoryStat = (() => {
|
|
1245
|
+
try {
|
|
1246
|
+
return fs.lstatSync(pluginsDir);
|
|
1247
|
+
}
|
|
1248
|
+
catch {
|
|
1249
|
+
return null;
|
|
1250
|
+
}
|
|
1251
|
+
})();
|
|
1252
|
+
if (!directoryStat) {
|
|
1253
|
+
continue;
|
|
1254
|
+
}
|
|
1255
|
+
if (!directoryStat.isDirectory() || directoryStat.isSymbolicLink()) {
|
|
1256
|
+
plugins.clear();
|
|
1257
|
+
logger.error('Ignoring unsafe plugin directory %s and every lower-priority definition', pluginsDir);
|
|
882
1258
|
continue;
|
|
883
1259
|
}
|
|
884
1260
|
try {
|
|
885
|
-
const
|
|
886
|
-
for (const
|
|
1261
|
+
const entries = fs.readdirSync(pluginsDir, { withFileTypes: true });
|
|
1262
|
+
for (const entry of entries) {
|
|
1263
|
+
const file = entry.name;
|
|
887
1264
|
if (file.endsWith('.json') && !file.startsWith('.')) {
|
|
888
1265
|
const filenameId = path.basename(file, '.json');
|
|
889
1266
|
try {
|
|
890
1267
|
const filePath = path.join(pluginsDir, file);
|
|
891
|
-
|
|
1268
|
+
if (!entry.isFile() || entry.isSymbolicLink()) {
|
|
1269
|
+
plugins.delete(filenameId);
|
|
1270
|
+
logger.error('Ignoring non-regular plugin definition %s', filePath);
|
|
1271
|
+
continue;
|
|
1272
|
+
}
|
|
1273
|
+
const content = readRegularPluginDefinition(filePath);
|
|
892
1274
|
const parsedPlugin = JSON.parse(content);
|
|
893
1275
|
// Validate plugin structure
|
|
894
1276
|
if (this.validatePlugin(parsedPlugin) &&
|
|
895
1277
|
parsedPlugin.id === filenameId) {
|
|
896
|
-
if (!this.isPluginDefinitionApproved(parsedPlugin, filePath)) {
|
|
1278
|
+
if (!(await this.isPluginDefinitionApproved(parsedPlugin, filePath))) {
|
|
897
1279
|
// A later writable definition shadows an earlier bundled
|
|
898
1280
|
// definition even while quarantined.
|
|
899
1281
|
plugins.delete(parsedPlugin.id);
|
|
@@ -901,7 +1283,7 @@ export class PluginService {
|
|
|
901
1283
|
}
|
|
902
1284
|
const plugin = applyPluginDefinitionPolicy(parsedPlugin);
|
|
903
1285
|
plugin.active = activePluginIds.has(plugin.id);
|
|
904
|
-
plugins.set(plugin.id, this.applyDiscoveredModels(plugin, userId));
|
|
1286
|
+
plugins.set(plugin.id, await this.applyDiscoveredModels(plugin, userId));
|
|
905
1287
|
}
|
|
906
1288
|
else if (parsedPlugin.id !== filenameId) {
|
|
907
1289
|
plugins.delete(filenameId);
|
|
@@ -924,41 +1306,84 @@ export class PluginService {
|
|
|
924
1306
|
logger.error(`Failed to read plugins directory ${pluginsDir}:`, error);
|
|
925
1307
|
}
|
|
926
1308
|
}
|
|
927
|
-
|
|
1309
|
+
if (this.sharedPluginDefinitions) {
|
|
1310
|
+
const definitions = await this.repositories().pluginDefinitions.list();
|
|
1311
|
+
const currentDefinitionIds = new Set(definitions.map(definition => definition.plugin_id));
|
|
1312
|
+
for (const pluginId of this.sharedDefinitionIds) {
|
|
1313
|
+
if (!currentDefinitionIds.has(pluginId)) {
|
|
1314
|
+
this.sharedDefinitionIds.delete(pluginId);
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
for (const definition of definitions) {
|
|
1318
|
+
// The shared row has precedence over bundled release assets even when
|
|
1319
|
+
// corrupt or unapproved, preserving quarantine rather than silently
|
|
1320
|
+
// changing the provider route on another replica.
|
|
1321
|
+
plugins.delete(definition.plugin_id);
|
|
1322
|
+
const plugin = this.parseSharedPluginDefinition(definition);
|
|
1323
|
+
if (!plugin) {
|
|
1324
|
+
logger.error('Ignoring invalid or unapproved shared plugin definition %s', definition.plugin_id);
|
|
1325
|
+
continue;
|
|
1326
|
+
}
|
|
1327
|
+
plugin.active = activePluginIds.has(plugin.id);
|
|
1328
|
+
plugins.set(plugin.id, await this.applyDiscoveredModels(plugin, userId));
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
return Array.from(plugins.values());
|
|
1332
|
+
}
|
|
1333
|
+
async getAllPlugins(userId) {
|
|
1334
|
+
const plugins = await this.getAllPluginsUnchecked(userId);
|
|
1335
|
+
const visible = [];
|
|
1336
|
+
for (const plugin of plugins) {
|
|
1337
|
+
if (await this.isPluginVisibleToUser(plugin, userId)) {
|
|
1338
|
+
visible.push(plugin);
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
return visible;
|
|
928
1342
|
}
|
|
929
1343
|
/**
|
|
930
1344
|
* The codex-oauth plugin rides the server user's ChatGPT sign-in, so it is
|
|
931
1345
|
* administrator-only and hidden entirely when no sign-in exists.
|
|
932
1346
|
*/
|
|
933
|
-
isPluginVisibleToUser(plugin, userId) {
|
|
1347
|
+
async isPluginVisibleToUser(plugin, userId) {
|
|
934
1348
|
if (plugin.id !== CODEX_OAUTH_PLUGIN_ID)
|
|
935
1349
|
return true;
|
|
936
1350
|
if (!codexOAuthService.isAvailable())
|
|
937
1351
|
return false;
|
|
938
1352
|
if (!userId)
|
|
939
1353
|
return false;
|
|
940
|
-
return userModel.getUserById(userId)?.role === 'admin';
|
|
1354
|
+
return (await userModel.getUserById(userId))?.role === 'admin';
|
|
941
1355
|
}
|
|
942
1356
|
// Get a specific plugin by ID
|
|
943
|
-
|
|
1357
|
+
async loadPlugin(id, userId) {
|
|
1358
|
+
await this.ensureLegacyActivationMigration();
|
|
944
1359
|
// Sanitize the ID to prevent path traversal
|
|
945
1360
|
const sanitizedId = sanitize(id);
|
|
946
1361
|
if (!sanitizedId || sanitizedId !== id) {
|
|
947
1362
|
logger.error('Invalid plugin ID provided:', id);
|
|
948
1363
|
return null;
|
|
949
1364
|
}
|
|
1365
|
+
if (this.sharedPluginDefinitions) {
|
|
1366
|
+
const definition = await this.repositories().pluginDefinitions.find(sanitizedId);
|
|
1367
|
+
if (definition) {
|
|
1368
|
+
const plugin = this.parseSharedPluginDefinition(definition);
|
|
1369
|
+
if (!plugin)
|
|
1370
|
+
return null;
|
|
1371
|
+
plugin.active = await this.isPluginActive(plugin.id, userId);
|
|
1372
|
+
return this.applyDiscoveredModels(plugin, userId);
|
|
1373
|
+
}
|
|
1374
|
+
this.sharedDefinitionIds.delete(sanitizedId);
|
|
1375
|
+
}
|
|
950
1376
|
const filePath = this.resolveEffectivePluginFilePath(sanitizedId);
|
|
951
1377
|
if (!filePath)
|
|
952
1378
|
return null;
|
|
953
1379
|
try {
|
|
954
|
-
const content =
|
|
1380
|
+
const content = readRegularPluginDefinition(filePath);
|
|
955
1381
|
const parsedPlugin = JSON.parse(content);
|
|
956
1382
|
if (this.validatePlugin(parsedPlugin) &&
|
|
957
1383
|
parsedPlugin.id === sanitizedId &&
|
|
958
|
-
this.isPluginDefinitionApproved(parsedPlugin, filePath)
|
|
959
|
-
this.isPluginVisibleToUser(parsedPlugin, userId)) {
|
|
1384
|
+
(await this.isPluginDefinitionApproved(parsedPlugin, filePath))) {
|
|
960
1385
|
const plugin = applyPluginDefinitionPolicy(parsedPlugin);
|
|
961
|
-
plugin.active = this.isPluginActive(plugin.id, userId);
|
|
1386
|
+
plugin.active = await this.isPluginActive(plugin.id, userId);
|
|
962
1387
|
return this.applyDiscoveredModels(plugin, userId);
|
|
963
1388
|
}
|
|
964
1389
|
}
|
|
@@ -967,15 +1392,29 @@ export class PluginService {
|
|
|
967
1392
|
}
|
|
968
1393
|
return null;
|
|
969
1394
|
}
|
|
1395
|
+
async getPluginUnchecked(id, userId) {
|
|
1396
|
+
if (id === CODEX_OAUTH_PLUGIN_ID)
|
|
1397
|
+
return null;
|
|
1398
|
+
return this.loadPlugin(id, userId);
|
|
1399
|
+
}
|
|
1400
|
+
async getPlugin(id, userId) {
|
|
1401
|
+
const plugin = await this.loadPlugin(id, userId);
|
|
1402
|
+
if (!plugin || !(await this.isPluginVisibleToUser(plugin, userId))) {
|
|
1403
|
+
return null;
|
|
1404
|
+
}
|
|
1405
|
+
return plugin;
|
|
1406
|
+
}
|
|
970
1407
|
// Install or update a plugin
|
|
971
|
-
installPlugin(pluginData, approvedByUserId) {
|
|
1408
|
+
async installPlugin(pluginData, approvedByUserId) {
|
|
1409
|
+
if (!this.sharedPluginDefinitions)
|
|
1410
|
+
this.ensurePluginsDirectory();
|
|
972
1411
|
if (!this.validatePlugin(pluginData)) {
|
|
973
1412
|
throw new Error('Invalid plugin structure');
|
|
974
1413
|
}
|
|
975
1414
|
if (pluginData.id === CODEX_OAUTH_PLUGIN_ID) {
|
|
976
1415
|
throw new Error('The bundled Codex OAuth plugin ID is reserved');
|
|
977
1416
|
}
|
|
978
|
-
if (!this.canUseStoredConnectionOverrides(approvedByUserId)) {
|
|
1417
|
+
if (!(await this.canUseStoredConnectionOverrides(approvedByUserId))) {
|
|
979
1418
|
throw new Error('Administrator approval is required');
|
|
980
1419
|
}
|
|
981
1420
|
const now = Date.now();
|
|
@@ -988,6 +1427,20 @@ export class PluginService {
|
|
|
988
1427
|
const safeId = plugin.id.replace(/[^a-zA-Z0-9_.-]/g, '');
|
|
989
1428
|
if (safeId !== plugin.id)
|
|
990
1429
|
throw new Error('Invalid plugin ID');
|
|
1430
|
+
if (this.sharedPluginDefinitions) {
|
|
1431
|
+
await this.repositories().pluginDefinitions.replaceApproved({
|
|
1432
|
+
plugin_id: plugin.id,
|
|
1433
|
+
definition_json: JSON.stringify(plugin, null, 2),
|
|
1434
|
+
definition_fingerprint: getPluginDefinitionFingerprint(plugin),
|
|
1435
|
+
approved_by_user_id: approvedByUserId,
|
|
1436
|
+
approved_at: now,
|
|
1437
|
+
created_at: plugin.created_at || now,
|
|
1438
|
+
updated_at: now,
|
|
1439
|
+
});
|
|
1440
|
+
this.sharedDefinitionIds.add(plugin.id);
|
|
1441
|
+
await this.clearDiscoveredModels(plugin.id);
|
|
1442
|
+
return plugin;
|
|
1443
|
+
}
|
|
991
1444
|
const filePath = path.resolve(this.pluginsDir, `${safeId}.json`);
|
|
992
1445
|
if (!filePath.startsWith(path.resolve(this.pluginsDir))) {
|
|
993
1446
|
throw new Error('Path traversal detected');
|
|
@@ -995,8 +1448,8 @@ export class PluginService {
|
|
|
995
1448
|
// Revoke definition approval and every user's activation before replacing
|
|
996
1449
|
// bytes on disk. Any crash or write failure therefore leaves the provider
|
|
997
1450
|
// quarantined and inactive.
|
|
998
|
-
this.revokePluginDefinitionConsent(plugin.id);
|
|
999
|
-
this.clearDiscoveredModels(plugin.id);
|
|
1451
|
+
await this.revokePluginDefinitionConsent(plugin.id);
|
|
1452
|
+
await this.clearDiscoveredModels(plugin.id);
|
|
1000
1453
|
const temporaryPath = path.resolve(this.pluginsDir, `.${safeId}.${process.pid}.${Date.now()}.tmp`);
|
|
1001
1454
|
try {
|
|
1002
1455
|
fs.writeFileSync(temporaryPath, JSON.stringify(plugin, null, 2), {
|
|
@@ -1010,11 +1463,11 @@ export class PluginService {
|
|
|
1010
1463
|
}
|
|
1011
1464
|
throw error;
|
|
1012
1465
|
}
|
|
1013
|
-
this.approvePluginDefinition(plugin, filePath, approvedByUserId);
|
|
1466
|
+
await this.approvePluginDefinition(plugin, filePath, approvedByUserId);
|
|
1014
1467
|
return plugin;
|
|
1015
1468
|
}
|
|
1016
1469
|
// Delete a plugin
|
|
1017
|
-
deletePlugin(id) {
|
|
1470
|
+
async deletePlugin(id) {
|
|
1018
1471
|
// Validate the ID parameter using a strict pattern (allows dots for version numbers like 1.6b)
|
|
1019
1472
|
const idPattern = /^[a-zA-Z0-9._-]+$/;
|
|
1020
1473
|
if (!idPattern.test(id)) {
|
|
@@ -1027,23 +1480,37 @@ export class PluginService {
|
|
|
1027
1480
|
logger.error('Plugin ID failed sanitization:', id);
|
|
1028
1481
|
return false;
|
|
1029
1482
|
}
|
|
1483
|
+
if (this.sharedPluginDefinitions) {
|
|
1484
|
+
try {
|
|
1485
|
+
const removed = await this.repositories().pluginDefinitions.deleteWithState(id);
|
|
1486
|
+
if (!removed)
|
|
1487
|
+
return false;
|
|
1488
|
+
this.sharedDefinitionIds.delete(id);
|
|
1489
|
+
await this.clearDiscoveredModels(id);
|
|
1490
|
+
return true;
|
|
1491
|
+
}
|
|
1492
|
+
catch (error) {
|
|
1493
|
+
logger.error('Failed to delete shared plugin %s:', sanitizedId, error);
|
|
1494
|
+
return false;
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1030
1497
|
const bundledDirectory = path.resolve(this.bundledPluginsDir);
|
|
1031
|
-
const writablePluginDirs = Array.from(new Set([this.pluginsDir, this.
|
|
1498
|
+
const writablePluginDirs = Array.from(new Set([this.pluginsDir, ...this.legacyPluginsDirs])).filter(pluginsDir => path.resolve(pluginsDir) !== bundledDirectory);
|
|
1032
1499
|
const filePath = writablePluginDirs
|
|
1033
1500
|
.map(pluginsDir => path.resolve(pluginsDir, `${sanitizedId}.json`))
|
|
1034
|
-
.find(candidate =>
|
|
1501
|
+
.find(candidate => isRegularPluginDefinition(candidate));
|
|
1035
1502
|
if (!filePath) {
|
|
1036
1503
|
logger.error('File path is invalid or does not exist:', filePath);
|
|
1037
1504
|
return false;
|
|
1038
1505
|
}
|
|
1039
1506
|
try {
|
|
1040
1507
|
fs.unlinkSync(filePath);
|
|
1041
|
-
pluginActivationService.deletePlugin(id);
|
|
1042
|
-
this.removePluginDefinitionApproval(id);
|
|
1508
|
+
await pluginActivationService.deletePlugin(id);
|
|
1509
|
+
await this.removePluginDefinitionApproval(id);
|
|
1043
1510
|
// Clean up stored variables
|
|
1044
|
-
pluginVariablesService.deletePluginVariables(id);
|
|
1045
|
-
pluginCredentialsService.deleteAllPluginCredentials(id);
|
|
1046
|
-
this.clearDiscoveredModels(id);
|
|
1511
|
+
await pluginVariablesService.deletePluginVariables(id);
|
|
1512
|
+
await pluginCredentialsService.deleteAllPluginCredentials(id);
|
|
1513
|
+
await this.clearDiscoveredModels(id);
|
|
1047
1514
|
return true;
|
|
1048
1515
|
}
|
|
1049
1516
|
catch (error) {
|
|
@@ -1053,21 +1520,31 @@ export class PluginService {
|
|
|
1053
1520
|
}
|
|
1054
1521
|
// Activate a plugin
|
|
1055
1522
|
async activatePlugin(id, userId) {
|
|
1056
|
-
const plugin = this.getPlugin(id, userId);
|
|
1523
|
+
const plugin = await this.getPlugin(id, userId);
|
|
1057
1524
|
if (!plugin) {
|
|
1058
1525
|
throw new Error('Plugin not found');
|
|
1059
1526
|
}
|
|
1060
|
-
if (!pluginActivationService.activate(id, userId)) {
|
|
1527
|
+
if (!(await pluginActivationService.activate(id, userId))) {
|
|
1061
1528
|
throw new Error('Failed to persist plugin activation');
|
|
1062
1529
|
}
|
|
1063
1530
|
// Wait for discovery so the activation response and the UI's first reload
|
|
1064
|
-
// observe the same user-scoped model catalog.
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1531
|
+
// observe the same user-scoped model catalog. These writes are deliberately
|
|
1532
|
+
// sequential: each persisted catalog publishes a cross-replica plugin-user
|
|
1533
|
+
// invalidation. Running sibling discoveries concurrently would make the
|
|
1534
|
+
// first completed catalog advance every sibling's configuration revision,
|
|
1535
|
+
// causing the remaining valid results to be discarded as stale.
|
|
1536
|
+
await this.discoverModels(id, userId).catch(() => []);
|
|
1537
|
+
for (const capability of [
|
|
1538
|
+
'image',
|
|
1539
|
+
'stt',
|
|
1540
|
+
'tts',
|
|
1541
|
+
'audio',
|
|
1542
|
+
'video',
|
|
1543
|
+
]) {
|
|
1544
|
+
if (!plugin.capabilities?.[capability]?.models_endpoint)
|
|
1545
|
+
continue;
|
|
1546
|
+
await this.discoverCapabilityModels(id, capability, userId).catch(() => undefined);
|
|
1547
|
+
}
|
|
1071
1548
|
return true;
|
|
1072
1549
|
}
|
|
1073
1550
|
// Deactivate a specific plugin
|
|
@@ -1076,67 +1553,104 @@ export class PluginService {
|
|
|
1076
1553
|
return pluginActivationService.deactivate(id, userId);
|
|
1077
1554
|
}
|
|
1078
1555
|
// Get the active plugin for a specific model
|
|
1079
|
-
getActivePluginForModel(model, userId, pluginId) {
|
|
1556
|
+
async getActivePluginForModel(model, userId, pluginId) {
|
|
1080
1557
|
if (pluginId) {
|
|
1081
|
-
|
|
1558
|
+
let plugin = await this.getPlugin(pluginId, userId);
|
|
1082
1559
|
if (!plugin) {
|
|
1083
1560
|
throw new Error(`Plugin not found: ${pluginId}`);
|
|
1084
1561
|
}
|
|
1085
|
-
if (!this.isPluginActive(pluginId, userId)) {
|
|
1562
|
+
if (!(await this.isPluginActive(pluginId, userId))) {
|
|
1086
1563
|
throw new Error(`Plugin is not active: ${pluginId}`);
|
|
1087
1564
|
}
|
|
1088
1565
|
if (!plugin.model_map.includes(model)) {
|
|
1089
|
-
|
|
1566
|
+
// The UI offers a live-fetched catalog, so a just-released provider
|
|
1567
|
+
// model can be requested before the shared store carries it — and an
|
|
1568
|
+
// external worker resolves from the shared store only. Refresh a due
|
|
1569
|
+
// catalog once before rejecting; the discovery backoff keeps a
|
|
1570
|
+
// mistyped model from probing the provider on every request.
|
|
1571
|
+
if (await this.isModelDiscoveryDue(pluginId, userId)) {
|
|
1572
|
+
await this.discoverModelsResult(pluginId, userId).catch(() => undefined);
|
|
1573
|
+
plugin = await this.getPlugin(pluginId, userId);
|
|
1574
|
+
}
|
|
1575
|
+
if (!plugin?.model_map.includes(model)) {
|
|
1576
|
+
throw new Error(`Model ${model} is not supported by plugin ${pluginId}`);
|
|
1577
|
+
}
|
|
1090
1578
|
}
|
|
1091
|
-
this.resolveOperationEndpoint(plugin, userId);
|
|
1092
|
-
const apiKey = this.getApiKey(plugin, userId);
|
|
1579
|
+
await this.resolveOperationEndpoint(plugin, userId);
|
|
1580
|
+
const apiKey = await this.getApiKey(plugin, userId);
|
|
1093
1581
|
if (pluginRequiresApiKey(plugin) && !apiKey) {
|
|
1094
1582
|
throw new Error(`API key not found for plugin ${pluginId} (save a provider credential in Settings)`);
|
|
1095
1583
|
}
|
|
1096
1584
|
return plugin;
|
|
1097
1585
|
}
|
|
1098
1586
|
// Only route through plugins the user explicitly activated.
|
|
1099
|
-
const activePlugins = this.getActivePlugins(userId);
|
|
1587
|
+
const activePlugins = await this.getActivePlugins(userId);
|
|
1100
1588
|
// Find the active plugin that supports this model
|
|
1101
1589
|
for (const plugin of activePlugins) {
|
|
1102
1590
|
if (plugin.model_map.includes(model)) {
|
|
1103
1591
|
// Local OpenAI-compatible servers can explicitly opt out of auth by
|
|
1104
1592
|
// leaving both auth fields empty.
|
|
1105
|
-
this.resolveOperationEndpoint(plugin, userId);
|
|
1106
|
-
const apiKey = this.getApiKey(plugin, userId);
|
|
1593
|
+
await this.resolveOperationEndpoint(plugin, userId);
|
|
1594
|
+
const apiKey = await this.getApiKey(plugin, userId);
|
|
1107
1595
|
if (pluginRequiresApiKey(plugin) && !apiKey) {
|
|
1108
1596
|
continue;
|
|
1109
1597
|
}
|
|
1110
1598
|
return plugin;
|
|
1111
1599
|
}
|
|
1112
1600
|
}
|
|
1601
|
+
// Same store-vs-catalog skew guard for sessions without an explicit
|
|
1602
|
+
// provider: refresh every due catalog once, then rescan before giving up.
|
|
1603
|
+
let refreshedAnyCatalog = false;
|
|
1604
|
+
for (const plugin of activePlugins) {
|
|
1605
|
+
if ((plugin.type === 'completion' || plugin.type === 'chat') &&
|
|
1606
|
+
(await this.isModelDiscoveryDue(plugin.id, userId))) {
|
|
1607
|
+
await this.discoverModelsResult(plugin.id, userId).catch(() => undefined);
|
|
1608
|
+
refreshedAnyCatalog = true;
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
if (refreshedAnyCatalog) {
|
|
1612
|
+
for (const plugin of await this.getActivePlugins(userId)) {
|
|
1613
|
+
if (plugin.model_map.includes(model)) {
|
|
1614
|
+
await this.resolveOperationEndpoint(plugin, userId);
|
|
1615
|
+
const apiKey = await this.getApiKey(plugin, userId);
|
|
1616
|
+
if (pluginRequiresApiKey(plugin) && !apiKey) {
|
|
1617
|
+
continue;
|
|
1618
|
+
}
|
|
1619
|
+
return plugin;
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1113
1623
|
return null;
|
|
1114
1624
|
}
|
|
1115
1625
|
// Get all currently active plugins
|
|
1116
|
-
getActivePlugins(userId) {
|
|
1117
|
-
const allPlugins = this.getAllPlugins(userId);
|
|
1626
|
+
async getActivePlugins(userId) {
|
|
1627
|
+
const allPlugins = await this.getAllPlugins(userId);
|
|
1118
1628
|
const activePlugins = allPlugins.filter(plugin => plugin.active);
|
|
1119
1629
|
return activePlugins;
|
|
1120
1630
|
}
|
|
1631
|
+
async getActivePluginsUnchecked(userId) {
|
|
1632
|
+
return (await this.getAllPluginsUnchecked(userId)).filter(plugin => plugin.active && plugin.id !== CODEX_OAUTH_PLUGIN_ID);
|
|
1633
|
+
}
|
|
1121
1634
|
// Legacy method for backward compatibility - returns first active plugin
|
|
1122
|
-
getActivePlugin(userId) {
|
|
1123
|
-
const activePlugins = this.getActivePlugins(userId);
|
|
1635
|
+
async getActivePlugin(userId) {
|
|
1636
|
+
const activePlugins = await this.getActivePlugins(userId);
|
|
1124
1637
|
return activePlugins.length > 0 ? activePlugins[0] : null;
|
|
1125
1638
|
}
|
|
1126
1639
|
// Get plugin status
|
|
1127
|
-
getPluginStatus(userId) {
|
|
1128
|
-
const plugins = this.getAllPlugins(userId);
|
|
1129
|
-
return plugins.map(plugin => ({
|
|
1640
|
+
async getPluginStatus(userId) {
|
|
1641
|
+
const plugins = await this.getAllPlugins(userId);
|
|
1642
|
+
return Promise.all(plugins.map(async (plugin) => ({
|
|
1130
1643
|
id: plugin.id,
|
|
1131
1644
|
active: plugin.active || false,
|
|
1132
1645
|
available: !pluginRequiresApiKey(plugin) ||
|
|
1133
|
-
this.getApiKey(plugin, userId) !== null,
|
|
1134
|
-
}));
|
|
1646
|
+
(await this.getApiKey(plugin, userId)) !== null,
|
|
1647
|
+
})));
|
|
1135
1648
|
}
|
|
1136
1649
|
// Execute a chat request through the active plugin
|
|
1137
|
-
async executePluginRequest(model, messages, options = {}, userId, pluginId) {
|
|
1650
|
+
async executePluginRequest(model, messages, options = {}, userId, pluginId, signal) {
|
|
1651
|
+
throwIfChatGenerationCancelled(signal);
|
|
1138
1652
|
validatePluginModel(model);
|
|
1139
|
-
const activePlugin = this.getActivePluginForModel(model, userId, pluginId);
|
|
1653
|
+
const activePlugin = await this.getActivePluginForModel(model, userId, pluginId);
|
|
1140
1654
|
if (!activePlugin) {
|
|
1141
1655
|
throw new Error(`No active plugin found for model: ${model}`);
|
|
1142
1656
|
}
|
|
@@ -1144,11 +1658,11 @@ export class PluginService {
|
|
|
1144
1658
|
throw new Error(`Model ${model} is not supported by plugin ${activePlugin.id}`);
|
|
1145
1659
|
}
|
|
1146
1660
|
if (activePlugin.id === CODEX_OAUTH_PLUGIN_ID) {
|
|
1147
|
-
await codexOAuthService.ensureFreshToken();
|
|
1661
|
+
await codexOAuthService.ensureFreshToken(signal);
|
|
1148
1662
|
// The codex endpoint only answers as an SSE stream; aggregate it here
|
|
1149
1663
|
// so non-streaming callers still get a complete response.
|
|
1150
1664
|
let aggregated = '';
|
|
1151
|
-
for await (const chunk of this.executePluginStreamRequest(model, messages, options, userId, activePlugin.id)) {
|
|
1665
|
+
for await (const chunk of this.executePluginStreamRequest(model, messages, options, userId, activePlugin.id, signal)) {
|
|
1152
1666
|
if (chunk.type === 'content' && chunk.content) {
|
|
1153
1667
|
aggregated += chunk.content;
|
|
1154
1668
|
}
|
|
@@ -1157,11 +1671,11 @@ export class PluginService {
|
|
|
1157
1671
|
choices: [{ message: { role: 'assistant', content: aggregated } }],
|
|
1158
1672
|
};
|
|
1159
1673
|
}
|
|
1160
|
-
const pluginVars = this.getPluginVariables(activePlugin, userId);
|
|
1674
|
+
const pluginVars = await this.getPluginVariables(activePlugin, userId);
|
|
1161
1675
|
const { apiMode, endpoint: effectiveEndpoint } = resolvePluginApiConfig(activePlugin, pluginVars);
|
|
1162
1676
|
const processedEndpoint = applyModelEndpointTemplate(effectiveEndpoint, model);
|
|
1163
1677
|
assertSafePluginEndpoint(processedEndpoint, 'endpoint URL constructed');
|
|
1164
|
-
const apiKey = this.getApiKey(activePlugin, userId);
|
|
1678
|
+
const apiKey = await this.getApiKey(activePlugin, userId);
|
|
1165
1679
|
if (pluginRequiresApiKey(activePlugin) && !apiKey) {
|
|
1166
1680
|
throw new Error(`API key not found for plugin ${activePlugin.id} (save a provider credential in Settings)`);
|
|
1167
1681
|
}
|
|
@@ -1177,6 +1691,7 @@ export class PluginService {
|
|
|
1177
1691
|
headers,
|
|
1178
1692
|
timeout: 60000, // 60 second timeout
|
|
1179
1693
|
maxRedirects: 0,
|
|
1694
|
+
signal,
|
|
1180
1695
|
});
|
|
1181
1696
|
const normalized = convertProviderResponse(activePlugin, response.data, model, apiMode, providerStateScope);
|
|
1182
1697
|
pluginUsageService.record({
|
|
@@ -1198,16 +1713,22 @@ export class PluginService {
|
|
|
1198
1713
|
return normalized;
|
|
1199
1714
|
}
|
|
1200
1715
|
catch (error) {
|
|
1716
|
+
const cancelled = isChatGenerationCancelled(error, signal);
|
|
1201
1717
|
pluginUsageService.record({
|
|
1202
1718
|
userId,
|
|
1203
1719
|
pluginId: activePlugin.id,
|
|
1204
1720
|
pluginName: activePlugin.name,
|
|
1205
1721
|
capability: 'chat',
|
|
1206
1722
|
model,
|
|
1207
|
-
status: 'error',
|
|
1723
|
+
status: cancelled ? 'cancelled' : 'error',
|
|
1208
1724
|
durationMs: Date.now() - startedAt,
|
|
1209
1725
|
});
|
|
1210
|
-
|
|
1726
|
+
if (!cancelled) {
|
|
1727
|
+
logger.error(`Plugin request failed for ${activePlugin.id}:`, error);
|
|
1728
|
+
}
|
|
1729
|
+
if (cancelled) {
|
|
1730
|
+
throw error;
|
|
1731
|
+
}
|
|
1211
1732
|
if (error && typeof error === 'object' && 'response' in error) {
|
|
1212
1733
|
const axiosError = error;
|
|
1213
1734
|
throw new Error(`Plugin API error: ${axiosError.response.status} - ${axiosError.response.data?.error?.message || axiosError.response.statusText}`);
|
|
@@ -1226,9 +1747,10 @@ export class PluginService {
|
|
|
1226
1747
|
* Returns an async generator that yields SSE delta content strings.
|
|
1227
1748
|
* Also collects tool_calls from the stream.
|
|
1228
1749
|
*/
|
|
1229
|
-
async *executePluginStreamRequest(model, messages, options = {}, userId, pluginId) {
|
|
1750
|
+
async *executePluginStreamRequest(model, messages, options = {}, userId, pluginId, signal) {
|
|
1751
|
+
throwIfChatGenerationCancelled(signal);
|
|
1230
1752
|
validatePluginModel(model);
|
|
1231
|
-
const activePlugin = this.getActivePluginForModel(model, userId, pluginId);
|
|
1753
|
+
const activePlugin = await this.getActivePluginForModel(model, userId, pluginId);
|
|
1232
1754
|
if (!activePlugin) {
|
|
1233
1755
|
throw new Error(`No active plugin found for model: ${model}`);
|
|
1234
1756
|
}
|
|
@@ -1236,13 +1758,13 @@ export class PluginService {
|
|
|
1236
1758
|
throw new Error(`Model ${model} is not supported by plugin ${activePlugin.id}`);
|
|
1237
1759
|
}
|
|
1238
1760
|
if (activePlugin.id === CODEX_OAUTH_PLUGIN_ID) {
|
|
1239
|
-
await codexOAuthService.ensureFreshToken();
|
|
1761
|
+
await codexOAuthService.ensureFreshToken(signal);
|
|
1240
1762
|
}
|
|
1241
|
-
const pluginVars = this.getPluginVariables(activePlugin, userId);
|
|
1763
|
+
const pluginVars = await this.getPluginVariables(activePlugin, userId);
|
|
1242
1764
|
const { apiMode, endpoint: effectiveEndpoint } = resolvePluginApiConfig(activePlugin, pluginVars);
|
|
1243
1765
|
const processedEndpoint = applyModelEndpointTemplate(effectiveEndpoint, model);
|
|
1244
1766
|
assertSafePluginEndpoint(processedEndpoint);
|
|
1245
|
-
const apiKey = this.getApiKey(activePlugin, userId);
|
|
1767
|
+
const apiKey = await this.getApiKey(activePlugin, userId);
|
|
1246
1768
|
if (pluginRequiresApiKey(activePlugin) && !apiKey) {
|
|
1247
1769
|
throw new Error(`API key not found for plugin ${activePlugin.id} (save a provider credential in Settings)`);
|
|
1248
1770
|
}
|
|
@@ -1301,6 +1823,7 @@ export class PluginService {
|
|
|
1301
1823
|
headers,
|
|
1302
1824
|
body: JSON.stringify(payload),
|
|
1303
1825
|
redirect: 'error',
|
|
1826
|
+
signal,
|
|
1304
1827
|
});
|
|
1305
1828
|
if (activePlugin.id === 'anthropic') {
|
|
1306
1829
|
yield* forward(streamAnthropicResponse(response));
|
|
@@ -1377,7 +1900,7 @@ export class PluginService {
|
|
|
1377
1900
|
status = 'success';
|
|
1378
1901
|
}
|
|
1379
1902
|
catch (error) {
|
|
1380
|
-
status = 'error';
|
|
1903
|
+
status = isChatGenerationCancelled(error, signal) ? 'cancelled' : 'error';
|
|
1381
1904
|
throw error;
|
|
1382
1905
|
}
|
|
1383
1906
|
finally {
|
|
@@ -1436,23 +1959,29 @@ export class PluginService {
|
|
|
1436
1959
|
pluginRecord.model_map.length > 0);
|
|
1437
1960
|
}
|
|
1438
1961
|
// Export plugin to JSON
|
|
1439
|
-
exportPlugin(id, userId) {
|
|
1962
|
+
async exportPlugin(id, userId) {
|
|
1440
1963
|
return this.getPlugin(id, userId);
|
|
1441
1964
|
}
|
|
1442
1965
|
// Import plugin from JSON data
|
|
1443
|
-
importPlugin(pluginData, approvedByUserId) {
|
|
1966
|
+
async importPlugin(pluginData, approvedByUserId) {
|
|
1444
1967
|
// Validate and clean the plugin data
|
|
1445
1968
|
if (!this.validatePlugin(pluginData)) {
|
|
1446
1969
|
throw new Error('Invalid plugin data');
|
|
1447
1970
|
}
|
|
1971
|
+
if (this.sharedPluginDefinitions) {
|
|
1972
|
+
const existing = await this.repositories().pluginDefinitions.find(pluginData.id);
|
|
1973
|
+
if (existing && this.parseSharedPluginDefinition(existing)) {
|
|
1974
|
+
throw new Error(`Plugin with ID ${pluginData.id} already exists`);
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1448
1977
|
// Check if plugin already exists
|
|
1449
1978
|
const existingPluginPath = this.resolveEffectivePluginFilePath(pluginData.id);
|
|
1450
1979
|
if (existingPluginPath) {
|
|
1451
1980
|
try {
|
|
1452
|
-
const existingPlugin = JSON.parse(
|
|
1981
|
+
const existingPlugin = JSON.parse(readRegularPluginDefinition(existingPluginPath));
|
|
1453
1982
|
if (this.validatePlugin(existingPlugin) &&
|
|
1454
1983
|
existingPlugin.id === pluginData.id &&
|
|
1455
|
-
this.isPluginDefinitionApproved(existingPlugin, existingPluginPath)) {
|
|
1984
|
+
(await this.isPluginDefinitionApproved(existingPlugin, existingPluginPath))) {
|
|
1456
1985
|
throw new Error(`Plugin with ID ${pluginData.id} already exists`);
|
|
1457
1986
|
}
|
|
1458
1987
|
}
|
|
@@ -1470,59 +1999,88 @@ export class PluginService {
|
|
|
1470
1999
|
// ============================================
|
|
1471
2000
|
// Capability Methods
|
|
1472
2001
|
// ============================================
|
|
1473
|
-
getPluginForTTS(model, pluginId, userId) {
|
|
2002
|
+
async getPluginForTTS(model, pluginId, userId) {
|
|
1474
2003
|
return this.ttsService.getPluginForTTS(model, pluginId, userId);
|
|
1475
2004
|
}
|
|
1476
|
-
getPluginForEmbedding(model, pluginId, userId) {
|
|
2005
|
+
async getPluginForEmbedding(model, pluginId, userId) {
|
|
1477
2006
|
return this.embeddingService.getPluginForEmbedding(model, pluginId, userId);
|
|
1478
2007
|
}
|
|
1479
|
-
getAvailableEmbeddingModels(userId) {
|
|
2008
|
+
async getAvailableEmbeddingModels(userId) {
|
|
1480
2009
|
return this.embeddingService.getAvailableEmbeddingModels(userId);
|
|
1481
2010
|
}
|
|
1482
|
-
async executeEmbeddingRequest(model, input, pluginId, userId) {
|
|
1483
|
-
return this.embeddingService.executeEmbeddingRequest(model, input, pluginId, userId);
|
|
2011
|
+
async executeEmbeddingRequest(model, input, pluginId, userId, signal) {
|
|
2012
|
+
return this.embeddingService.executeEmbeddingRequest(model, input, pluginId, userId, signal);
|
|
1484
2013
|
}
|
|
1485
|
-
getAvailableTTSModels(userId) {
|
|
2014
|
+
async getAvailableTTSModels(userId) {
|
|
1486
2015
|
return this.ttsService.getAvailableTTSModels(userId);
|
|
1487
2016
|
}
|
|
2017
|
+
async getAvailableSTTModels(userId) {
|
|
2018
|
+
return this.sttService.getAvailableModels(userId);
|
|
2019
|
+
}
|
|
2020
|
+
executeSTTRequest(model, audio, options) {
|
|
2021
|
+
return this.sttService.transcribe(model, audio, options);
|
|
2022
|
+
}
|
|
1488
2023
|
async executeTTSRequest(model, input, options = {}) {
|
|
1489
2024
|
return this.ttsService.executeTTSRequest(model, input, options);
|
|
1490
2025
|
}
|
|
1491
|
-
|
|
2026
|
+
async executeVoiceCloneRequest(model, input, referenceAudio, options = {}) {
|
|
2027
|
+
return this.ttsService.executeVoiceCloneRequest(model, input, referenceAudio, options);
|
|
2028
|
+
}
|
|
2029
|
+
async getTTSConfig(pluginId, userId) {
|
|
1492
2030
|
return this.ttsService.getTTSConfig(pluginId, userId);
|
|
1493
2031
|
}
|
|
1494
|
-
getPluginForImageGen(model, pluginId, userId) {
|
|
2032
|
+
async getPluginForImageGen(model, pluginId, userId) {
|
|
1495
2033
|
return this.imageGenerationService.getPluginForImageGen(model, pluginId, userId);
|
|
1496
2034
|
}
|
|
1497
|
-
getAvailableImageGenModels(userId) {
|
|
2035
|
+
async getAvailableImageGenModels(userId) {
|
|
1498
2036
|
return this.imageGenerationService.getAvailableImageGenModels(userId);
|
|
1499
2037
|
}
|
|
1500
2038
|
async executeImageGenRequest(model, prompt, options) {
|
|
1501
2039
|
return this.imageGenerationService.executeImageGenRequest(model, prompt, options);
|
|
1502
2040
|
}
|
|
1503
|
-
getImageGenConfig(pluginId, userId) {
|
|
2041
|
+
async getImageGenConfig(pluginId, userId) {
|
|
1504
2042
|
return this.imageGenerationService.getImageGenConfig(pluginId, userId);
|
|
1505
2043
|
}
|
|
1506
|
-
getPluginsByCapability(capabilityType, userId) {
|
|
2044
|
+
async getPluginsByCapability(capabilityType, userId) {
|
|
1507
2045
|
return this.capabilityRegistryService.getPluginsByCapability(capabilityType, userId);
|
|
1508
2046
|
}
|
|
1509
|
-
getAvailableAudioGenModels(userId) {
|
|
2047
|
+
async getAvailableAudioGenModels(userId) {
|
|
1510
2048
|
return this.audioGenerationService.getAvailableModels(userId);
|
|
1511
2049
|
}
|
|
1512
2050
|
executeAudioGenRequest(model, prompt, options) {
|
|
1513
2051
|
return this.audioGenerationService.generate(model, prompt, options);
|
|
1514
2052
|
}
|
|
1515
|
-
getAvailableVideoGenModels(userId) {
|
|
2053
|
+
async getAvailableVideoGenModels(userId) {
|
|
1516
2054
|
return this.videoGenerationService.getAvailableModels(userId);
|
|
1517
2055
|
}
|
|
1518
2056
|
submitVideoGenRequest(model, prompt, options) {
|
|
1519
2057
|
return this.videoGenerationService.submit(model, prompt, options);
|
|
1520
2058
|
}
|
|
1521
|
-
pollVideoGenRequest(model, providerJobId, pluginId, userId) {
|
|
1522
|
-
return this.videoGenerationService.poll(model, providerJobId, pluginId, userId);
|
|
1523
|
-
}
|
|
1524
|
-
downloadVideoGenResult(model, providerJobId, pluginId, userId) {
|
|
1525
|
-
return this.videoGenerationService.download(model, providerJobId, pluginId, userId);
|
|
2059
|
+
pollVideoGenRequest(model, providerJobId, pluginId, userId, signal) {
|
|
2060
|
+
return this.videoGenerationService.poll(model, providerJobId, pluginId, userId, signal);
|
|
2061
|
+
}
|
|
2062
|
+
downloadVideoGenResult(model, providerJobId, pluginId, userId, signal) {
|
|
2063
|
+
return this.videoGenerationService.download(model, providerJobId, pluginId, userId, signal);
|
|
2064
|
+
}
|
|
2065
|
+
async canCancelVideoGenRequest(model, pluginId, userId) {
|
|
2066
|
+
return this.videoGenerationService.supportsCancellation(model, pluginId, userId);
|
|
2067
|
+
}
|
|
2068
|
+
cancelVideoGenRequest(model, providerJobId, pluginId, userId, signal) {
|
|
2069
|
+
return this.videoGenerationService.cancel(model, providerJobId, pluginId, userId, signal);
|
|
2070
|
+
}
|
|
2071
|
+
async closeCacheInvalidation() {
|
|
2072
|
+
this.removeCacheInvalidationListener?.();
|
|
2073
|
+
this.removeCacheInvalidationListener = undefined;
|
|
2074
|
+
this.discoveredModelsCache.clear();
|
|
2075
|
+
this.discoveredModelsUpdatedAt.clear();
|
|
2076
|
+
this.discoveryAttemptedAt.clear();
|
|
2077
|
+
this.inflightDiscovery.clear();
|
|
2078
|
+
this.discoveredCapabilityModelsCache.clear();
|
|
2079
|
+
this.discoveredCapabilityModelsUpdatedAt.clear();
|
|
2080
|
+
this.capabilityDiscoveryAttemptedAt.clear();
|
|
2081
|
+
this.inflightCapabilityDiscovery.clear();
|
|
2082
|
+
this.discoveryCacheRevisions.clear();
|
|
2083
|
+
this.capabilityDiscoveryCacheRevisions.clear();
|
|
1526
2084
|
}
|
|
1527
2085
|
}
|
|
1528
2086
|
export default new PluginService();
|