@harperfast/harper 5.2.13 → 5.3.0-beta.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/agent/mcpTools.ts +1 -1
- package/agent/session.ts +25 -14
- package/bin/cliOperations.ts +58 -9
- package/bin/copyDb.ts +282 -59
- package/bin/deploySetup.ts +16 -5
- package/bin/harper.ts +1 -1
- package/bin/help.ts +4 -1
- package/bin/lite.ts +4 -1
- package/bin/restart.ts +120 -4
- package/bin/run.ts +6 -11
- package/bin/upgrade.js +7 -3
- package/bin/workloadIdentity.ts +119 -0
- package/components/Application.ts +3436 -242
- package/components/ApplicationScope.ts +8 -0
- package/components/EntryHandler.ts +59 -39
- package/components/OptionsWatcher.ts +440 -98
- package/components/RuntimeModuleTracker.ts +38 -7
- package/components/Scope.ts +56 -15
- package/components/awaitRestart.ts +84 -0
- package/components/componentLoader.ts +381 -32
- package/components/componentPreparationLock.ts +16 -5
- package/components/deploymentOperations.ts +4 -1
- package/components/deploymentRecorder.ts +9 -2
- package/components/mcp/adapters/harperHttp.ts +4 -0
- package/components/mcp/listChanged.ts +4 -0
- package/components/mcp/toolRegistry.ts +2 -0
- package/components/mcp/tools/operations.ts +9 -0
- package/components/mcp/tools/schemas/operationDescriptions.ts +2 -2
- package/components/operations.js +537 -113
- package/components/operationsValidation.js +98 -3
- package/components/packageComponent.ts +25 -1
- package/components/requestRestart.ts +11 -0
- package/components/status/ComponentStatusRegistry.ts +59 -0
- package/config/RootConfigWatcher.ts +240 -40
- package/config/configReadRetry.ts +62 -0
- package/config/configUtils.ts +357 -48
- package/config/harperConfigEnvVars.ts +170 -27
- package/config/parseConfigFile.ts +34 -0
- package/config/readConfigFileSync.ts +44 -0
- package/config/watcherArming.ts +59 -0
- package/config-root.schema.json +33 -0
- package/dataLayer/blobBackup.ts +160 -50
- package/dataLayer/delete.ts +6 -1
- package/dataLayer/harperBridge/ResourceBridge.ts +80 -10
- package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js +3 -1
- package/dataLayer/hdbInfoController.ts +34 -1
- package/dataLayer/insert.ts +44 -1
- package/dataLayer/rocksdbBackup.ts +53 -10
- package/dataLayer/schema.ts +11 -1
- package/dataLayer/schemaDescribe.ts +8 -1
- package/dist/agent/mcpTools.js +1 -1
- package/dist/agent/mcpTools.js.map +1 -1
- package/dist/agent/session.d.ts +22 -0
- package/dist/agent/session.js +26 -15
- package/dist/agent/session.js.map +1 -1
- package/dist/bin/cliOperations.js +61 -9
- package/dist/bin/cliOperations.js.map +1 -1
- package/dist/bin/copyDb.d.ts +12 -1
- package/dist/bin/copyDb.js +248 -60
- package/dist/bin/copyDb.js.map +1 -1
- package/dist/bin/deploySetup.d.ts +2 -0
- package/dist/bin/deploySetup.js +11 -3
- package/dist/bin/deploySetup.js.map +1 -1
- package/dist/bin/harper.js +1 -1
- package/dist/bin/harper.js.map +1 -1
- package/dist/bin/help.js +4 -1
- package/dist/bin/help.js.map +1 -1
- package/dist/bin/lite.js +4 -1
- package/dist/bin/lite.js.map +1 -1
- package/dist/bin/restart.js +91 -6
- package/dist/bin/restart.js.map +1 -1
- package/dist/bin/run.js +4 -10
- package/dist/bin/run.js.map +1 -1
- package/dist/bin/upgrade.js +4 -3
- package/dist/bin/upgrade.js.map +1 -1
- package/dist/bin/workloadIdentity.d.ts +18 -0
- package/dist/bin/workloadIdentity.js +100 -0
- package/dist/bin/workloadIdentity.js.map +1 -0
- package/dist/components/Application.d.ts +235 -17
- package/dist/components/Application.js +3038 -238
- package/dist/components/Application.js.map +1 -1
- package/dist/components/ApplicationScope.d.ts +8 -0
- package/dist/components/ApplicationScope.js +7 -0
- package/dist/components/ApplicationScope.js.map +1 -1
- package/dist/components/EntryHandler.js +26 -10
- package/dist/components/EntryHandler.js.map +1 -1
- package/dist/components/OptionsWatcher.d.ts +4 -0
- package/dist/components/OptionsWatcher.js +440 -99
- package/dist/components/OptionsWatcher.js.map +1 -1
- package/dist/components/RuntimeModuleTracker.js +40 -6
- package/dist/components/RuntimeModuleTracker.js.map +1 -1
- package/dist/components/Scope.js +52 -13
- package/dist/components/Scope.js.map +1 -1
- package/dist/components/awaitRestart.d.ts +33 -0
- package/dist/components/awaitRestart.js +61 -0
- package/dist/components/awaitRestart.js.map +1 -0
- package/dist/components/componentLoader.d.ts +38 -1
- package/dist/components/componentLoader.js +313 -24
- package/dist/components/componentLoader.js.map +1 -1
- package/dist/components/componentPreparationLock.d.ts +5 -0
- package/dist/components/componentPreparationLock.js +14 -6
- package/dist/components/componentPreparationLock.js.map +1 -1
- package/dist/components/deploymentOperations.js +4 -1
- package/dist/components/deploymentOperations.js.map +1 -1
- package/dist/components/deploymentRecorder.d.ts +4 -2
- package/dist/components/deploymentRecorder.js +1 -0
- package/dist/components/deploymentRecorder.js.map +1 -1
- package/dist/components/mcp/adapters/harperHttp.js +4 -0
- package/dist/components/mcp/adapters/harperHttp.js.map +1 -1
- package/dist/components/mcp/listChanged.js +5 -0
- package/dist/components/mcp/listChanged.js.map +1 -1
- package/dist/components/mcp/toolRegistry.d.ts +1 -0
- package/dist/components/mcp/toolRegistry.js.map +1 -1
- package/dist/components/mcp/tools/operations.d.ts +5 -0
- package/dist/components/mcp/tools/operations.js +9 -0
- package/dist/components/mcp/tools/operations.js.map +1 -1
- package/dist/components/mcp/tools/schemas/operationDescriptions.js +2 -2
- package/dist/components/mcp/tools/schemas/operationDescriptions.js.map +1 -1
- package/dist/components/operations.d.ts +28 -0
- package/dist/components/operations.js +476 -113
- package/dist/components/operations.js.map +1 -1
- package/dist/components/operationsValidation.js +97 -3
- package/dist/components/operationsValidation.js.map +1 -1
- package/dist/components/packageComponent.js +24 -0
- package/dist/components/packageComponent.js.map +1 -1
- package/dist/components/requestRestart.d.ts +1 -0
- package/dist/components/requestRestart.js +7 -0
- package/dist/components/requestRestart.js.map +1 -1
- package/dist/components/status/ComponentStatusRegistry.d.ts +0 -4
- package/dist/components/status/ComponentStatusRegistry.js +63 -0
- package/dist/components/status/ComponentStatusRegistry.js.map +1 -1
- package/dist/config/RootConfigWatcher.d.ts +9 -1
- package/dist/config/RootConfigWatcher.js +228 -37
- package/dist/config/RootConfigWatcher.js.map +1 -1
- package/dist/config/configReadRetry.d.ts +8 -0
- package/dist/config/configReadRetry.js +62 -0
- package/dist/config/configReadRetry.js.map +1 -0
- package/dist/config/configUtils.d.ts +11 -2
- package/dist/config/configUtils.js +304 -47
- package/dist/config/configUtils.js.map +1 -1
- package/dist/config/harperConfigEnvVars.d.ts +16 -0
- package/dist/config/harperConfigEnvVars.js +162 -25
- package/dist/config/harperConfigEnvVars.js.map +1 -1
- package/dist/config/parseConfigFile.d.ts +4 -0
- package/dist/config/parseConfigFile.js +35 -0
- package/dist/config/parseConfigFile.js.map +1 -0
- package/dist/config/readConfigFileSync.d.ts +1 -0
- package/dist/config/readConfigFileSync.js +47 -0
- package/dist/config/readConfigFileSync.js.map +1 -0
- package/dist/config/watcherArming.d.ts +15 -0
- package/dist/config/watcherArming.js +59 -0
- package/dist/config/watcherArming.js.map +1 -0
- package/dist/dataLayer/blobBackup.d.ts +49 -20
- package/dist/dataLayer/blobBackup.js +139 -50
- package/dist/dataLayer/blobBackup.js.map +1 -1
- package/dist/dataLayer/delete.js +1 -1
- package/dist/dataLayer/delete.js.map +1 -1
- package/dist/dataLayer/harperBridge/ResourceBridge.d.ts +14 -1
- package/dist/dataLayer/harperBridge/ResourceBridge.js +72 -12
- package/dist/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
- package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.d.ts +3 -1
- package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js +3 -1
- package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js.map +1 -1
- package/dist/dataLayer/hdbInfoController.d.ts +10 -0
- package/dist/dataLayer/hdbInfoController.js +30 -1
- package/dist/dataLayer/hdbInfoController.js.map +1 -1
- package/dist/dataLayer/insert.d.ts +9 -1
- package/dist/dataLayer/insert.js +30 -0
- package/dist/dataLayer/insert.js.map +1 -1
- package/dist/dataLayer/rocksdbBackup.d.ts +2 -2
- package/dist/dataLayer/rocksdbBackup.js +45 -8
- package/dist/dataLayer/rocksdbBackup.js.map +1 -1
- package/dist/dataLayer/schema.js +8 -0
- package/dist/dataLayer/schema.js.map +1 -1
- package/dist/dataLayer/schemaDescribe.js +8 -1
- package/dist/dataLayer/schemaDescribe.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/json/systemSchema.json +55 -0
- package/dist/resources/DatabaseTransaction.d.ts +77 -0
- package/dist/resources/DatabaseTransaction.js +586 -54
- package/dist/resources/DatabaseTransaction.js.map +1 -1
- package/dist/resources/LMDBTransaction.d.ts +2 -1
- package/dist/resources/LMDBTransaction.js +43 -6
- package/dist/resources/LMDBTransaction.js.map +1 -1
- package/dist/resources/PrimaryRocksDatabase.d.ts +1 -0
- package/dist/resources/PrimaryRocksDatabase.js +49 -5
- package/dist/resources/PrimaryRocksDatabase.js.map +1 -1
- package/dist/resources/RecordEncoder.d.ts +20 -0
- package/dist/resources/RecordEncoder.js +110 -9
- package/dist/resources/RecordEncoder.js.map +1 -1
- package/dist/resources/RequestTarget.d.ts +2 -0
- package/dist/resources/RequestTarget.js.map +1 -1
- package/dist/resources/Resource.js +117 -24
- package/dist/resources/Resource.js.map +1 -1
- package/dist/resources/ResourceInterface.d.ts +28 -1
- package/dist/resources/ResourceInterface.js.map +1 -1
- package/dist/resources/RocksIndexStore.d.ts +6 -1
- package/dist/resources/RocksIndexStore.js +24 -9
- package/dist/resources/RocksIndexStore.js.map +1 -1
- package/dist/resources/RocksTransactionLogStore.d.ts +24 -1
- package/dist/resources/RocksTransactionLogStore.js +160 -49
- package/dist/resources/RocksTransactionLogStore.js.map +1 -1
- package/dist/resources/Table.d.ts +165 -10
- package/dist/resources/Table.js +1805 -303
- package/dist/resources/Table.js.map +1 -1
- package/dist/resources/analytics/write.js +10 -3
- package/dist/resources/analytics/write.js.map +1 -1
- package/dist/resources/auditStore.d.ts +181 -2
- package/dist/resources/auditStore.js +640 -29
- package/dist/resources/auditStore.js.map +1 -1
- package/dist/resources/blob.d.ts +129 -9
- package/dist/resources/blob.js +992 -114
- package/dist/resources/blob.js.map +1 -1
- package/dist/resources/branchDatabase.d.ts +48 -0
- package/dist/resources/branchDatabase.js +892 -0
- package/dist/resources/branchDatabase.js.map +1 -0
- package/dist/resources/crdt.js +50 -12
- package/dist/resources/crdt.js.map +1 -1
- package/dist/resources/dataLoader.js +3 -4
- package/dist/resources/dataLoader.js.map +1 -1
- package/dist/resources/databases.d.ts +157 -14
- package/dist/resources/databases.js +1462 -325
- package/dist/resources/databases.js.map +1 -1
- package/dist/resources/defineTable.d.ts +10 -2
- package/dist/resources/defineTable.js +9 -1
- package/dist/resources/defineTable.js.map +1 -1
- package/dist/resources/derivedIndexRegistry.d.ts +5 -0
- package/dist/resources/derivedIndexRegistry.js +68 -0
- package/dist/resources/derivedIndexRegistry.js.map +1 -0
- package/dist/resources/derivedIndexRuntime.d.ts +215 -0
- package/dist/resources/derivedIndexRuntime.js +2027 -0
- package/dist/resources/derivedIndexRuntime.js.map +1 -0
- package/dist/resources/graphql.d.ts +1 -1
- package/dist/resources/graphql.js +52 -16
- package/dist/resources/graphql.js.map +1 -1
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +103 -9
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +854 -41
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
- package/dist/resources/indexes/hnswDerivedIndex.d.ts +67 -0
- package/dist/resources/indexes/hnswDerivedIndex.js +464 -0
- package/dist/resources/indexes/hnswDerivedIndex.js.map +1 -0
- package/dist/resources/indexes/hnswPlaneBinding.d.ts +65 -0
- package/dist/resources/indexes/hnswPlaneBinding.js +91 -0
- package/dist/resources/indexes/hnswPlaneBinding.js.map +1 -0
- package/dist/resources/longLivedTransactions.d.ts +71 -0
- package/dist/resources/longLivedTransactions.js +358 -0
- package/dist/resources/longLivedTransactions.js.map +1 -0
- package/dist/resources/models/backendRegistry.d.ts +26 -0
- package/dist/resources/models/backendRegistry.js +60 -2
- package/dist/resources/models/backendRegistry.js.map +1 -1
- package/dist/resources/models/bootstrap.d.ts +33 -1
- package/dist/resources/models/bootstrap.js +416 -31
- package/dist/resources/models/bootstrap.js.map +1 -1
- package/dist/resources/nodeIdMapping.d.ts +5 -0
- package/dist/resources/nodeIdMapping.js +49 -0
- package/dist/resources/nodeIdMapping.js.map +1 -1
- package/dist/resources/recordLock.d.ts +123 -0
- package/dist/resources/recordLock.js +315 -0
- package/dist/resources/recordLock.js.map +1 -0
- package/dist/resources/recordLockCoordinator.d.ts +557 -0
- package/dist/resources/recordLockCoordinator.js +2565 -0
- package/dist/resources/recordLockCoordinator.js.map +1 -0
- package/dist/resources/replayLogs.d.ts +14 -1
- package/dist/resources/replayLogs.js +157 -24
- package/dist/resources/replayLogs.js.map +1 -1
- package/dist/resources/replayLogsGuards.d.ts +94 -7
- package/dist/resources/replayLogsGuards.js +111 -7
- package/dist/resources/replayLogsGuards.js.map +1 -1
- package/dist/resources/replicatedApplyFailure.d.ts +16 -0
- package/dist/resources/replicatedApplyFailure.js +63 -0
- package/dist/resources/replicatedApplyFailure.js.map +1 -0
- package/dist/resources/scheduler/scheduler.js +3 -3
- package/dist/resources/scheduler/scheduler.js.map +1 -1
- package/dist/resources/search.d.ts +10 -4
- package/dist/resources/search.js +283 -37
- package/dist/resources/search.js.map +1 -1
- package/dist/resources/tracked.d.ts +5 -1
- package/dist/resources/tracked.js +74 -23
- package/dist/resources/tracked.js.map +1 -1
- package/dist/resources/transactionBroadcast.d.ts +1 -1
- package/dist/resources/transactionBroadcast.js +2 -2
- package/dist/resources/transactionBroadcast.js.map +1 -1
- package/dist/security/auth.js +61 -30
- package/dist/security/auth.js.map +1 -1
- package/dist/security/authn/oidc/claims.d.ts +22 -0
- package/dist/security/authn/oidc/claims.js +71 -0
- package/dist/security/authn/oidc/claims.js.map +1 -0
- package/dist/security/authn/oidc/identityToken.d.ts +27 -0
- package/dist/security/authn/oidc/identityToken.js +111 -0
- package/dist/security/authn/oidc/identityToken.js.map +1 -0
- package/dist/security/authn/oidc/jwks.d.ts +25 -0
- package/dist/security/authn/oidc/jwks.js +261 -0
- package/dist/security/authn/oidc/jwks.js.map +1 -0
- package/dist/security/authn/oidc/providers/generic.d.ts +13 -0
- package/dist/security/authn/oidc/providers/generic.js +34 -0
- package/dist/security/authn/oidc/providers/generic.js.map +1 -0
- package/dist/security/authn/oidc/providers/githubActions.d.ts +11 -0
- package/dist/security/authn/oidc/providers/githubActions.js +129 -0
- package/dist/security/authn/oidc/providers/githubActions.js.map +1 -0
- package/dist/security/authn/oidc/providers/index.d.ts +37 -0
- package/dist/security/authn/oidc/providers/index.js +24 -0
- package/dist/security/authn/oidc/providers/index.js.map +1 -0
- package/dist/security/authn/oidc/tokenExchange.d.ts +12 -0
- package/dist/security/authn/oidc/tokenExchange.js +306 -0
- package/dist/security/authn/oidc/tokenExchange.js.map +1 -0
- package/dist/security/authn/oidc/trustPolicyOperations.d.ts +49 -0
- package/dist/security/authn/oidc/trustPolicyOperations.js +358 -0
- package/dist/security/authn/oidc/trustPolicyOperations.js.map +1 -0
- package/dist/security/authn/oidc/types.d.ts +38 -0
- package/dist/security/authn/oidc/types.js +6 -0
- package/dist/security/authn/oidc/types.js.map +1 -0
- package/dist/security/certificateVerification/index.js +40 -11
- package/dist/security/certificateVerification/index.js.map +1 -1
- package/dist/security/certificateVerification/trustedIssuers.d.ts +24 -0
- package/dist/security/certificateVerification/trustedIssuers.js +79 -0
- package/dist/security/certificateVerification/trustedIssuers.js.map +1 -0
- package/dist/security/certificateVerification/types.d.ts +1 -0
- package/dist/security/credentialProvenance.d.ts +35 -0
- package/dist/security/credentialProvenance.js +51 -0
- package/dist/security/credentialProvenance.js.map +1 -0
- package/dist/security/credentialRejection.d.ts +4 -0
- package/dist/security/credentialRejection.js +24 -0
- package/dist/security/credentialRejection.js.map +1 -0
- package/dist/security/deferredAuthentication.d.ts +36 -0
- package/dist/security/deferredAuthentication.js +70 -0
- package/dist/security/deferredAuthentication.js.map +1 -0
- package/dist/security/impersonation.d.ts +21 -0
- package/dist/security/impersonation.js +108 -9
- package/dist/security/impersonation.js.map +1 -1
- package/dist/security/jsLoader.d.ts +6 -0
- package/dist/security/jsLoader.js +77 -15
- package/dist/security/jsLoader.js.map +1 -1
- package/dist/security/keys.js +301 -71
- package/dist/security/keys.js.map +1 -1
- package/dist/security/operationScope.d.ts +21 -0
- package/dist/security/operationScope.js +36 -0
- package/dist/security/operationScope.js.map +1 -0
- package/dist/security/permissionsTranslator.js +21 -0
- package/dist/security/permissionsTranslator.js.map +1 -1
- package/dist/security/tokenAuthentication.d.ts +19 -1
- package/dist/security/tokenAuthentication.js +191 -10
- package/dist/security/tokenAuthentication.js.map +1 -1
- package/dist/security/user.js +4 -3
- package/dist/security/user.js.map +1 -1
- package/dist/server/DurableSubscriptionsSession.d.ts +2 -2
- package/dist/server/DurableSubscriptionsSession.js +67 -10
- package/dist/server/DurableSubscriptionsSession.js.map +1 -1
- package/dist/server/REST.js +106 -2
- package/dist/server/REST.js.map +1 -1
- package/dist/server/graphqlQuerying.js +4 -0
- package/dist/server/graphqlQuerying.js.map +1 -1
- package/dist/server/http.d.ts +16 -1
- package/dist/server/http.js +122 -17
- package/dist/server/http.js.map +1 -1
- package/dist/server/itc/serverHandlers.js +8 -1
- package/dist/server/itc/serverHandlers.js.map +1 -1
- package/dist/server/jobs/jobProcess.js +6 -2
- package/dist/server/jobs/jobProcess.js.map +1 -1
- package/dist/server/jobs/jobs.js +4 -1
- package/dist/server/jobs/jobs.js.map +1 -1
- package/dist/server/liveSubscriptionAuth.d.ts +26 -4
- package/dist/server/liveSubscriptionAuth.js +105 -39
- package/dist/server/liveSubscriptionAuth.js.map +1 -1
- package/dist/server/loadRootComponents.js +49 -10
- package/dist/server/loadRootComponents.js.map +1 -1
- package/dist/server/mqtt.d.ts +2 -0
- package/dist/server/mqtt.js +165 -30
- package/dist/server/mqtt.js.map +1 -1
- package/dist/server/nodeName.d.ts +2 -0
- package/dist/server/nodeName.js +107 -23
- package/dist/server/nodeName.js.map +1 -1
- package/dist/server/serverHelpers/Headers.d.ts +27 -0
- package/dist/server/serverHelpers/Headers.js +145 -1
- package/dist/server/serverHelpers/Headers.js.map +1 -1
- package/dist/server/serverHelpers/NodeAdapterResponse.d.ts +48 -0
- package/dist/server/serverHelpers/NodeAdapterResponse.js +220 -0
- package/dist/server/serverHelpers/NodeAdapterResponse.js.map +1 -0
- package/dist/server/serverHelpers/Request.d.ts +5 -10
- package/dist/server/serverHelpers/Request.js +38 -136
- package/dist/server/serverHelpers/Request.js.map +1 -1
- package/dist/server/serverHelpers/contentTypes.d.ts +11 -0
- package/dist/server/serverHelpers/contentTypes.js +221 -40
- package/dist/server/serverHelpers/contentTypes.js.map +1 -1
- package/dist/server/serverHelpers/deployValidationState.d.ts +3 -0
- package/dist/server/serverHelpers/deployValidationState.js +9 -19
- package/dist/server/serverHelpers/deployValidationState.js.map +1 -1
- package/dist/server/serverHelpers/operationAuthorizationState.d.ts +12 -0
- package/dist/server/serverHelpers/operationAuthorizationState.js +24 -2
- package/dist/server/serverHelpers/operationAuthorizationState.js.map +1 -1
- package/dist/server/serverHelpers/registeredOperations.d.ts +5 -4
- package/dist/server/serverHelpers/registeredOperations.js +74 -21
- package/dist/server/serverHelpers/registeredOperations.js.map +1 -1
- package/dist/server/serverHelpers/requestSanitization.d.ts +11 -0
- package/dist/server/serverHelpers/requestSanitization.js +20 -0
- package/dist/server/serverHelpers/requestSanitization.js.map +1 -0
- package/dist/server/serverHelpers/serverHandlers.js +6 -3
- package/dist/server/serverHelpers/serverHandlers.js.map +1 -1
- package/dist/server/serverHelpers/serverUtilities.d.ts +18 -0
- package/dist/server/serverHelpers/serverUtilities.js +178 -29
- package/dist/server/serverHelpers/serverUtilities.js.map +1 -1
- package/dist/server/serverHelpers/sharedMessageEncoding.d.ts +67 -0
- package/dist/server/serverHelpers/sharedMessageEncoding.js +280 -0
- package/dist/server/serverHelpers/sharedMessageEncoding.js.map +1 -0
- package/dist/server/serverHelpers/uwsServer.js +19 -1
- package/dist/server/serverHelpers/uwsServer.js.map +1 -1
- package/dist/server/static.js +24 -28
- package/dist/server/static.js.map +1 -1
- package/dist/server/storageReclamation.d.ts +5 -0
- package/dist/server/storageReclamation.js +17 -1
- package/dist/server/storageReclamation.js.map +1 -1
- package/dist/server/threads/isolatedApplications.d.ts +47 -0
- package/dist/server/threads/isolatedApplications.js +171 -0
- package/dist/server/threads/isolatedApplications.js.map +1 -0
- package/dist/server/threads/itc.d.ts +7 -2
- package/dist/server/threads/itc.js +5 -1
- package/dist/server/threads/itc.js.map +1 -1
- package/dist/server/threads/logRotationTransport.d.ts +1 -0
- package/dist/server/threads/logRotationTransport.js +33 -0
- package/dist/server/threads/logRotationTransport.js.map +1 -0
- package/dist/server/threads/manageThreads.d.ts +83 -3
- package/dist/server/threads/manageThreads.js +769 -75
- package/dist/server/threads/manageThreads.js.map +1 -1
- package/dist/server/threads/socketRouter.d.ts +1 -0
- package/dist/server/threads/socketRouter.js +277 -31
- package/dist/server/threads/socketRouter.js.map +1 -1
- package/dist/server/threads/threadHeapMemory.d.ts +2 -0
- package/dist/server/threads/threadHeapMemory.js +31 -0
- package/dist/server/threads/threadHeapMemory.js.map +1 -0
- package/dist/server/threads/threadServer.js +76 -22
- package/dist/server/threads/threadServer.js.map +1 -1
- package/dist/sqlEngine/config.d.ts +1 -3
- package/dist/sqlEngine/config.js +19 -16
- package/dist/sqlEngine/config.js.map +1 -1
- package/dist/sqlTranslator/index.d.ts +1 -1
- package/dist/sqlTranslator/index.js +30 -7
- package/dist/sqlTranslator/index.js.map +1 -1
- package/dist/upgrade/directives/5-3-0.d.ts +7 -0
- package/dist/upgrade/directives/5-3-0.js +148 -0
- package/dist/upgrade/directives/5-3-0.js.map +1 -0
- package/dist/upgrade/directives/directivesController.js +2 -1
- package/dist/upgrade/directives/directivesController.js.map +1 -1
- package/dist/utility/OperationFunctionCaller.js +2 -1
- package/dist/utility/OperationFunctionCaller.js.map +1 -1
- package/dist/utility/common_utils.d.ts +16 -0
- package/dist/utility/common_utils.js +32 -6
- package/dist/utility/common_utils.js.map +1 -1
- package/dist/utility/componentNames.d.ts +8 -0
- package/dist/utility/componentNames.js +12 -1
- package/dist/utility/componentNames.js.map +1 -1
- package/dist/utility/environment/environmentManager.js +3 -6
- package/dist/utility/environment/environmentManager.js.map +1 -1
- package/dist/utility/environment/systemInformation.d.ts +1 -0
- package/dist/utility/environment/systemInformation.js +1 -0
- package/dist/utility/environment/systemInformation.js.map +1 -1
- package/dist/utility/errors/commonErrors.d.ts +2 -0
- package/dist/utility/errors/commonErrors.js +2 -0
- package/dist/utility/errors/commonErrors.js.map +1 -1
- package/dist/utility/errors/hdbError.d.ts +33 -0
- package/dist/utility/errors/hdbError.js +58 -1
- package/dist/utility/errors/hdbError.js.map +1 -1
- package/dist/utility/globalSchema.d.ts +18 -0
- package/dist/utility/hdbTerms.d.ts +18 -0
- package/dist/utility/hdbTerms.js +20 -2
- package/dist/utility/hdbTerms.js.map +1 -1
- package/dist/utility/logging/harper_logger.d.ts +2 -0
- package/dist/utility/logging/harper_logger.js +286 -30
- package/dist/utility/logging/harper_logger.js.map +1 -1
- package/dist/utility/logging/logGenerationCoordinator.d.ts +35 -0
- package/dist/utility/logging/logGenerationCoordinator.js +184 -0
- package/dist/utility/logging/logGenerationCoordinator.js.map +1 -0
- package/dist/utility/logging/logRotation.d.ts +46 -0
- package/dist/utility/logging/logRotation.js +365 -0
- package/dist/utility/logging/logRotation.js.map +1 -0
- package/dist/utility/logging/logRotator.d.ts +1 -1
- package/dist/utility/logging/logRotator.js +192 -85
- package/dist/utility/logging/logRotator.js.map +1 -1
- package/dist/utility/nodeIdentity.d.ts +9 -0
- package/dist/utility/nodeIdentity.js +58 -0
- package/dist/utility/nodeIdentity.js.map +1 -0
- package/dist/utility/npmUtilities.js +11 -7
- package/dist/utility/npmUtilities.js.map +1 -1
- package/dist/utility/operationPermissions.d.ts +3 -1
- package/dist/utility/operationPermissions.js +16 -1
- package/dist/utility/operationPermissions.js.map +1 -1
- package/dist/utility/operation_authorization.d.ts +10 -7
- package/dist/utility/operation_authorization.js +212 -41
- package/dist/utility/operation_authorization.js.map +1 -1
- package/dist/utility/watchPath.d.ts +29 -0
- package/dist/utility/watchPath.js +68 -0
- package/dist/utility/watchPath.js.map +1 -0
- package/dist/utility/watcherFallback.d.ts +41 -0
- package/dist/utility/watcherFallback.js +153 -0
- package/dist/utility/watcherFallback.js.map +1 -1
- package/dist/validation/configValidator.d.ts +12 -0
- package/dist/validation/configValidator.js +205 -75
- package/dist/validation/configValidator.js.map +1 -1
- package/dist/validation/installValidator.js +12 -0
- package/dist/validation/installValidator.js.map +1 -1
- package/dist/validation/validationWrapper.d.ts +11 -0
- package/dist/validation/validationWrapper.js +16 -3
- package/dist/validation/validationWrapper.js.map +1 -1
- package/index.ts +8 -0
- package/json/systemSchema.json +55 -0
- package/npm-shrinkwrap.json +286 -194
- package/package.json +15 -7
- package/resources/DESIGN.md +219 -52
- package/resources/DatabaseTransaction.ts +657 -52
- package/resources/LMDBTransaction.ts +46 -6
- package/resources/PrimaryRocksDatabase.ts +46 -7
- package/resources/RecordEncoder.ts +124 -9
- package/resources/RequestTarget.ts +2 -0
- package/resources/Resource.ts +114 -22
- package/resources/ResourceInterface.ts +31 -0
- package/resources/RocksIndexStore.ts +30 -9
- package/resources/RocksTransactionLogStore.ts +188 -51
- package/resources/Table.ts +1960 -301
- package/resources/analytics/write.ts +10 -3
- package/resources/auditStore.ts +647 -32
- package/resources/blob.ts +1029 -110
- package/resources/branchDatabase.ts +941 -0
- package/resources/crdt.ts +76 -12
- package/resources/dataLoader.ts +3 -4
- package/resources/databases.ts +1584 -320
- package/resources/defineTable.ts +18 -2
- package/resources/derivedIndexRegistry.ts +56 -0
- package/resources/derivedIndexRuntime.ts +2292 -0
- package/resources/graphql.ts +72 -17
- package/resources/indexes/HierarchicalNavigableSmallWorld.ts +911 -48
- package/resources/indexes/hnswDerivedIndex.ts +531 -0
- package/resources/indexes/hnswPlaneBinding.ts +174 -0
- package/resources/longLivedTransactions.ts +360 -0
- package/resources/models/backendRegistry.ts +84 -2
- package/resources/models/bootstrap.ts +473 -28
- package/resources/nodeIdMapping.ts +50 -0
- package/resources/recordLock.ts +419 -0
- package/resources/recordLockCoordinator.ts +3043 -0
- package/resources/replayLogs.ts +152 -26
- package/resources/replayLogsGuards.ts +171 -8
- package/resources/replicatedApplyFailure.ts +77 -0
- package/resources/scheduler/scheduler.ts +4 -4
- package/resources/search.ts +288 -46
- package/resources/tracked.ts +73 -22
- package/resources/transactionBroadcast.ts +3 -3
- package/security/auth.ts +68 -29
- package/security/authn/oidc/claims.ts +72 -0
- package/security/authn/oidc/identityToken.ts +129 -0
- package/security/authn/oidc/jwks.ts +260 -0
- package/security/authn/oidc/providers/generic.ts +40 -0
- package/security/authn/oidc/providers/githubActions.ts +137 -0
- package/security/authn/oidc/providers/index.ts +52 -0
- package/security/authn/oidc/tokenExchange.ts +300 -0
- package/security/authn/oidc/trustPolicyOperations.ts +343 -0
- package/security/authn/oidc/types.ts +41 -0
- package/security/certificateVerification/index.ts +54 -13
- package/security/certificateVerification/trustedIssuers.ts +76 -0
- package/security/certificateVerification/types.ts +1 -0
- package/security/credentialProvenance.ts +47 -0
- package/security/credentialRejection.ts +22 -0
- package/security/deferredAuthentication.ts +71 -0
- package/security/impersonation.ts +117 -12
- package/security/jsLoader.ts +83 -18
- package/security/keys.ts +298 -72
- package/security/operationScope.ts +33 -0
- package/security/permissionsTranslator.js +23 -0
- package/security/tokenAuthentication.ts +233 -12
- package/security/user.ts +4 -3
- package/server/DESIGN.md +194 -16
- package/server/DurableSubscriptionsSession.ts +71 -11
- package/server/REST.ts +115 -4
- package/server/graphqlQuerying.ts +4 -0
- package/server/http.ts +133 -20
- package/server/itc/serverHandlers.js +8 -1
- package/server/jobs/jobProcess.ts +8 -2
- package/server/jobs/jobs.ts +4 -1
- package/server/liveSubscriptionAuth.ts +129 -46
- package/server/loadRootComponents.js +50 -8
- package/server/mqtt.ts +179 -38
- package/server/nodeName.ts +103 -21
- package/server/serverHelpers/Headers.ts +135 -0
- package/server/serverHelpers/NodeAdapterResponse.ts +221 -0
- package/server/serverHelpers/Request.ts +33 -131
- package/server/serverHelpers/contentTypes.ts +217 -36
- package/server/serverHelpers/deployValidationState.ts +24 -13
- package/server/serverHelpers/operationAuthorizationState.ts +34 -3
- package/server/serverHelpers/registeredOperations.ts +79 -22
- package/server/serverHelpers/requestSanitization.ts +15 -0
- package/server/serverHelpers/serverHandlers.js +6 -3
- package/server/serverHelpers/serverUtilities.ts +232 -40
- package/server/serverHelpers/sharedMessageEncoding.ts +307 -0
- package/server/serverHelpers/uwsServer.ts +17 -2
- package/server/static.ts +23 -29
- package/server/storageReclamation.ts +15 -2
- package/server/threads/isolatedApplications.ts +157 -0
- package/server/threads/itc.js +11 -1
- package/server/threads/logRotationTransport.ts +40 -0
- package/server/threads/manageThreads.js +771 -66
- package/server/threads/socketRouter.ts +291 -30
- package/server/threads/threadHeapMemory.ts +26 -0
- package/server/threads/threadServer.js +73 -22
- package/sqlTranslator/index.ts +31 -8
- package/studio/web/assets/{Chat-4RrB5134.js → Chat-D3j-1yY1.js} +1 -1
- package/studio/web/assets/{FloatingChat-omlNMDcJ.js → FloatingChat-BxJGYcfB.js} +3 -3
- package/studio/web/assets/{apiToken-Bke3wvfZ.js → apiToken-CT55oWOe.js} +1 -1
- package/studio/web/assets/{applications-DvFDYJgK.js → applications-D9Ct9_vm.js} +1 -1
- package/studio/web/assets/{cssMode-C1vRa7zh.js → cssMode-DV8H7VwA.js} +1 -1
- package/studio/web/assets/{editor-3XRWEDWX.js → editor-uatc0unt.js} +1 -1
- package/studio/web/assets/{html-CTY5tdMr.js → html-Bm6D6paN.js} +1 -1
- package/studio/web/assets/{htmlMode-CG1vSD9t.js → htmlMode-CEn7tpLG.js} +1 -1
- package/studio/web/assets/{index-Dfpeofdu.js → index-BIXW6Pu4.js} +5 -5
- package/studio/web/assets/{index.lazy-B1VOIv-t.js → index.lazy-UI7L-Vrk.js} +1 -1
- package/studio/web/assets/{javascript-CJeJzGnI.js → javascript-CJ0G3AFZ.js} +1 -1
- package/studio/web/assets/{jsonMode-CYPBwM82.js → jsonMode-DQADAYEa.js} +1 -1
- package/studio/web/assets/{languageServices-IKH4GUHl.js → languageServices-CAQJXWcI.js} +1 -1
- package/studio/web/assets/{lspLanguageFeatures-BC8_gxKG.js → lspLanguageFeatures-CCQ8P5sY.js} +1 -1
- package/studio/web/assets/{notifications-BfKBpYcq.js → notifications-BbxTU6Aw.js} +1 -1
- package/studio/web/assets/{notifications-DFbArTfC.js → notifications-Cvb3P1lB.js} +1 -1
- package/studio/web/assets/{profile-Q4-T6c-S.js → profile-Yyb7gsvL.js} +1 -1
- package/studio/web/assets/{regions-CtkV0xje.js → regions-OgjGHlU5.js} +1 -1
- package/studio/web/assets/{register-1ZZuMsiA.js → register-6qwNEOY3.js} +2 -2
- package/studio/web/assets/{setComponentFile-ZWLcWv5X.js → setComponentFile-BilDMtgB.js} +1 -1
- package/studio/web/assets/{setup-DWprJyJy.js → setup-J6qJ7OIU.js} +2 -2
- package/studio/web/assets/{status--aNm8isn.js → status-0RWGcfyD.js} +1 -1
- package/studio/web/assets/{toggleHighContrast-Cq_lt3XD.js → toggleHighContrast-BIn-vErT.js} +1 -1
- package/studio/web/assets/{tsMode-pjgytARx.js → tsMode-DgUXku4d.js} +1 -1
- package/studio/web/assets/{typescript-Cdg0mqUh.js → typescript-C9orXcsM.js} +1 -1
- package/studio/web/assets/{useEntityRestURL-RZhaY8Rn.js → useEntityRestURL-BEoXXbUB.js} +1 -1
- package/studio/web/assets/{workers-BgoXIqQe.js → workers-JVzSDmgx.js} +1 -1
- package/studio/web/assets/{xml-BwSeDMiP.js → xml-Cq-S8S4X.js} +1 -1
- package/studio/web/assets/{yaml-DotCUG5l.js → yaml-sfoRdh1M.js} +1 -1
- package/studio/web/index.html +1 -1
- package/upgrade/directives/5-3-0.ts +132 -0
- package/upgrade/directives/directivesController.ts +2 -1
- package/utility/OperationFunctionCaller.ts +2 -1
- package/utility/common_utils.ts +30 -5
- package/utility/componentNames.ts +12 -0
- package/utility/environment/environmentManager.ts +3 -7
- package/utility/environment/systemInformation.ts +7 -0
- package/utility/errors/commonErrors.ts +4 -0
- package/utility/errors/hdbError.ts +57 -0
- package/utility/hdbTerms.ts +19 -0
- package/utility/logging/harper_logger.ts +278 -26
- package/utility/logging/logGenerationCoordinator.ts +196 -0
- package/utility/logging/logRotation.ts +367 -0
- package/utility/logging/logRotator.ts +213 -81
- package/utility/nodeIdentity.ts +45 -0
- package/utility/npmUtilities.ts +12 -8
- package/utility/operationPermissions.ts +18 -1
- package/utility/operation_authorization.ts +231 -42
- package/utility/watchPath.ts +63 -0
- package/utility/watcherFallback.ts +148 -0
- package/validation/configValidator.ts +215 -75
- package/validation/installValidator.ts +15 -0
- package/validation/validationWrapper.ts +18 -4
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* YAML→registry boot bridge (#629 / #630 of #510)
|
|
2
|
+
* YAML→registry boot bridge (#629 / #630 of #510), plus hot reload of the
|
|
3
|
+
* `models` block when the root config file changes (#2344) — so an
|
|
4
|
+
* orchestrator can rotate a credential or re-point a baseUrl by rewriting
|
|
5
|
+
* harperdb-config.yaml, without a process restart.
|
|
3
6
|
*
|
|
4
7
|
* Reads the top-level `models` block from the root config and dispatches each
|
|
5
8
|
* `models.embedding.<name>` / `models.generative.<name>` entry to the matching
|
|
@@ -22,7 +25,8 @@
|
|
|
22
25
|
* convention from `@harperfast/oauth`'s config loader.
|
|
23
26
|
*
|
|
24
27
|
* Errors per entry are logged and skipped, not thrown — one misconfigured
|
|
25
|
-
* backend should not block Harper boot
|
|
28
|
+
* backend should not block Harper boot, and one bad entry in a rewritten file
|
|
29
|
+
* should not tear down the entries that were fine.
|
|
26
30
|
*/
|
|
27
31
|
import harperLogger from '../../utility/logging/harper_logger.ts';
|
|
28
32
|
import { expandEnvVarsDeep, isUnresolvedEnvVarPlaceholder } from '../../utility/expandEnvVar.ts';
|
|
@@ -35,6 +39,16 @@ import { pathToFileURL } from 'node:url';
|
|
|
35
39
|
import { createRequire } from 'node:module';
|
|
36
40
|
import { getHdbBasePath } from '../../utility/environment/environmentManager.ts';
|
|
37
41
|
import { setFallbackGroup, clearFallbackGroups } from './routing.ts';
|
|
42
|
+
import {
|
|
43
|
+
constructBackend,
|
|
44
|
+
getBackend,
|
|
45
|
+
removeIfCurrent,
|
|
46
|
+
replaceIfCurrent,
|
|
47
|
+
type CapturedInstall,
|
|
48
|
+
} from './backendRegistry.ts';
|
|
49
|
+
import { getSharedRootConfigWatcher, RootConfigWatcher } from '../../config/RootConfigWatcher.ts';
|
|
50
|
+
import { validateModelsBlock } from '../../validation/configValidator.ts';
|
|
51
|
+
import type { ModelBackend } from './types.ts';
|
|
38
52
|
|
|
39
53
|
/**
|
|
40
54
|
* Field names treated as credentials. When present in config as a literal
|
|
@@ -79,36 +93,149 @@ const FACTORIES: Record<string, BackendRegisterFn> = {
|
|
|
79
93
|
bedrock: (args) => registerBedrockBackend({ ...args, config: args.config as BedrockBackendConfig }),
|
|
80
94
|
};
|
|
81
95
|
|
|
96
|
+
// What the config projection currently has installed, per kind+logicalName slot. `backend` is the
|
|
97
|
+
// exact instance this module put in the registry: reload swaps compare against it, so a
|
|
98
|
+
// programmatic `registerBackend` that overrode a config entry keeps its documented precedence, and
|
|
99
|
+
// removal can never delete a slot another writer has since taken over.
|
|
100
|
+
interface InstalledSlot {
|
|
101
|
+
kind: ModelKind;
|
|
102
|
+
logicalName: string;
|
|
103
|
+
/** The instance this projection installed; absent while an application override owns the slot. */
|
|
104
|
+
backend?: ModelBackend;
|
|
105
|
+
/** Raw entry as applied, for change detection — an unchanged entry is not reconstructed. */
|
|
106
|
+
entryJson: string;
|
|
107
|
+
/** The entry's fallback group as applied, so a retained backend keeps its routing. */
|
|
108
|
+
fallback?: string[];
|
|
109
|
+
/** Whether the entry's backend is a built-in. Module-backed entries are restart-managed: reload
|
|
110
|
+
* refuses to add, change, OR remove them, so a rename cannot half-apply as a bare removal. */
|
|
111
|
+
builtin?: boolean;
|
|
112
|
+
/** Helper registrations the entry's factory made, installed and removed with the entry.
|
|
113
|
+
* `suppressed`: a config entry claimed this helper's name; the record is kept so the helper is
|
|
114
|
+
* restored when that entry is removed — matching a restart with the final config. */
|
|
115
|
+
extras?: Array<CapturedInstall & { suppressed?: boolean }>;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const installedSlots = new Map<string, InstalledSlot>();
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Let a config entry claim a name held by a projection-installed helper. The record is suppressed
|
|
122
|
+
* rather than deleted, so removing the claiming entry later restores the helper — the live
|
|
123
|
+
* registry then matches a restart with the same final config.
|
|
124
|
+
*/
|
|
125
|
+
function claimHelperOccupant(kind: ModelKind, logicalName: string): ModelBackend | undefined {
|
|
126
|
+
for (const slot of installedSlots.values()) {
|
|
127
|
+
const extra = slot.extras?.find((e) => !e.suppressed && e.kind === kind && e.logicalName === logicalName);
|
|
128
|
+
if (extra) {
|
|
129
|
+
extra.suppressed = true;
|
|
130
|
+
return extra.backend;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Restore a suppressed helper once the entry that claimed its name is removed. */
|
|
137
|
+
function restoreSuppressedHelper(kind: ModelKind, logicalName: string): void {
|
|
138
|
+
for (const slot of installedSlots.values()) {
|
|
139
|
+
const extra = slot.extras?.find((e) => e.suppressed && e.kind === kind && e.logicalName === logicalName);
|
|
140
|
+
if (extra && replaceIfCurrent(extra.kind, extra.logicalName, undefined, extra.backend)) {
|
|
141
|
+
extra.suppressed = false;
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const slotKey = (kind: ModelKind, logicalName: string) => `${kind} ${logicalName}`;
|
|
148
|
+
|
|
82
149
|
/**
|
|
83
150
|
* Populate the model registry from `rootConfig.models`. No-op if the block
|
|
84
151
|
* is absent or empty. Idempotent within a process: each entry overwrites any
|
|
85
|
-
* prior registration under the same logical name
|
|
152
|
+
* prior registration under the same logical name.
|
|
86
153
|
*/
|
|
87
154
|
export async function bootstrapModels(rootConfig: RootConfig | undefined | null): Promise<void> {
|
|
88
|
-
//
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (!block) return;
|
|
93
|
-
await registerKind('embedding', block.embedding);
|
|
94
|
-
await registerKind('generative', block.generative);
|
|
155
|
+
// The projection is NOT reset here: boot rides the same serialized queue as a reload, so a
|
|
156
|
+
// re-bootstrap in a long-lived process removes entries the new config dropped, keeps retained
|
|
157
|
+
// state (a malformed entry's routing), and cannot corrupt an in-flight apply's swaps.
|
|
158
|
+
return queueModelsApply(rootConfig?.models, true);
|
|
95
159
|
}
|
|
96
160
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
161
|
+
/** Forget everything the projection installed. Test isolation only — never part of a reload. */
|
|
162
|
+
export function resetModelsProjection(): void {
|
|
163
|
+
installedSlots.clear();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Apply a `models:` block as a hot reload: changed entries are rebuilt through the same factories
|
|
168
|
+
* boot uses and swapped in atomically, removed entries stop serving, and programmatic
|
|
169
|
+
* registrations that overrode a config entry are left in place. Serialized and coalesced —
|
|
170
|
+
* concurrent calls settle on the latest block.
|
|
171
|
+
*/
|
|
172
|
+
export function applyModelsConfig(models: ModelsConfig | undefined): Promise<void> {
|
|
173
|
+
return queueModelsApply(models, false);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Interleaved applies could install an older credential last; serialized + coalesced, they cannot.
|
|
177
|
+
// Boot and reload coalesce SEPARATELY: merging them either loses boot's overwrite contract or
|
|
178
|
+
// launders a raw watcher block through boot semantics, past reload validation and the missing-key
|
|
179
|
+
// no-op. Boot drains first; the newest reload then refines it under its own rules.
|
|
180
|
+
let pendingBoot: { block: ModelsConfig | undefined } | undefined;
|
|
181
|
+
let pendingReload: { block: ModelsConfig | null | undefined } | undefined;
|
|
182
|
+
let applyChain: Promise<void> | undefined;
|
|
183
|
+
|
|
184
|
+
function queueModelsApply(block: ModelsConfig | null | undefined, isBoot: boolean): Promise<void> {
|
|
185
|
+
if (isBoot) {
|
|
186
|
+
pendingBoot = { block: block ?? undefined };
|
|
187
|
+
// A boot supersedes any reload queued before it — draining that older snapshot after the
|
|
188
|
+
// newer boot would refine it backwards. A reload queued after the boot still refines. The
|
|
189
|
+
// same applies one layer down: a watcher snapshot OBSERVED before this boot whose settle
|
|
190
|
+
// timer has not fired yet is also pre-boot content — boot read the same file, so dropping
|
|
191
|
+
// the timer loses nothing newer.
|
|
192
|
+
pendingReload = undefined;
|
|
193
|
+
clearTimeout(pendingSettle);
|
|
194
|
+
pendingSettle = undefined;
|
|
195
|
+
} else {
|
|
196
|
+
pendingReload = { block };
|
|
106
197
|
}
|
|
198
|
+
applyChain ??= (async () => {
|
|
199
|
+
try {
|
|
200
|
+
while (pendingBoot || pendingReload) {
|
|
201
|
+
const isBootTurn = pendingBoot !== undefined;
|
|
202
|
+
const next = isBootTurn ? pendingBoot! : pendingReload!;
|
|
203
|
+
if (isBootTurn) pendingBoot = undefined;
|
|
204
|
+
else pendingReload = undefined;
|
|
205
|
+
try {
|
|
206
|
+
await applyModels(next.block, isBootTurn);
|
|
207
|
+
} catch (err) {
|
|
208
|
+
// Per-entry failures are handled inside applyModels; this catches a failure of the apply
|
|
209
|
+
// itself. The chain must not reject — the watcher calls fire-and-forget, so a rejection
|
|
210
|
+
// would be unhandled — and the loop continues so a newer pending snapshot still applies.
|
|
211
|
+
harperLogger.error(`models: config apply failed (${(err as Error)?.message ?? err})`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
} finally {
|
|
215
|
+
applyChain = undefined;
|
|
216
|
+
}
|
|
217
|
+
})();
|
|
218
|
+
return applyChain;
|
|
107
219
|
}
|
|
108
220
|
|
|
109
|
-
|
|
221
|
+
interface DesiredEntry {
|
|
222
|
+
kind: ModelKind;
|
|
223
|
+
logicalName: string;
|
|
224
|
+
entry: ModelEntry;
|
|
225
|
+
entryJson: string;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function collectKind(
|
|
229
|
+
kind: ModelKind,
|
|
230
|
+
entries: Record<string, ModelEntry> | undefined,
|
|
231
|
+
out: DesiredEntry[],
|
|
232
|
+
present: Set<string>
|
|
233
|
+
): void {
|
|
110
234
|
if (!entries) return;
|
|
111
235
|
for (const [logicalName, entry] of Object.entries(entries)) {
|
|
236
|
+
// Present even when invalid: a malformed entry keeps its previously applied backend (like a
|
|
237
|
+
// failed rebuild does) rather than reading as removed. Only true absence removes.
|
|
238
|
+
present.add(slotKey(kind, logicalName));
|
|
112
239
|
if (!entry || typeof entry !== 'object') {
|
|
113
240
|
// Schema validation (configValidator.ts) catches this before bootstrap
|
|
114
241
|
// runs, so reaching here means config was loaded by an unusual path
|
|
@@ -120,6 +247,125 @@ async function registerKind(kind: ModelKind, entries: Record<string, ModelEntry>
|
|
|
120
247
|
harperLogger.error(`models.${kind}.${logicalName}: 'backend' must be a non-empty string; skipping`);
|
|
121
248
|
continue;
|
|
122
249
|
}
|
|
250
|
+
out.push({ kind, logicalName, entry, entryJson: JSON.stringify(entry) });
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function publishEntry(
|
|
255
|
+
desiredEntry: DesiredEntry,
|
|
256
|
+
backend: ModelBackend,
|
|
257
|
+
extras: CapturedInstall[],
|
|
258
|
+
isBoot: boolean
|
|
259
|
+
): void {
|
|
260
|
+
const { kind, logicalName, entry, entryJson } = desiredEntry;
|
|
261
|
+
const key = slotKey(kind, logicalName);
|
|
262
|
+
// Boot overwrites occupants (the documented contract); a reload replaces only what this
|
|
263
|
+
// projection installed — or a helper the projection installed under this name, which a config
|
|
264
|
+
// entry outranks — so genuine application overrides survive.
|
|
265
|
+
const previous = installedSlots.get(key);
|
|
266
|
+
let expected = isBoot ? getBackend(kind, logicalName) : previous?.backend;
|
|
267
|
+
// Claimed at boot too, or the parent record keeps an installable claim on a name it lost.
|
|
268
|
+
const claimedHelper = claimHelperOccupant(kind, logicalName);
|
|
269
|
+
if (!isBoot && expected === undefined && claimedHelper !== undefined) expected = claimedHelper;
|
|
270
|
+
// Helpers rotate and retire with their entry even when the primary swap is lost to an
|
|
271
|
+
// override — a frozen helper would keep serving a revoked credential.
|
|
272
|
+
const installedExtras: Array<CapturedInstall & { suppressed?: boolean }> = [];
|
|
273
|
+
for (const extra of extras) {
|
|
274
|
+
const extraExpected = isBoot
|
|
275
|
+
? getBackend(extra.kind, extra.logicalName)
|
|
276
|
+
: previous?.extras?.find((e) => e.kind === extra.kind && e.logicalName === extra.logicalName)?.backend;
|
|
277
|
+
if (replaceIfCurrent(extra.kind, extra.logicalName, extraExpected, extra.backend)) {
|
|
278
|
+
installedExtras.push(extra);
|
|
279
|
+
} else {
|
|
280
|
+
harperLogger.warn(
|
|
281
|
+
`models.${kind}.${logicalName}: helper '${extra.logicalName}' is owned by another registration; leaving it in place`
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
for (const old of previous?.extras ?? []) {
|
|
286
|
+
if (old.suppressed) continue;
|
|
287
|
+
if (!extras.some((e) => e.kind === old.kind && e.logicalName === old.logicalName)) {
|
|
288
|
+
removeIfCurrent(old.kind, old.logicalName, old.backend);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
const builtin = Boolean(FACTORIES[entry.backend as string]);
|
|
292
|
+
if (replaceIfCurrent(kind, logicalName, expected, backend)) {
|
|
293
|
+
installedSlots.set(key, {
|
|
294
|
+
kind,
|
|
295
|
+
logicalName,
|
|
296
|
+
backend,
|
|
297
|
+
entryJson,
|
|
298
|
+
builtin,
|
|
299
|
+
fallback: entry.fallback,
|
|
300
|
+
extras: installedExtras,
|
|
301
|
+
});
|
|
302
|
+
} else {
|
|
303
|
+
// Record the ask with no installed instance, so unchanged reloads skip instead of
|
|
304
|
+
// re-losing this swap every apply; installed helpers stay recorded and removable.
|
|
305
|
+
installedSlots.set(key, {
|
|
306
|
+
kind,
|
|
307
|
+
logicalName,
|
|
308
|
+
entryJson,
|
|
309
|
+
builtin,
|
|
310
|
+
fallback: entry.fallback,
|
|
311
|
+
extras: installedExtras,
|
|
312
|
+
});
|
|
313
|
+
harperLogger.warn(`models.${kind}.${logicalName}: another registration owns this entry; leaving it in place`);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
async function applyModels(block: ModelsConfig | null | undefined, isBoot: boolean): Promise<void> {
|
|
318
|
+
if (!isBoot) {
|
|
319
|
+
if (block === undefined) {
|
|
320
|
+
// A snapshot with no `models` key is not evidence of intent: a non-atomic in-place rewrite
|
|
321
|
+
// can be observed as a valid YAML prefix that simply hasn't reached the block yet, and
|
|
322
|
+
// removing every backend on that would be catastrophic. Removal is expressed by an empty
|
|
323
|
+
// or shrunken block — `models: {}`. A bare `models:` key (null) is rejected by validation
|
|
324
|
+
// below, exactly as boot's validator rejects it, so reload cannot accept a file a restart
|
|
325
|
+
// would refuse.
|
|
326
|
+
harperLogger.debug?.('models: watched config has no models block; projection left as-is');
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
// Boot validates the composed config (configValidator); a reload must enforce the same
|
|
330
|
+
// schema, or a typo boot would reject hot-applies silently (e.g. a misspelled baseUrl
|
|
331
|
+
// falling back to the public endpoint).
|
|
332
|
+
const { error } = validateModelsBlock(block);
|
|
333
|
+
if (error) {
|
|
334
|
+
harperLogger.error(`models: rejecting config reload (${error.message}); keeping the previous projection`);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
const desired: DesiredEntry[] = [];
|
|
339
|
+
const presentKeys = new Set<string>();
|
|
340
|
+
collectKind('embedding', block?.embedding, desired, presentKeys);
|
|
341
|
+
collectKind('generative', block?.generative, desired, presentKeys);
|
|
342
|
+
|
|
343
|
+
const staged = new Map<string, { desiredEntry: DesiredEntry; backend: ModelBackend; extras: CapturedInstall[] }>();
|
|
344
|
+
const failedKeys = new Set<string>();
|
|
345
|
+
for (const desiredEntry of desired) {
|
|
346
|
+
const { kind, logicalName, entry, entryJson } = desiredEntry;
|
|
347
|
+
const key = slotKey(kind, logicalName);
|
|
348
|
+
const slot = installedSlots.get(key);
|
|
349
|
+
// Unchanged: skip while ANY occupant serves (rebuilding under an override would run the factory
|
|
350
|
+
// only to lose the swap). Boot is stricter — it must overwrite occupants, so it only skips when
|
|
351
|
+
// the projection's own instance is installed; an emptied registry rebuilds either way.
|
|
352
|
+
const installed = getBackend(kind, logicalName);
|
|
353
|
+
if (slot && slot.entryJson === entryJson && (isBoot ? installed === slot.backend : installed !== undefined))
|
|
354
|
+
continue;
|
|
355
|
+
// Reload runs factories for built-ins only: they are independent, pure constructors. A module
|
|
356
|
+
// factory may compose with other entries (wrap an earlier backend), which staged construction
|
|
357
|
+
// cannot honor — changing one needs a restart, exactly as before this feature. The guard covers
|
|
358
|
+
// the incoming backend AND a currently-installed module-backed slot: a module→built-in rewrite
|
|
359
|
+
// is still a change of a restart-managed entry, so it must not live-replace the module (which
|
|
360
|
+
// would drop its helpers with none of the disposal a restart performs).
|
|
361
|
+
if (!isBoot && (!FACTORIES[entry.backend as string] || slot?.builtin === false)) {
|
|
362
|
+
harperLogger.warn(
|
|
363
|
+
`models.${kind}.${logicalName}: module-backed entries require a restart to add or change; ` +
|
|
364
|
+
`keeping the previous projection for this entry`
|
|
365
|
+
);
|
|
366
|
+
failedKeys.add(key);
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
123
369
|
// Warn before expansion: literal credentials in `harperdb-config.yaml`
|
|
124
370
|
// land on disk, in backups, and (depending on deployment) in replicated
|
|
125
371
|
// config tables. The `${VAR}` indirection pattern from
|
|
@@ -132,19 +378,218 @@ async function registerKind(kind: ModelKind, entries: Record<string, ModelEntry>
|
|
|
132
378
|
// (env var unset) pass through unchanged — backend's required-field
|
|
133
379
|
// validation catches them with a meaningful error.
|
|
134
380
|
const config = expandEnvVarsDeep(entry);
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
381
|
+
const { backend, extras } = await constructBackend(kind, logicalName, async () => {
|
|
382
|
+
const builtin = FACTORIES[entry.backend as string];
|
|
383
|
+
if (builtin) {
|
|
384
|
+
await builtin({ logicalName, kind, config });
|
|
385
|
+
} else {
|
|
386
|
+
// Not a built-in name: treat `backend` as a module specifier (#1471).
|
|
387
|
+
await registerFromModule(kind, logicalName, entry.backend as string, config);
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
if (!backend) {
|
|
391
|
+
harperLogger.error(
|
|
392
|
+
`models.${kind}.${logicalName}: registration installed no backend; skipping ` +
|
|
393
|
+
`(a factory must register before its returned promise resolves)`
|
|
394
|
+
);
|
|
395
|
+
failedKeys.add(key);
|
|
396
|
+
continue;
|
|
141
397
|
}
|
|
142
|
-
//
|
|
143
|
-
|
|
398
|
+
// Boot publishes each entry as it is built, in config order, so a later module factory
|
|
399
|
+
// observes earlier entries exactly as it always has (a wrapper resolves its base). Reload
|
|
400
|
+
// defers everything to one synchronous publish, so a request never sees a partial rebuild.
|
|
401
|
+
if (isBoot) publishEntry(desiredEntry, backend, extras, true);
|
|
402
|
+
else staged.set(key, { desiredEntry, backend, extras });
|
|
144
403
|
} catch (err) {
|
|
404
|
+
failedKeys.add(key);
|
|
145
405
|
harperLogger.error(`models.${kind}.${logicalName}: registration failed (${(err as Error)?.message ?? err})`);
|
|
146
406
|
}
|
|
147
407
|
}
|
|
408
|
+
|
|
409
|
+
for (const { desiredEntry, backend, extras } of staged.values()) {
|
|
410
|
+
publishEntry(desiredEntry, backend, extras, isBoot);
|
|
411
|
+
}
|
|
412
|
+
const desiredKeys = new Set(desired.map((d) => slotKey(d.kind, d.logicalName)));
|
|
413
|
+
// Module-backed slots retained across a removal are absent from both `desiredKeys` and `presentKeys`,
|
|
414
|
+
// so neither routing loop below would restore them after the clear; track them so their recorded
|
|
415
|
+
// fallback survives — else the primary keeps serving while its failover is silently dropped.
|
|
416
|
+
const retainedModuleKeys = new Set<string>();
|
|
417
|
+
for (const [key, slot] of [...installedSlots]) {
|
|
418
|
+
if (presentKeys.has(key)) continue;
|
|
419
|
+
// Module-backed entries are restart-managed on reload in BOTH directions: refusing an added
|
|
420
|
+
// rename target while removing its old name would turn the rename into a bare removal.
|
|
421
|
+
if (!isBoot && slot.builtin === false) {
|
|
422
|
+
harperLogger.warn(
|
|
423
|
+
`models.${slot.kind}.${slot.logicalName}: module-backed entries require a restart to remove; keeping it`
|
|
424
|
+
);
|
|
425
|
+
retainedModuleKeys.add(key);
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
if (slot.backend) removeIfCurrent(slot.kind, slot.logicalName, slot.backend);
|
|
429
|
+
for (const extra of slot.extras ?? []) {
|
|
430
|
+
if (!extra.suppressed) removeIfCurrent(extra.kind, extra.logicalName, extra.backend);
|
|
431
|
+
}
|
|
432
|
+
installedSlots.delete(key);
|
|
433
|
+
// A removed entry may have been shadowing a factory helper of the same name; put the
|
|
434
|
+
// helper back, so the live registry matches a restart with this final config. Not gated on
|
|
435
|
+
// slot.backend: the claim suppressed the helper before this entry's swap was known to win, so
|
|
436
|
+
// a lost swap must still release it (a no-op while the name is held or nothing is suppressed).
|
|
437
|
+
restoreSuppressedHelper(slot.kind, slot.logicalName);
|
|
438
|
+
}
|
|
439
|
+
// Rebuild fallback routing from scratch each apply so a removed/changed `fallback:` (or a
|
|
440
|
+
// removed `models:` block) doesn't leave stale routing behind (#1326).
|
|
441
|
+
clearFallbackGroups();
|
|
442
|
+
for (const { kind, logicalName, entry } of desired) {
|
|
443
|
+
// A failed rebuild keeps its previous backend serving, so it keeps the routing that was
|
|
444
|
+
// applied WITH that backend — not the new entry's group, which belongs to the build that
|
|
445
|
+
// did not happen.
|
|
446
|
+
const key = slotKey(kind, logicalName);
|
|
447
|
+
const group = failedKeys.has(key) ? installedSlots.get(key)?.fallback : entry.fallback;
|
|
448
|
+
if (group?.length && getBackend(kind, logicalName)) setFallbackGroup(kind, logicalName, group);
|
|
449
|
+
}
|
|
450
|
+
// Routing follows the name, not the instance.
|
|
451
|
+
for (const [key, slot] of installedSlots) {
|
|
452
|
+
if (desiredKeys.has(key) || !presentKeys.has(key)) continue;
|
|
453
|
+
if (slot.fallback?.length && getBackend(slot.kind, slot.logicalName)) {
|
|
454
|
+
setFallbackGroup(slot.kind, slot.logicalName, slot.fallback);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
// Retained module-backed slots keep serving under their old name, so they keep the fallback group
|
|
458
|
+
// they were built with — a restart-managed removal must not half-apply as a silent failover loss.
|
|
459
|
+
for (const key of retainedModuleKeys) {
|
|
460
|
+
const slot = installedSlots.get(key);
|
|
461
|
+
if (slot?.fallback?.length && getBackend(slot.kind, slot.logicalName)) {
|
|
462
|
+
setFallbackGroup(slot.kind, slot.logicalName, slot.fallback);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// ── Hot reload wiring ─────────────────────────────────────────────────────────
|
|
468
|
+
|
|
469
|
+
let modelsConfigWatcher: RootConfigWatcher | undefined;
|
|
470
|
+
let ownsModelsConfigWatcher = false;
|
|
471
|
+
let modelsApplyListener: ((config: unknown) => void) | undefined;
|
|
472
|
+
let pendingSettle: NodeJS.Timeout | undefined;
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Watch the root config file and hot-apply `models:` changes. Follows `harper_logger`'s
|
|
476
|
+
* root-config-watch pattern: one watcher per worker, each worker reprojecting its own registry.
|
|
477
|
+
*
|
|
478
|
+
* The watcher sees the FILE, but `HARPER_DEFAULT_CONFIG` / `HARPER_CONFIG` / `HARPER_SET_CONFIG`
|
|
479
|
+
* compose with it at boot — so if any of those names `models`, the file alone is not authoritative
|
|
480
|
+
* for the block and hot reload stays off, preserving boot semantics unchanged. (That is also the
|
|
481
|
+
* compatibility gate: an orchestrator still injecting models through `HARPER_SET_CONFIG` gets
|
|
482
|
+
* today's restart behavior; one that writes the file instead gets live reload.)
|
|
483
|
+
*
|
|
484
|
+
* Returns whether the watch is active.
|
|
485
|
+
*/
|
|
486
|
+
export function startModelsConfigHotReload(options?: {
|
|
487
|
+
configFilePath?: string;
|
|
488
|
+
debounceMs?: number;
|
|
489
|
+
/** Test seam: invoked when a watcher snapshot is observed, before its settle timer is armed. */
|
|
490
|
+
onSnapshotObserved?: () => void;
|
|
491
|
+
/** Test seam: subscribe to this instance (caller-owned) instead of constructing or sharing. */
|
|
492
|
+
watcher?: RootConfigWatcher;
|
|
493
|
+
}): boolean {
|
|
494
|
+
if (modelsConfigWatcher) return true;
|
|
495
|
+
const pinnedBy = envLayerNamingModels();
|
|
496
|
+
if (pinnedBy) {
|
|
497
|
+
harperLogger.info(
|
|
498
|
+
`models: hot reload of the config file is disabled: ${pinnedBy} also defines 'models', so the file alone is not authoritative for it`
|
|
499
|
+
);
|
|
500
|
+
return false;
|
|
501
|
+
}
|
|
502
|
+
// One watcher per isolate: logging already opens one, and a second per worker doubles the
|
|
503
|
+
// native-watcher/FD footprint and the read+parse work on every config write. A test-supplied
|
|
504
|
+
// path gets a private instance, owned (and closed) by this module.
|
|
505
|
+
ownsModelsConfigWatcher = options?.watcher === undefined && options?.configFilePath !== undefined;
|
|
506
|
+
modelsConfigWatcher =
|
|
507
|
+
options?.watcher ??
|
|
508
|
+
(ownsModelsConfigWatcher ? new RootConfigWatcher(options?.configFilePath) : getSharedRootConfigWatcher());
|
|
509
|
+
// An 'error' event with no listener would take the worker down; and `ready` is an events.once
|
|
510
|
+
// promise that rejects on a pre-ready 'error', so it must be observed too.
|
|
511
|
+
modelsConfigWatcher.on('error', modelsWatcherErrorListener);
|
|
512
|
+
modelsConfigWatcher.ready.catch(() => {});
|
|
513
|
+
// 'ready' carries the file state at watch start: a rewrite that lands during the watcher's
|
|
514
|
+
// initial scan arrives there rather than as 'change', and an unchanged block is a no-op anyway.
|
|
515
|
+
// Settle window: a non-atomic in-place rewrite emits an event per write() and an early snapshot
|
|
516
|
+
// can be a schema-valid subset of the final file (a truncated map removes the unwritten
|
|
517
|
+
// entries). Applying only the last event in a quiet window adopts the completed file. The real
|
|
518
|
+
// contract for orchestrators remains an atomic tmp+rename.
|
|
519
|
+
const debounceMs = options?.debounceMs ?? 150;
|
|
520
|
+
const applyFromFile = (config: unknown) => {
|
|
521
|
+
const models = (config as RootConfig | undefined)?.models;
|
|
522
|
+
options?.onSnapshotObserved?.();
|
|
523
|
+
clearTimeout(pendingSettle);
|
|
524
|
+
pendingSettle = setTimeout(() => {
|
|
525
|
+
void queueModelsApply(models, false);
|
|
526
|
+
}, debounceMs);
|
|
527
|
+
pendingSettle.unref?.();
|
|
528
|
+
};
|
|
529
|
+
modelsConfigWatcher.on('ready', applyFromFile);
|
|
530
|
+
modelsConfigWatcher.on('change', applyFromFile);
|
|
531
|
+
modelsApplyListener = applyFromFile;
|
|
532
|
+
// The shared watcher's one-time 'ready' may predate this subscription (logging usually
|
|
533
|
+
// constructs the singleton first, and EventEmitter does not replay). A snapshot it already
|
|
534
|
+
// holds is applied directly, so a rewrite landing before this subscription is not invisible
|
|
535
|
+
// until the next write.
|
|
536
|
+
if (modelsConfigWatcher.config !== undefined) applyFromFile(modelsConfigWatcher.config);
|
|
537
|
+
return true;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/** Stop watching the config file. The current projection keeps serving. */
|
|
541
|
+
export function stopModelsConfigHotReload(): void {
|
|
542
|
+
clearTimeout(pendingSettle);
|
|
543
|
+
pendingSettle = undefined;
|
|
544
|
+
if (modelsConfigWatcher) {
|
|
545
|
+
if (modelsApplyListener) {
|
|
546
|
+
modelsConfigWatcher.off('ready', modelsApplyListener);
|
|
547
|
+
modelsConfigWatcher.off('change', modelsApplyListener);
|
|
548
|
+
}
|
|
549
|
+
modelsConfigWatcher.off('error', modelsWatcherErrorListener);
|
|
550
|
+
// The shared watcher belongs to the isolate (logging still consumes it); close only a
|
|
551
|
+
// private, test-supplied instance.
|
|
552
|
+
if (ownsModelsConfigWatcher) modelsConfigWatcher.close();
|
|
553
|
+
}
|
|
554
|
+
modelsApplyListener = undefined;
|
|
555
|
+
modelsConfigWatcher = undefined;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
function modelsWatcherErrorListener(error: unknown): void {
|
|
559
|
+
harperLogger.warn(`models: config watcher error: ${(error as Error)?.message ?? error}`);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
function envLayerNamingModels(): string | undefined {
|
|
563
|
+
for (const name of ['HARPER_SET_CONFIG', 'HARPER_CONFIG', 'HARPER_DEFAULT_CONFIG']) {
|
|
564
|
+
const raw = process.env[name];
|
|
565
|
+
if (!raw) continue;
|
|
566
|
+
try {
|
|
567
|
+
const parsed = JSON.parse(raw);
|
|
568
|
+
// Dotted top-level keys ("models.embedding.default") compose into the models block too
|
|
569
|
+
// (harperConfigEnvVars flattens/expands them), so they pin exactly like a nested key.
|
|
570
|
+
if (
|
|
571
|
+
parsed &&
|
|
572
|
+
typeof parsed === 'object' &&
|
|
573
|
+
Object.keys(parsed).some((k) => k === 'models' || k.startsWith('models.'))
|
|
574
|
+
)
|
|
575
|
+
return name;
|
|
576
|
+
} catch {
|
|
577
|
+
// Malformed env config is reported by config loading itself; not this watcher's job.
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
return undefined;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
function warnOnLiteralCredentials(kind: ModelKind, logicalName: string, entry: ModelEntry): void {
|
|
584
|
+
for (const field of CREDENTIAL_FIELDS) {
|
|
585
|
+
const value = (entry as Record<string, unknown>)[field];
|
|
586
|
+
if (typeof value !== 'string' || value.length === 0) continue;
|
|
587
|
+
if (isUnresolvedEnvVarPlaceholder(value)) continue; // operator is using ${VAR} indirection
|
|
588
|
+
harperLogger.warn(
|
|
589
|
+
`models.${kind}.${logicalName}: '${field}' is a literal value in harperdb-config.yaml; ` +
|
|
590
|
+
`prefer \${ENV_VAR} indirection for credentials to keep them off disk`
|
|
591
|
+
);
|
|
592
|
+
}
|
|
148
593
|
}
|
|
149
594
|
|
|
150
595
|
/**
|
|
@@ -50,6 +50,7 @@ function getIdMappingRecord(auditStore) {
|
|
|
50
50
|
// now we can take over the local node id
|
|
51
51
|
nameToId[node_name] = 0;
|
|
52
52
|
auditStore.putSync(REMOTE_NODE_IDS, pack(idMappingRecord));
|
|
53
|
+
invalidateNodeNames(auditStore);
|
|
53
54
|
}
|
|
54
55
|
return idMappingRecord;
|
|
55
56
|
}
|
|
@@ -84,6 +85,7 @@ export function remoteToLocalNodeId(remoteMapping: any, auditStore: any) {
|
|
|
84
85
|
}
|
|
85
86
|
if (hasChanges) {
|
|
86
87
|
auditStore.putSync(REMOTE_NODE_IDS, pack(idMappingRecord));
|
|
88
|
+
invalidateNodeNames(auditStore);
|
|
87
89
|
}
|
|
88
90
|
return remoteToLocalId;
|
|
89
91
|
}
|
|
@@ -103,6 +105,7 @@ export function getIdOfRemoteNode(remoteNodeName, auditStore) {
|
|
|
103
105
|
id = lastId + 1;
|
|
104
106
|
nameToId[remoteNodeName] = id;
|
|
105
107
|
auditStore.putSync(REMOTE_NODE_IDS, pack(idMappingRecord));
|
|
108
|
+
invalidateNodeNames(auditStore);
|
|
106
109
|
}
|
|
107
110
|
logger.trace?.('The remote node name map', remoteNodeName, nameToId, id);
|
|
108
111
|
return id;
|
|
@@ -123,3 +126,50 @@ export function lastTimeInAuditStore(auditStore: Database) {
|
|
|
123
126
|
export function getThisNodeId(auditStore: any) {
|
|
124
127
|
return exportIdMapping(auditStore)?.[server.hostname];
|
|
125
128
|
}
|
|
129
|
+
|
|
130
|
+
// Inverted id -> name map. exportIdMapping() re-reads and unpacks the mapping record on every call,
|
|
131
|
+
// which is far too much per replicated entry on the apply thread; ids are stable once minted, so the
|
|
132
|
+
// inversion is cached and dropped wherever nameToId is written. Keyed per audit store because short
|
|
133
|
+
// ids are minted per database in first-seen order — id 1 names a different node in each one.
|
|
134
|
+
// Refresh window for a MISS. A hit is always trusted, but a miss cannot be: ids are minted by
|
|
135
|
+
// whichever worker first talks to a peer, and invalidation only reaches that worker's own copy — so
|
|
136
|
+
// a newly admitted node is absent from every other worker's map until it is rebuilt. Rebuilding on
|
|
137
|
+
// every miss would put a store read and unpack back on the apply thread for each unmapped entry, so
|
|
138
|
+
// misses re-read at most this often — short enough that a joining node loses at most a stray entry,
|
|
139
|
+
// long enough that a burst of unmapped ids cannot drive the store. `onMiss` records whether the
|
|
140
|
+
// cached map was built by a `rebuildOnMiss` caller, which is what bounds that caller to one read per
|
|
141
|
+
// window too rather than exempting it from the interval entirely.
|
|
142
|
+
const NODE_NAME_REFRESH_MS = 50;
|
|
143
|
+
const idToNodeName = new WeakMap<object, { names: Map<number, string>; refreshedAt: number; onMiss: boolean }>();
|
|
144
|
+
function invalidateNodeNames(auditStore: any) {
|
|
145
|
+
idToNodeName.delete(auditStore);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* The node name a local short id refers to. Callers that must attribute a replicated entry to its
|
|
150
|
+
* origin node need the globally stable name, not the id, which is assigned per node.
|
|
151
|
+
*/
|
|
152
|
+
export function getNodeNameForId(
|
|
153
|
+
auditStore: any,
|
|
154
|
+
nodeId: number | undefined,
|
|
155
|
+
rebuildOnMiss = false
|
|
156
|
+
): string | undefined {
|
|
157
|
+
if (typeof nodeId !== 'number' || !auditStore) return undefined;
|
|
158
|
+
const cached = idToNodeName.get(auditStore);
|
|
159
|
+
const hit = cached?.names.get(nodeId);
|
|
160
|
+
if (hit !== undefined) return hit;
|
|
161
|
+
const now = Date.now();
|
|
162
|
+
// `rebuildOnMiss` is for entry classes whose loss costs more than the read: a dropped record lock
|
|
163
|
+
// release leaves the home holding a grant until its own deadline, so a just-minted id is worth one
|
|
164
|
+
// store read rather than up to `NODE_NAME_REFRESH_MS` of dropped entries. ONE read, though, not one
|
|
165
|
+
// per entry — an id this map will never resolve (a node whose mapping was purged, an origin
|
|
166
|
+
// relayed from a database that never minted one) is exactly the burst the interval exists to keep
|
|
167
|
+
// off the apply thread, and a rebuild inside the window is not stale: a write to the mapping drops
|
|
168
|
+
// this entry outright (`invalidateNodeNames`).
|
|
169
|
+
if (cached && now - cached.refreshedAt < NODE_NAME_REFRESH_MS && (!rebuildOnMiss || cached.onMiss)) return undefined;
|
|
170
|
+
const nameToId = exportIdMapping(auditStore);
|
|
171
|
+
const names = new Map<number, string>();
|
|
172
|
+
for (const name in nameToId) names.set(nameToId[name], name);
|
|
173
|
+
idToNodeName.set(auditStore, { names, refreshedAt: now, onMiss: rebuildOnMiss });
|
|
174
|
+
return names.get(nodeId);
|
|
175
|
+
}
|