harper 5.1.20 → 5.2.0-alpha.5
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/operations.ts +13 -16
- package/components/Application.ts +188 -7
- package/components/ApplicationScope.ts +3 -0
- package/components/Scope.ts +12 -0
- package/components/anthropic/index.ts +9 -2
- package/components/bedrock/index.ts +25 -2
- package/components/componentLoader.ts +56 -7
- package/components/componentSecrets.ts +451 -0
- package/components/deploymentRecorder.ts +7 -1
- package/components/mcp/adapters/fastify.ts +3 -0
- package/components/mcp/adapters/harperHttp.ts +3 -0
- package/components/mcp/audit.ts +21 -6
- package/components/mcp/customResourceRegistry.ts +17 -38
- package/components/mcp/listChanged.ts +37 -20
- package/components/mcp/quota.ts +145 -0
- package/components/mcp/rateLimit.ts +152 -14
- package/components/mcp/resources.ts +2 -4
- package/components/mcp/toolRegistry.ts +73 -4
- package/components/mcp/tools/application.ts +77 -8
- package/components/mcp/tools/operations.ts +106 -36
- package/components/mcp/tools/schemas/operationDescriptions.ts +4 -4
- package/components/mcp/tools/schemas/operations.ts +5 -0
- package/components/mcp/transport.ts +55 -4
- package/components/ollama/index.ts +6 -2
- package/components/openai/index.ts +9 -2
- package/components/operations.js +204 -10
- package/components/operationsValidation.js +173 -0
- package/components/secretOperations.ts +498 -0
- package/config/configUtils.ts +25 -1
- package/config-app.schema.json +81 -0
- package/config-root.schema.json +8 -1
- package/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +4 -3
- package/dataLayer/readAuditLog.ts +14 -0
- package/dataLayer/schemaDescribe.ts +1 -0
- package/dist/agent/operations.d.ts +7 -4
- package/dist/agent/operations.js +13 -15
- package/dist/agent/operations.js.map +1 -1
- package/dist/components/Application.d.ts +25 -2
- package/dist/components/Application.js +166 -7
- package/dist/components/Application.js.map +1 -1
- package/dist/components/ApplicationScope.d.ts +2 -0
- package/dist/components/ApplicationScope.js +3 -0
- package/dist/components/ApplicationScope.js.map +1 -1
- package/dist/components/Scope.d.ts +8 -0
- package/dist/components/Scope.js +11 -0
- package/dist/components/Scope.js.map +1 -1
- package/dist/components/anthropic/index.d.ts +4 -1
- package/dist/components/anthropic/index.js +4 -2
- package/dist/components/anthropic/index.js.map +1 -1
- package/dist/components/bedrock/index.d.ts +4 -1
- package/dist/components/bedrock/index.js +22 -2
- package/dist/components/bedrock/index.js.map +1 -1
- package/dist/components/componentLoader.d.ts +2 -0
- package/dist/components/componentLoader.js +52 -10
- package/dist/components/componentLoader.js.map +1 -1
- package/dist/components/componentSecrets.d.ts +63 -0
- package/dist/components/componentSecrets.js +405 -0
- package/dist/components/componentSecrets.js.map +1 -0
- package/dist/components/deploymentRecorder.d.ts +3 -0
- package/dist/components/deploymentRecorder.js +4 -3
- package/dist/components/deploymentRecorder.js.map +1 -1
- package/dist/components/mcp/adapters/fastify.d.ts +2 -0
- package/dist/components/mcp/adapters/fastify.js +1 -0
- package/dist/components/mcp/adapters/fastify.js.map +1 -1
- package/dist/components/mcp/adapters/harperHttp.d.ts +2 -0
- package/dist/components/mcp/adapters/harperHttp.js +1 -0
- package/dist/components/mcp/adapters/harperHttp.js.map +1 -1
- package/dist/components/mcp/audit.d.ts +2 -2
- package/dist/components/mcp/audit.js +20 -5
- package/dist/components/mcp/audit.js.map +1 -1
- package/dist/components/mcp/customResourceRegistry.d.ts +5 -12
- package/dist/components/mcp/customResourceRegistry.js +15 -30
- package/dist/components/mcp/customResourceRegistry.js.map +1 -1
- package/dist/components/mcp/listChanged.d.ts +7 -2
- package/dist/components/mcp/listChanged.js +28 -0
- package/dist/components/mcp/listChanged.js.map +1 -1
- package/dist/components/mcp/quota.d.ts +31 -0
- package/dist/components/mcp/quota.js +155 -0
- package/dist/components/mcp/quota.js.map +1 -0
- package/dist/components/mcp/rateLimit.d.ts +19 -3
- package/dist/components/mcp/rateLimit.js +129 -13
- package/dist/components/mcp/rateLimit.js.map +1 -1
- package/dist/components/mcp/resources.js.map +1 -1
- package/dist/components/mcp/toolRegistry.d.ts +39 -1
- package/dist/components/mcp/toolRegistry.js +60 -5
- package/dist/components/mcp/toolRegistry.js.map +1 -1
- package/dist/components/mcp/tools/application.d.ts +8 -1
- package/dist/components/mcp/tools/application.js +65 -5
- package/dist/components/mcp/tools/application.js.map +1 -1
- package/dist/components/mcp/tools/operations.d.ts +15 -3
- package/dist/components/mcp/tools/operations.js +99 -38
- package/dist/components/mcp/tools/operations.js.map +1 -1
- package/dist/components/mcp/tools/schemas/operationDescriptions.js +4 -4
- package/dist/components/mcp/tools/schemas/operationDescriptions.js.map +1 -1
- package/dist/components/mcp/tools/schemas/operations.js +4 -0
- package/dist/components/mcp/tools/schemas/operations.js.map +1 -1
- package/dist/components/mcp/transport.d.ts +6 -0
- package/dist/components/mcp/transport.js +47 -3
- package/dist/components/mcp/transport.js.map +1 -1
- package/dist/components/ollama/index.d.ts +4 -1
- package/dist/components/ollama/index.js +4 -2
- package/dist/components/ollama/index.js.map +1 -1
- package/dist/components/openai/index.d.ts +4 -1
- package/dist/components/openai/index.js +4 -2
- package/dist/components/openai/index.js.map +1 -1
- package/dist/components/operations.d.ts +39 -0
- package/dist/components/operations.js +194 -2
- package/dist/components/operations.js.map +1 -1
- package/dist/components/operationsValidation.d.ts +38 -0
- package/dist/components/operationsValidation.js +155 -0
- package/dist/components/operationsValidation.js.map +1 -1
- package/dist/components/secretOperations.d.ts +113 -0
- package/dist/components/secretOperations.js +475 -0
- package/dist/components/secretOperations.js.map +1 -0
- package/dist/config/configUtils.d.ts +4 -1
- package/dist/config/configUtils.js +18 -1
- package/dist/config/configUtils.js.map +1 -1
- package/dist/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +4 -3
- package/dist/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js.map +1 -1
- package/dist/dataLayer/readAuditLog.js +11 -0
- package/dist/dataLayer/readAuditLog.js.map +1 -1
- package/dist/dataLayer/schemaDescribe.js +2 -0
- package/dist/dataLayer/schemaDescribe.js.map +1 -1
- package/dist/globals.d.ts +1 -1
- package/dist/globals.js +1 -0
- package/dist/globals.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/json/systemSchema.json +38 -0
- package/dist/resources/DatabaseTransaction.d.ts +31 -3
- package/dist/resources/DatabaseTransaction.js +190 -29
- package/dist/resources/DatabaseTransaction.js.map +1 -1
- package/dist/resources/LMDBTransaction.d.ts +1 -1
- package/dist/resources/LMDBTransaction.js +43 -5
- package/dist/resources/LMDBTransaction.js.map +1 -1
- package/dist/resources/PrimaryRocksDatabase.d.ts +51 -0
- package/dist/resources/PrimaryRocksDatabase.js +194 -0
- package/dist/resources/PrimaryRocksDatabase.js.map +1 -0
- package/dist/resources/RecordEncoder.d.ts +1 -0
- package/dist/resources/RecordEncoder.js +81 -75
- package/dist/resources/RecordEncoder.js.map +1 -1
- package/dist/resources/RequestTarget.d.ts +19 -0
- package/dist/resources/RequestTarget.js.map +1 -1
- package/dist/resources/Resource.d.ts +0 -1
- package/dist/resources/Resource.js +103 -16
- package/dist/resources/Resource.js.map +1 -1
- package/dist/resources/ResourceInterface.d.ts +6 -2
- package/dist/resources/ResourceInterface.js.map +1 -1
- package/dist/resources/Resources.js.map +1 -1
- package/dist/resources/Table.d.ts +6 -4
- package/dist/resources/Table.js +247 -24
- package/dist/resources/Table.js.map +1 -1
- package/dist/resources/analytics/read.js +25 -28
- package/dist/resources/analytics/read.js.map +1 -1
- package/dist/resources/blob.js +165 -22
- package/dist/resources/blob.js.map +1 -1
- package/dist/resources/crdt.d.ts +2 -0
- package/dist/resources/crdt.js +45 -36
- package/dist/resources/crdt.js.map +1 -1
- package/dist/resources/databases.d.ts +1 -0
- package/dist/resources/databases.js +134 -7
- package/dist/resources/databases.js.map +1 -1
- package/dist/resources/graphql.js +8 -0
- package/dist/resources/graphql.js.map +1 -1
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +35 -5
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +239 -14
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
- package/dist/resources/jsResource.js +6 -0
- package/dist/resources/jsResource.js.map +1 -1
- package/dist/resources/loadEnv.js +30 -3
- package/dist/resources/loadEnv.js.map +1 -1
- package/dist/resources/login.js +2 -2
- package/dist/resources/login.js.map +1 -1
- package/dist/resources/models/Models.js +14 -3
- package/dist/resources/models/Models.js.map +1 -1
- package/dist/resources/models/embedHook.js +10 -2
- package/dist/resources/models/embedHook.js.map +1 -1
- package/dist/resources/models/openaiStream.d.ts +56 -0
- package/dist/resources/models/openaiStream.js +94 -0
- package/dist/resources/models/openaiStream.js.map +1 -0
- package/dist/resources/replayLogs.js +4 -3
- package/dist/resources/replayLogs.js.map +1 -1
- package/dist/resources/search.d.ts +1 -1
- package/dist/resources/search.js +104 -11
- package/dist/resources/search.js.map +1 -1
- package/dist/resources/secretDecryptor.d.ts +54 -0
- package/dist/resources/secretDecryptor.js +131 -0
- package/dist/resources/secretDecryptor.js.map +1 -0
- package/dist/resources/tracked.js +15 -5
- package/dist/resources/tracked.js.map +1 -1
- package/dist/security/auth.js +71 -8
- package/dist/security/auth.js.map +1 -1
- package/dist/security/jsLoader.js +11 -0
- package/dist/security/jsLoader.js.map +1 -1
- package/dist/security/tokenAuthentication.d.ts +9 -1
- package/dist/security/tokenAuthentication.js +31 -1
- package/dist/security/tokenAuthentication.js.map +1 -1
- package/dist/server/DurableSubscriptionsSession.d.ts +1 -1
- package/dist/server/DurableSubscriptionsSession.js +8 -4
- package/dist/server/DurableSubscriptionsSession.js.map +1 -1
- package/dist/server/REST.js +28 -9
- package/dist/server/REST.js.map +1 -1
- package/dist/server/fastifyRoutes.js +11 -1
- package/dist/server/fastifyRoutes.js.map +1 -1
- package/dist/server/graphqlQuerying.js +4 -3
- package/dist/server/graphqlQuerying.js.map +1 -1
- package/dist/server/http.d.ts +40 -2
- package/dist/server/http.js +560 -77
- package/dist/server/http.js.map +1 -1
- package/dist/server/itc/serverHandlers.js +64 -0
- package/dist/server/itc/serverHandlers.js.map +1 -1
- package/dist/server/liveSubscriptionAuth.d.ts +12 -0
- package/dist/server/liveSubscriptionAuth.js +140 -0
- package/dist/server/liveSubscriptionAuth.js.map +1 -0
- package/dist/server/loadRootComponents.js +2 -1
- package/dist/server/loadRootComponents.js.map +1 -1
- package/dist/server/middlewareChain.d.ts +44 -1
- package/dist/server/middlewareChain.js +86 -10
- package/dist/server/middlewareChain.js.map +1 -1
- package/dist/server/mqtt.js +2 -1
- package/dist/server/mqtt.js.map +1 -1
- package/dist/server/operationsServer.js +1 -1
- package/dist/server/operationsServer.js.map +1 -1
- package/dist/server/serverHelpers/Headers.d.ts +9 -0
- package/dist/server/serverHelpers/Headers.js +22 -0
- package/dist/server/serverHelpers/Headers.js.map +1 -1
- package/dist/server/serverHelpers/JSONStream.js +3 -3
- package/dist/server/serverHelpers/JSONStream.js.map +1 -1
- package/dist/server/serverHelpers/Request.d.ts +52 -1
- package/dist/server/serverHelpers/Request.js +122 -1
- package/dist/server/serverHelpers/Request.js.map +1 -1
- package/dist/server/serverHelpers/contentTypes.js +3 -8
- package/dist/server/serverHelpers/contentTypes.js.map +1 -1
- package/dist/server/serverHelpers/progressEmitter.d.ts +19 -0
- package/dist/server/serverHelpers/progressEmitter.js +52 -4
- package/dist/server/serverHelpers/progressEmitter.js.map +1 -1
- package/dist/server/serverHelpers/registeredOperations.d.ts +40 -0
- package/dist/server/serverHelpers/registeredOperations.js +286 -0
- package/dist/server/serverHelpers/registeredOperations.js.map +1 -0
- package/dist/server/serverHelpers/serverHandlers.js +11 -5
- package/dist/server/serverHelpers/serverHandlers.js.map +1 -1
- package/dist/server/serverHelpers/serverUtilities.d.ts +1 -0
- package/dist/server/serverHelpers/serverUtilities.js +67 -5
- package/dist/server/serverHelpers/serverUtilities.js.map +1 -1
- package/dist/server/serverHelpers/uwsServer.d.ts +80 -0
- package/dist/server/serverHelpers/uwsServer.js +433 -0
- package/dist/server/serverHelpers/uwsServer.js.map +1 -0
- package/dist/server/static.d.ts +0 -15
- package/dist/server/static.js +161 -2
- package/dist/server/static.js.map +1 -1
- package/dist/server/status/index.d.ts +4 -1
- package/dist/server/status/index.js +64 -4
- package/dist/server/status/index.js.map +1 -1
- package/dist/server/threads/manageThreads.d.ts +10 -0
- package/dist/server/threads/manageThreads.js +107 -4
- package/dist/server/threads/manageThreads.js.map +1 -1
- package/dist/server/threads/socketRouter.js +5 -1
- package/dist/server/threads/socketRouter.js.map +1 -1
- package/dist/server/threads/threadServer.js +92 -14
- package/dist/server/threads/threadServer.js.map +1 -1
- package/dist/upgrade/directives/5-2-0.d.ts +7 -0
- package/dist/upgrade/directives/5-2-0.js +108 -0
- package/dist/upgrade/directives/5-2-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/common_utils.js +5 -0
- package/dist/utility/common_utils.js.map +1 -1
- package/dist/utility/envFile.d.ts +41 -0
- package/dist/utility/envFile.js +221 -0
- package/dist/utility/envFile.js.map +1 -0
- package/dist/utility/globalSchema.d.ts +9 -0
- package/dist/utility/hdbTerms.d.ts +25 -0
- package/dist/utility/hdbTerms.js +31 -0
- package/dist/utility/hdbTerms.js.map +1 -1
- package/dist/utility/logging/harper_logger.d.ts +21 -0
- package/dist/utility/logging/harper_logger.js +160 -8
- package/dist/utility/logging/harper_logger.js.map +1 -1
- package/dist/utility/logging/logRotator.js +29 -17
- package/dist/utility/logging/logRotator.js.map +1 -1
- package/dist/utility/logging/readLog.d.ts +1 -1
- package/dist/utility/logging/readLog.js +305 -2
- package/dist/utility/logging/readLog.js.map +1 -1
- package/dist/utility/operationPermissions.d.ts +18 -0
- package/dist/utility/operationPermissions.js +35 -1
- package/dist/utility/operationPermissions.js.map +1 -1
- package/dist/utility/operation_authorization.d.ts +17 -0
- package/dist/utility/operation_authorization.js +38 -0
- package/dist/utility/operation_authorization.js.map +1 -1
- package/dist/utility/secretEnvelope.d.ts +30 -0
- package/dist/utility/secretEnvelope.js +94 -0
- package/dist/utility/secretEnvelope.js.map +1 -0
- package/dist/utility/signalling.d.ts +7 -0
- package/dist/utility/signalling.js +16 -0
- package/dist/utility/signalling.js.map +1 -1
- package/dist/validation/configValidator.js +70 -24
- package/dist/validation/configValidator.js.map +1 -1
- package/index.ts +4 -0
- package/json/systemSchema.json +38 -0
- package/npm-shrinkwrap.json +15066 -9347
- package/package.json +13 -8
- package/resources/DESIGN.md +15 -12
- package/resources/DatabaseTransaction.ts +206 -37
- package/resources/LMDBTransaction.ts +43 -9
- package/resources/PrimaryRocksDatabase.ts +201 -0
- package/resources/RecordEncoder.ts +81 -69
- package/resources/RequestTarget.ts +21 -0
- package/resources/Resource.ts +109 -25
- package/resources/ResourceInterface.ts +6 -2
- package/resources/Resources.ts +1 -3
- package/resources/Table.ts +238 -29
- package/resources/analytics/read.ts +25 -30
- package/resources/blob.ts +159 -23
- package/resources/crdt.ts +37 -31
- package/resources/databases.ts +143 -6
- package/resources/graphql.ts +8 -0
- package/resources/indexes/HierarchicalNavigableSmallWorld.ts +251 -17
- package/resources/jsResource.ts +6 -0
- package/resources/loadEnv.ts +34 -3
- package/resources/login.ts +2 -2
- package/resources/models/Models.ts +19 -3
- package/resources/models/embedHook.ts +13 -2
- package/resources/models/openaiStream.ts +133 -0
- package/resources/replayLogs.ts +4 -3
- package/resources/search.ts +113 -11
- package/resources/secretDecryptor.ts +159 -0
- package/resources/tracked.ts +24 -17
- package/security/auth.ts +70 -10
- package/security/jsLoader.ts +12 -0
- package/security/tokenAuthentication.ts +42 -1
- package/server/DESIGN.md +10 -0
- package/server/DurableSubscriptionsSession.ts +8 -4
- package/server/REST.ts +26 -9
- package/server/fastifyRoutes.ts +10 -1
- package/server/graphqlQuerying.ts +4 -3
- package/server/http.ts +563 -77
- package/server/itc/serverHandlers.js +70 -0
- package/server/liveSubscriptionAuth.ts +152 -0
- package/server/loadRootComponents.js +2 -1
- package/server/middlewareChain.ts +107 -17
- package/server/mqtt.ts +2 -1
- package/server/operationsServer.ts +2 -2
- package/server/serverHelpers/Headers.ts +24 -0
- package/server/serverHelpers/JSONStream.ts +3 -3
- package/server/serverHelpers/Request.ts +127 -0
- package/server/serverHelpers/contentTypes.ts +3 -8
- package/server/serverHelpers/progressEmitter.ts +55 -5
- package/server/serverHelpers/registeredOperations.ts +259 -0
- package/server/serverHelpers/serverHandlers.js +11 -5
- package/server/serverHelpers/serverUtilities.ts +88 -5
- package/server/serverHelpers/uwsServer.ts +488 -0
- package/server/static.ts +180 -2
- package/server/status/index.ts +72 -4
- package/server/threads/manageThreads.js +102 -4
- package/server/threads/socketRouter.ts +5 -1
- package/server/threads/threadServer.js +93 -10
- package/studio/web/assets/{Chat-SdD1EZca.js → Chat-Cv_2paZE.js} +2 -2
- package/studio/web/assets/{Chat-SdD1EZca.js.map → Chat-Cv_2paZE.js.map} +1 -1
- package/studio/web/assets/{FloatingChat-GMq6tHK9.js → FloatingChat-CPyPIcVR.js} +4 -4
- package/studio/web/assets/{FloatingChat-GMq6tHK9.js.map → FloatingChat-CPyPIcVR.js.map} +1 -1
- package/studio/web/assets/{applications-B5hhv7uA.js → applications-C0jT2vdZ.js} +2 -2
- package/studio/web/assets/{applications-B5hhv7uA.js.map → applications-C0jT2vdZ.js.map} +1 -1
- package/studio/web/assets/{index-VFcpNWgq.js → index-D_GKOkhn.js} +6 -6
- package/studio/web/assets/index-D_GKOkhn.js.map +1 -0
- package/studio/web/assets/{index.lazy-DgcJojIA.js → index.lazy-CAmCIA65.js} +4 -4
- package/studio/web/assets/{index.lazy-DgcJojIA.js.map → index.lazy-CAmCIA65.js.map} +1 -1
- package/studio/web/assets/{profile-BqN-8vzm.js → profile-CaF-4aZe.js} +2 -2
- package/studio/web/assets/{profile-BqN-8vzm.js.map → profile-CaF-4aZe.js.map} +1 -1
- package/studio/web/assets/{setComponentFile-CHFvFq9u.js → setComponentFile-DLW1DHCt.js} +2 -2
- package/studio/web/assets/{setComponentFile-CHFvFq9u.js.map → setComponentFile-DLW1DHCt.js.map} +1 -1
- package/studio/web/assets/{setup-CQ3vWJSr.js → setup-TGCErIhq.js} +2 -2
- package/studio/web/assets/{setup-CQ3vWJSr.js.map → setup-TGCErIhq.js.map} +1 -1
- package/studio/web/assets/{status-BsW5fjuh.js → status-DG0Maoao.js} +2 -2
- package/studio/web/assets/{status-BsW5fjuh.js.map → status-DG0Maoao.js.map} +1 -1
- package/studio/web/assets/{swagger-ui-react-DITdeB9-.js → swagger-ui-react-8T4SgQ1m.js} +2 -2
- package/studio/web/assets/{swagger-ui-react-DITdeB9-.js.map → swagger-ui-react-8T4SgQ1m.js.map} +1 -1
- package/studio/web/assets/{tsMode-Dyph-OUs.js → tsMode-DJ6Sl24Q.js} +2 -2
- package/studio/web/assets/{tsMode-Dyph-OUs.js.map → tsMode-DJ6Sl24Q.js.map} +1 -1
- package/studio/web/assets/{useEntityRestURL-D0QTUqex.js → useEntityRestURL-JO2mfWTQ.js} +2 -2
- package/studio/web/assets/{useEntityRestURL-D0QTUqex.js.map → useEntityRestURL-JO2mfWTQ.js.map} +1 -1
- package/studio/web/index.html +1 -1
- package/upgrade/directives/5-2-0.ts +82 -0
- package/upgrade/directives/directivesController.ts +2 -1
- package/utility/common_utils.ts +5 -0
- package/utility/envFile.ts +218 -0
- package/utility/hdbTerms.ts +31 -0
- package/utility/logging/harper_logger.ts +156 -8
- package/utility/logging/logRotator.ts +24 -14
- package/utility/logging/readLog.ts +323 -2
- package/utility/operationPermissions.ts +35 -1
- package/utility/operation_authorization.ts +74 -1
- package/utility/secretEnvelope.ts +113 -0
- package/utility/signalling.ts +15 -0
- package/validation/configValidator.ts +78 -25
- package/studio/web/assets/index-VFcpNWgq.js.map +0 -1
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// 5.2.0 — introduces system.hdb_secret for the encrypted secrets store (#715).
|
|
4
|
+
//
|
|
5
|
+
// Fresh installs get the table automatically via utility/mount_hdb.ts (which iterates
|
|
6
|
+
// json/systemSchema.json on first boot). This directive handles the upgrade path: existing
|
|
7
|
+
// installs that already have a system schema need the new table added explicitly.
|
|
8
|
+
//
|
|
9
|
+
// IMPORTANT: this directive must be versioned to the first release that ships the secrets-store
|
|
10
|
+
// operations depending on the table. Directives only run when
|
|
11
|
+
// current_version < directive_version <= upgrade_version (see
|
|
12
|
+
// directivesController.getVersionsForUpgrade), so tagging it for a later release than the
|
|
13
|
+
// dependent code means it never fires and the table is missing on upgraded installs
|
|
14
|
+
// (see the mis-tagging history documented in 5-1-0.ts).
|
|
15
|
+
|
|
16
|
+
import { databases } from '../../resources/databases.ts';
|
|
17
|
+
import systemSchema from '../../json/systemSchema.json';
|
|
18
|
+
import * as terms from '../../utility/hdbTerms.ts';
|
|
19
|
+
import * as initPaths from '../../dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js';
|
|
20
|
+
import bridge from '../../dataLayer/harperBridge/harperBridge.ts';
|
|
21
|
+
import hdbLogger from '../../utility/logging/harper_logger.ts';
|
|
22
|
+
|
|
23
|
+
const SECRET_TABLE = terms.SYSTEM_TABLE_NAMES.SECRET_TABLE_NAME;
|
|
24
|
+
|
|
25
|
+
async function createHdbSecretIfMissing() {
|
|
26
|
+
if (databases.system?.[SECRET_TABLE]) {
|
|
27
|
+
hdbLogger.info(`system.${SECRET_TABLE} already exists; skipping create.`);
|
|
28
|
+
// Still run the catalog patch (see patchHdbSecretIsHashAttribute).
|
|
29
|
+
await patchHdbSecretIsHashAttribute();
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
hdbLogger.info(`Creating system.${SECRET_TABLE} table for the secrets store.`);
|
|
34
|
+
|
|
35
|
+
const CreateTableObject =
|
|
36
|
+
require('../../dataLayer/CreateTableObject').default || require('../../dataLayer/CreateTableObject');
|
|
37
|
+
const schema = (systemSchema as any)[SECRET_TABLE];
|
|
38
|
+
if (!schema) {
|
|
39
|
+
throw new Error(`systemSchema.${SECRET_TABLE} is missing; cannot run 5.2.0 directive.`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
initPaths.initSystemSchemaPaths(terms.SYSTEM_SCHEMA_NAME, SECRET_TABLE);
|
|
43
|
+
const createTable = new (CreateTableObject as any)(terms.SYSTEM_SCHEMA_NAME, SECRET_TABLE, schema.hash_attribute);
|
|
44
|
+
createTable.attributes = schema.attributes;
|
|
45
|
+
const primaryKeyAttribute = createTable.attributes.find(({ attribute }) => attribute === schema.hash_attribute);
|
|
46
|
+
if (primaryKeyAttribute) primaryKeyAttribute.isPrimaryKey = true;
|
|
47
|
+
createTable.audit = true;
|
|
48
|
+
|
|
49
|
+
await bridge.createTable(SECRET_TABLE, createTable);
|
|
50
|
+
await patchHdbSecretIsHashAttribute();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Ensure the hdb_secret __dbis__ primary-key entry carries is_hash_attribute: true.
|
|
55
|
+
*
|
|
56
|
+
* harperdb@4.x reads is_hash_attribute from __dbis__ to derive the LMDB DBI open flags; without
|
|
57
|
+
* it the DBI is opened with the opposite flags (DUPSORT set) and LMDB throws MDB_INCOMPATIBLE,
|
|
58
|
+
* breaking downgrade — the exact failure patchHdbDeploymentIsHashAttribute() in 5-1-0.ts guards
|
|
59
|
+
* hdb_deployment against. Idempotent: no-op when the field is already set.
|
|
60
|
+
*/
|
|
61
|
+
async function patchHdbSecretIsHashAttribute() {
|
|
62
|
+
const systemTable = (databases as any).system?.[SECRET_TABLE];
|
|
63
|
+
if (!systemTable?.dbisDB) return;
|
|
64
|
+
|
|
65
|
+
const dbiName = `${SECRET_TABLE}/`;
|
|
66
|
+
const primaryAttr = systemTable.dbisDB.getSync(dbiName);
|
|
67
|
+
if (!primaryAttr || primaryAttr.is_hash_attribute) return; // already correct
|
|
68
|
+
primaryAttr.is_hash_attribute = true;
|
|
69
|
+
await systemTable.dbisDB.put(dbiName, primaryAttr);
|
|
70
|
+
hdbLogger.info(
|
|
71
|
+
`Patched system.${SECRET_TABLE} __dbis__ entry with is_hash_attribute=true for harperdb@4.x downgrade compatibility.`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const directive520 = {
|
|
76
|
+
version: '5.2.0',
|
|
77
|
+
description: 'create system.hdb_secret table for the secrets store',
|
|
78
|
+
sync_functions: [] as Array<() => unknown>,
|
|
79
|
+
async_functions: [createHdbSecretIfMissing] as Array<() => Promise<unknown>>,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export default [directive520];
|
|
@@ -10,13 +10,14 @@ import * as hdbUtils from '../../utility/common_utils.ts';
|
|
|
10
10
|
import * as hdbTerms from '../../utility/hdbTerms.ts';
|
|
11
11
|
import hdbLog from '../../utility/logging/harper_logger.ts';
|
|
12
12
|
import directive510 from './5-1-0.ts';
|
|
13
|
+
import directive520 from './5-2-0.ts';
|
|
13
14
|
const { DATA_VERSION, UPGRADE_VERSION } = hdbTerms.UPGRADE_JSON_FIELD_NAMES_ENUM as any;
|
|
14
15
|
|
|
15
16
|
let versions: any = new Map();
|
|
16
17
|
|
|
17
18
|
// All directive modules export an array of { version, sync_functions, async_functions }.
|
|
18
19
|
// New directives must be imported above and registered here.
|
|
19
|
-
for (const directive of directive510) {
|
|
20
|
+
for (const directive of [...directive510, ...directive520]) {
|
|
20
21
|
versions.set(directive.version, directive);
|
|
21
22
|
}
|
|
22
23
|
|
package/utility/common_utils.ts
CHANGED
|
@@ -819,7 +819,12 @@ export function convertToMS(interval: any) {
|
|
|
819
819
|
if (typeof interval === 'number') seconds = interval;
|
|
820
820
|
if (typeof interval === 'string') {
|
|
821
821
|
seconds = parseFloat(interval);
|
|
822
|
+
// Note the case-sensitive units: `M` is a (30-day) month, `m` is a minute.
|
|
822
823
|
switch (interval.slice(-1)) {
|
|
824
|
+
case 'y':
|
|
825
|
+
case 'Y':
|
|
826
|
+
seconds *= 86400 * 365;
|
|
827
|
+
break;
|
|
823
828
|
case 'M':
|
|
824
829
|
seconds *= 86400 * 30;
|
|
825
830
|
break;
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure helpers for inspecting and editing `.env` files behind the operations API.
|
|
3
|
+
*
|
|
4
|
+
* The goal is editor-facing, "smoke and mirrors" protection: callers can read key *names* and
|
|
5
|
+
* edit individual key/values without ever seeing — or accidentally clobbering — the other secret
|
|
6
|
+
* values in the file. This is deliberately not real security (the runtime can still read
|
|
7
|
+
* `process.env`); it only stops accidental disclosure through the editor surface.
|
|
8
|
+
*
|
|
9
|
+
* The runtime loader (`resources/loadEnv.ts`) reads `.env` files directly with `dotenv.parse()`
|
|
10
|
+
* and is intentionally NOT routed through here, so it always sees the real values. To keep the
|
|
11
|
+
* key list we report and the values we write in lockstep with what the runtime loads, this module
|
|
12
|
+
* is built around the exact behaviour of the same `dotenv` parser:
|
|
13
|
+
*
|
|
14
|
+
* - keys match `[\w.-]+`, optionally prefixed with `export `;
|
|
15
|
+
* - an unquoted value runs to the first `#` (inline comment) or newline;
|
|
16
|
+
* - single-quoted values are taken literally (only the surrounding quotes are stripped) and may
|
|
17
|
+
* span multiple lines;
|
|
18
|
+
* - double-quoted values are stripped of their quotes and then have `\n`/`\r` expanded — dotenv
|
|
19
|
+
* does NOT un-escape `\"` or `\\`, which constrains how we serialise values below.
|
|
20
|
+
*/
|
|
21
|
+
import { basename } from 'node:path';
|
|
22
|
+
import { parse } from 'dotenv';
|
|
23
|
+
|
|
24
|
+
/** The placeholder substituted for every value in a masked rendering. */
|
|
25
|
+
export const ENV_VALUE_MASK = '********';
|
|
26
|
+
|
|
27
|
+
/** dotenv's accepted key character set. Keys written through the API must match this. */
|
|
28
|
+
export const ENV_KEY_REGEX = /^[\w.-]+$/;
|
|
29
|
+
|
|
30
|
+
// An assignment line: optional indentation + optional `export ` prefix, a key, `=`, then the raw
|
|
31
|
+
// value text. The value may begin a quoted region that continues onto subsequent lines.
|
|
32
|
+
const ASSIGNMENT_LINE = /^(\s*(?:export\s+)?)([\w.-]+)\s*=(.*)$/;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* True for `.env` and `.env.<suffix>` (e.g. `.env.local`), matched by basename at any depth.
|
|
36
|
+
* Case-insensitive: a protection feature should err toward over-matching (e.g. catch `.ENV` on a
|
|
37
|
+
* case-insensitive filesystem) rather than let a secret slip through on a casing technicality.
|
|
38
|
+
*/
|
|
39
|
+
export function isEnvFile(file: string): boolean {
|
|
40
|
+
if (!file) return false;
|
|
41
|
+
const base = basename(file).toLowerCase();
|
|
42
|
+
return base === '.env' || base.startsWith('.env.');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Template/example env files conventionally hold placeholders, not real secrets, so they are NOT
|
|
46
|
+
// protected — editors may read and write them verbatim like any other file. Matched by suffix.
|
|
47
|
+
const EXAMPLE_ENV_SUFFIXES = ['.example', '.sample', '.template'];
|
|
48
|
+
|
|
49
|
+
/** True for non-secret template env files: `.env.example`, `.env.sample`, `.env.template`, … */
|
|
50
|
+
export function isExampleEnvFile(file: string): boolean {
|
|
51
|
+
if (!isEnvFile(file)) return false;
|
|
52
|
+
const base = basename(file).toLowerCase();
|
|
53
|
+
return EXAMPLE_ENV_SUFFIXES.some((suffix) => base.endsWith(suffix));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** True for `.env*` files whose values should be masked/guarded — i.e. excluding template files. */
|
|
57
|
+
export function isProtectedEnvFile(file: string): boolean {
|
|
58
|
+
return isEnvFile(file) && !isExampleEnvFile(file);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The key names of an env file, in file order, de-duplicated — exactly what the runtime loads. */
|
|
62
|
+
export function parseEnvKeys(text: string): string[] {
|
|
63
|
+
if (!text) return [];
|
|
64
|
+
return Object.keys(parse(text));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** A value-free rendering of an env file: one `KEY=********` line per key. */
|
|
68
|
+
export function renderMaskedEnv(keys: string[]): string {
|
|
69
|
+
if (!keys || keys.length === 0) return '';
|
|
70
|
+
return keys.map((key) => `${key}=${ENV_VALUE_MASK}`).join('\n') + '\n';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Marker prefix for a value encrypted with the cluster env-secrets public key. The bytes after the
|
|
74
|
+
// prefix are an opaque envelope (see docs/env-secret-encryption.md) that only the Pro env-secrets
|
|
75
|
+
// component can decrypt; core just recognises the prefix and delegates to a registered decryptor.
|
|
76
|
+
export const ENV_ENCRYPTED_PREFIX = 'enc:v1:';
|
|
77
|
+
|
|
78
|
+
/** True if a parsed env value is an `enc:v1:` ciphertext envelope rather than a plaintext value. */
|
|
79
|
+
export function isEncryptedEnvValue(value: unknown): boolean {
|
|
80
|
+
return typeof value === 'string' && value.startsWith(ENV_ENCRYPTED_PREFIX);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Serialise a value so that `dotenv.parse` recovers it verbatim.
|
|
85
|
+
*
|
|
86
|
+
* dotenv only *expands* escapes inside double quotes (`\n`, `\r`) and never un-escapes `\"`/`\\`,
|
|
87
|
+
* so single quotes — which it treats literally — are the most faithful container:
|
|
88
|
+
* - no special chars -> bare;
|
|
89
|
+
* - no single quote -> single-quoted (handles spaces, `#`, `"`, `\`, newlines);
|
|
90
|
+
* - single quote but no `"`/`\`/`\r` -> double-quoted with newlines encoded as `\n`;
|
|
91
|
+
* - both quote styles -> unrepresentable; throws.
|
|
92
|
+
*/
|
|
93
|
+
export function formatEnvValue(value: string): string {
|
|
94
|
+
if (value === '') return '';
|
|
95
|
+
// Bare is safe only without whitespace, `#` (would begin an inline comment), or any quote
|
|
96
|
+
// character. Backslash and `=` stay literal in unquoted values, so they don't force quoting.
|
|
97
|
+
if (!/[\s#'"`]/.test(value)) return value;
|
|
98
|
+
if (!value.includes("'")) return `'${value}'`;
|
|
99
|
+
if (!value.includes('"') && !value.includes('\\') && !value.includes('\r')) {
|
|
100
|
+
return `"${value.replace(/\n/g, '\\n')}"`;
|
|
101
|
+
}
|
|
102
|
+
throw new Error('Environment value contains an unsupported combination of quote characters');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Does `s` contain an unescaped `quote`? A backslash escapes the following character, but only for
|
|
106
|
+
// double-quoted values — dotenv treats single-quoted and backtick-quoted values literally, so a
|
|
107
|
+
// value ending in `\` (e.g. a Windows path `'C:\Users\name\'`) still closes at that quote.
|
|
108
|
+
function closesQuote(s: string, quote: string): boolean {
|
|
109
|
+
const backslashEscapes = quote !== "'" && quote !== '`';
|
|
110
|
+
for (let i = 0; i < s.length; i++) {
|
|
111
|
+
if (backslashEscapes && s[i] === '\\') {
|
|
112
|
+
i++;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (s[i] === quote) return true;
|
|
116
|
+
}
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Index of the last line occupied by the value that starts at `lines[startIdx]`. Unquoted values
|
|
121
|
+
// are single-line; quoted values extend until their matching quote closes.
|
|
122
|
+
function valueEndLine(lines: string[], startIdx: number, rawValue: string): number {
|
|
123
|
+
const trimmed = rawValue.replace(/^\s+/, '');
|
|
124
|
+
const quote = trimmed[0];
|
|
125
|
+
if (quote !== '"' && quote !== "'" && quote !== '`') return startIdx;
|
|
126
|
+
if (closesQuote(trimmed.slice(1), quote)) return startIdx;
|
|
127
|
+
for (let j = startIdx + 1; j < lines.length; j++) {
|
|
128
|
+
if (closesQuote(lines[j], quote)) return j;
|
|
129
|
+
}
|
|
130
|
+
return lines.length - 1; // unterminated — treat the remainder of the file as the value
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function toMap(updates: Record<string, string> | Map<string, string>): Map<string, string> {
|
|
134
|
+
return updates instanceof Map ? new Map(updates) : new Map(Object.entries(updates));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
interface ScanState {
|
|
138
|
+
eol: string;
|
|
139
|
+
endedWithNewline: boolean;
|
|
140
|
+
lines: string[];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function beginScan(text: string): ScanState {
|
|
144
|
+
const eol = text.includes('\r\n') ? '\r\n' : '\n';
|
|
145
|
+
const endedWithNewline = /\r?\n$/.test(text);
|
|
146
|
+
const lines = text.length ? text.split(/\r?\n/) : [];
|
|
147
|
+
if (endedWithNewline) lines.pop(); // drop the empty element the trailing newline produces
|
|
148
|
+
return { eol, endedWithNewline, lines };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Insert or update the given keys, leaving every other line — comments, blank lines, untouched
|
|
153
|
+
* keys, and their formatting — exactly as it was. Existing keys are replaced in place (a duplicate
|
|
154
|
+
* later assignment of an updated key is dropped so the new value wins, as dotenv keeps the last);
|
|
155
|
+
* new keys are appended. The file is created from empty text transparently.
|
|
156
|
+
*/
|
|
157
|
+
export function upsertEnvValues(text: string, updates: Record<string, string> | Map<string, string>): string {
|
|
158
|
+
const pending = toMap(updates);
|
|
159
|
+
const applied = new Set<string>();
|
|
160
|
+
const { eol, endedWithNewline, lines } = beginScan(text);
|
|
161
|
+
|
|
162
|
+
const out: string[] = [];
|
|
163
|
+
for (let i = 0; i < lines.length; i++) {
|
|
164
|
+
const match = ASSIGNMENT_LINE.exec(lines[i]);
|
|
165
|
+
if (match) {
|
|
166
|
+
const [, prefix, key] = match;
|
|
167
|
+
const end = valueEndLine(lines, i, match[3]);
|
|
168
|
+
if (pending.has(key)) {
|
|
169
|
+
if (!applied.has(key)) {
|
|
170
|
+
out.push(`${prefix}${key}=${formatEnvValue(pending.get(key)!)}`);
|
|
171
|
+
applied.add(key);
|
|
172
|
+
}
|
|
173
|
+
// otherwise drop the duplicate assignment of an already-updated key
|
|
174
|
+
} else {
|
|
175
|
+
for (let j = i; j <= end; j++) out.push(lines[j]);
|
|
176
|
+
}
|
|
177
|
+
i = end;
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
out.push(lines[i]);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
let appended = false;
|
|
184
|
+
for (const [key, value] of pending) {
|
|
185
|
+
if (applied.has(key)) continue;
|
|
186
|
+
out.push(`${key}=${formatEnvValue(value)}`);
|
|
187
|
+
appended = true;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
let result = out.join(eol);
|
|
191
|
+
if (result.length > 0 && (endedWithNewline || appended)) result += eol;
|
|
192
|
+
return result;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** Remove the given keys (and any continuation lines of multi-line values), leaving the rest. */
|
|
196
|
+
export function removeEnvKeys(text: string, keys: string | string[]): string {
|
|
197
|
+
const removeSet = new Set(Array.isArray(keys) ? keys : [keys]);
|
|
198
|
+
if (removeSet.size === 0 || !text) return text;
|
|
199
|
+
const { eol, endedWithNewline, lines } = beginScan(text);
|
|
200
|
+
|
|
201
|
+
const out: string[] = [];
|
|
202
|
+
for (let i = 0; i < lines.length; i++) {
|
|
203
|
+
const match = ASSIGNMENT_LINE.exec(lines[i]);
|
|
204
|
+
if (match) {
|
|
205
|
+
const end = valueEndLine(lines, i, match[3]);
|
|
206
|
+
if (!removeSet.has(match[2])) {
|
|
207
|
+
for (let j = i; j <= end; j++) out.push(lines[j]);
|
|
208
|
+
}
|
|
209
|
+
i = end;
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
out.push(lines[i]);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
let result = out.join(eol);
|
|
216
|
+
if (result.length > 0 && endedWithNewline) result += eol;
|
|
217
|
+
return result;
|
|
218
|
+
}
|
package/utility/hdbTerms.ts
CHANGED
|
@@ -179,6 +179,7 @@ export const SYSTEM_TABLE_NAMES = {
|
|
|
179
179
|
INFO_TABLE_NAME: 'hdb_info',
|
|
180
180
|
DEPLOYMENT_TABLE_NAME: 'hdb_deployment',
|
|
181
181
|
AGENT_SESSION_TABLE_NAME: 'hdb_agent_session',
|
|
182
|
+
SECRET_TABLE_NAME: 'hdb_secret',
|
|
182
183
|
} as const;
|
|
183
184
|
|
|
184
185
|
/** Hash attribute for the system info table */
|
|
@@ -278,6 +279,9 @@ export const OPERATIONS_ENUM = {
|
|
|
278
279
|
GET_COMPONENTS: 'get_components',
|
|
279
280
|
GET_COMPONENT_FILE: 'get_component_file',
|
|
280
281
|
SET_COMPONENT_FILE: 'set_component_file',
|
|
282
|
+
GET_ENV_KEYS: 'get_env_keys',
|
|
283
|
+
SET_ENV_VALUE: 'set_env_value',
|
|
284
|
+
DELETE_ENV_VALUE: 'delete_env_value',
|
|
281
285
|
DROP_COMPONENT: 'drop_component',
|
|
282
286
|
DROP_CUSTOM_FUNCTION: 'drop_custom_function',
|
|
283
287
|
ADD_CUSTOM_FUNCTION_PROJECT: 'add_custom_function_project',
|
|
@@ -301,6 +305,12 @@ export const OPERATIONS_ENUM = {
|
|
|
301
305
|
CLEAR_STATUS: 'clear_status',
|
|
302
306
|
LIST_DEPLOYMENTS: 'list_deployments',
|
|
303
307
|
GET_DEPLOYMENT: 'get_deployment',
|
|
308
|
+
SET_SECRET: 'set_secret',
|
|
309
|
+
GRANT_SECRET: 'grant_secret',
|
|
310
|
+
REVOKE_SECRET: 'revoke_secret',
|
|
311
|
+
LIST_SECRETS: 'list_secrets',
|
|
312
|
+
DELETE_SECRET: 'delete_secret',
|
|
313
|
+
GET_SECRETS_PUBLIC_KEY: 'get_secrets_public_key',
|
|
304
314
|
GET_DEPLOYMENT_PAYLOAD: 'get_deployment_payload',
|
|
305
315
|
DELETE_DEPLOYMENT_PAYLOAD: 'delete_deployment_payload',
|
|
306
316
|
AGENT_PROMPT: 'agent_prompt',
|
|
@@ -550,6 +560,11 @@ export const CONFIG_PARAMS = {
|
|
|
550
560
|
MCP_OPERATIONS_RATELIMIT_PERTOOLBURST: 'mcp_operations_rateLimit_perToolBurst',
|
|
551
561
|
MCP_OPERATIONS_RATELIMIT_SESSIONCONCURRENCY: 'mcp_operations_rateLimit_sessionConcurrency',
|
|
552
562
|
MCP_OPERATIONS_RATELIMIT_SESSIONPERSECOND: 'mcp_operations_rateLimit_sessionPerSecond',
|
|
563
|
+
MCP_OPERATIONS_RATELIMIT_PERCLIENTPERSECOND: 'mcp_operations_rateLimit_perClientPerSecond',
|
|
564
|
+
MCP_OPERATIONS_RATELIMIT_PERCLIENTBURST: 'mcp_operations_rateLimit_perClientBurst',
|
|
565
|
+
MCP_OPERATIONS_RATELIMIT_IDENTITYHEADER: 'mcp_operations_rateLimit_identityHeader',
|
|
566
|
+
MCP_OPERATIONS_QUOTA_RESOURCE: 'mcp_operations_quota_resource',
|
|
567
|
+
MCP_OPERATIONS_QUOTA_METHOD: 'mcp_operations_quota_method',
|
|
553
568
|
MCP_APPLICATION_MOUNTPATH: 'mcp_application_mountPath',
|
|
554
569
|
MCP_APPLICATION_ALLOW: 'mcp_application_allow',
|
|
555
570
|
MCP_APPLICATION_DENY: 'mcp_application_deny',
|
|
@@ -559,6 +574,11 @@ export const CONFIG_PARAMS = {
|
|
|
559
574
|
MCP_APPLICATION_RATELIMIT_PERTOOLBURST: 'mcp_application_rateLimit_perToolBurst',
|
|
560
575
|
MCP_APPLICATION_RATELIMIT_SESSIONCONCURRENCY: 'mcp_application_rateLimit_sessionConcurrency',
|
|
561
576
|
MCP_APPLICATION_RATELIMIT_SESSIONPERSECOND: 'mcp_application_rateLimit_sessionPerSecond',
|
|
577
|
+
MCP_APPLICATION_RATELIMIT_PERCLIENTPERSECOND: 'mcp_application_rateLimit_perClientPerSecond',
|
|
578
|
+
MCP_APPLICATION_RATELIMIT_PERCLIENTBURST: 'mcp_application_rateLimit_perClientBurst',
|
|
579
|
+
MCP_APPLICATION_RATELIMIT_IDENTITYHEADER: 'mcp_application_rateLimit_identityHeader',
|
|
580
|
+
MCP_APPLICATION_QUOTA_RESOURCE: 'mcp_application_quota_resource',
|
|
581
|
+
MCP_APPLICATION_QUOTA_METHOD: 'mcp_application_quota_method',
|
|
562
582
|
MCP_SESSION_IDLETIMEOUTSECONDS: 'mcp_session_idleTimeoutSeconds',
|
|
563
583
|
MCP_SESSION_ALLOWCLIENTDELETE: 'mcp_session_allowClientDelete',
|
|
564
584
|
AGENT_ENABLED: 'agent_enabled',
|
|
@@ -866,9 +886,20 @@ export const ITC_EVENT_TYPES = {
|
|
|
866
886
|
COMPONENT_STATUS_RESPONSE: 'component_status_response',
|
|
867
887
|
RESOURCE_OPENAPI_REQUEST: 'resource_openapi_request',
|
|
868
888
|
RESOURCE_OPENAPI_RESPONSE: 'resource_openapi_response',
|
|
889
|
+
// Main thread asks an HTTP worker for its resolved middleware chains (#1573); app HTTP
|
|
890
|
+
// middleware is only registered on worker threads, so get_status must fetch it from one.
|
|
891
|
+
MIDDLEWARE_CHAINS_REQUEST: 'middleware_chains_request',
|
|
892
|
+
MIDDLEWARE_CHAINS_RESPONSE: 'middleware_chains_response',
|
|
869
893
|
// MCP §3.7: route a client's response to a server→client request back to the
|
|
870
894
|
// worker awaiting it (the response POST can land on any worker).
|
|
871
895
|
MCP_CLIENT_RESPONSE: 'mcp_client_response',
|
|
896
|
+
// #1736: components load per-worker, so a `server.registerOperation()` made there lands in
|
|
897
|
+
// a worker-local OPERATION_FUNCTION_MAP the main-thread ops-API dispatcher can't see. A
|
|
898
|
+
// worker announces each registration (OPERATION_REGISTERED) so the main thread can forward
|
|
899
|
+
// an unrecognized operation to one registering worker for execution (REQUEST/RESPONSE).
|
|
900
|
+
OPERATION_REGISTERED: 'operation_registered',
|
|
901
|
+
OPERATION_EXECUTE_REQUEST: 'operation_execute_request',
|
|
902
|
+
OPERATION_EXECUTE_RESPONSE: 'operation_execute_response',
|
|
872
903
|
} as const;
|
|
873
904
|
|
|
874
905
|
/** Supported thread types */
|
|
@@ -12,6 +12,9 @@ import * as os from 'os';
|
|
|
12
12
|
import { PACKAGE_ROOT } from '../../utility/packageUtils.js';
|
|
13
13
|
import { _assignPackageExport } from '../../globals.js';
|
|
14
14
|
import { Console } from 'console';
|
|
15
|
+
import { inspect, types } from 'node:util';
|
|
16
|
+
|
|
17
|
+
const { isNativeError } = types;
|
|
15
18
|
// store the native write function so we can call it after we write to the log file (and store it on process.stdout
|
|
16
19
|
// because unit tests will create multiple instances of this module)
|
|
17
20
|
let nativeStdWrite = process.env.IS_SCRIPTED_SERVICE
|
|
@@ -180,6 +183,43 @@ async function updateLogSettings() {
|
|
|
180
183
|
}
|
|
181
184
|
}
|
|
182
185
|
|
|
186
|
+
/**
|
|
187
|
+
* True when the argument is an Error (same-realm or native cross-realm). The try/catch guards
|
|
188
|
+
* exotic objects whose prototype is unreachable (e.g. a revoked Proxy, where `instanceof`
|
|
189
|
+
* throws) — the logger must never throw on any input, and util.format renders those fine raw.
|
|
190
|
+
*/
|
|
191
|
+
function isErrorLike(arg: any): boolean {
|
|
192
|
+
try {
|
|
193
|
+
return arg instanceof Error || isNativeError(arg);
|
|
194
|
+
} catch {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Replaces every Error argument with its log-safe errorForLog wrapper before the args reach
|
|
201
|
+
* Console's util.inspect formatting, which would otherwise dump the error's own-enumerable
|
|
202
|
+
* properties — where libraries and app code stash credentials (axios config headers, an
|
|
203
|
+
* hdb_secret for an outbound Authorization header) — into hdb.log (see #1734 and errorForLog).
|
|
204
|
+
* Called inside each level gate so filtered-out log calls pay nothing beyond the arg scan,
|
|
205
|
+
* and only allocates when an Error is actually present. Deliberately shallow: an Error nested
|
|
206
|
+
* inside a logged object/array is not rewritten (deep-walking every logged structure is not
|
|
207
|
+
* worth the per-call cost, and the #1734 threat is raw thrown errors).
|
|
208
|
+
*/
|
|
209
|
+
function sanitizeErrorArgs(args: any[]) {
|
|
210
|
+
for (let i = 0; i < args.length; i++) {
|
|
211
|
+
if (isErrorLike(args[i])) {
|
|
212
|
+
const sanitized = args.slice(0, i);
|
|
213
|
+
for (let j = i; j < args.length; j++) {
|
|
214
|
+
const arg = args[j];
|
|
215
|
+
sanitized[j] = isErrorLike(arg) ? errorForLog(arg) : arg;
|
|
216
|
+
}
|
|
217
|
+
return sanitized;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return args;
|
|
221
|
+
}
|
|
222
|
+
|
|
183
223
|
class HarperLogger extends Console {
|
|
184
224
|
[key: string]: any;
|
|
185
225
|
constructor(streams, level) {
|
|
@@ -193,35 +233,35 @@ class HarperLogger extends Console {
|
|
|
193
233
|
trace(...args) {
|
|
194
234
|
currentLevel = 'trace';
|
|
195
235
|
if (this.level <= LOG_LEVEL_HIERARCHY.trace) {
|
|
196
|
-
super.info(...args);
|
|
236
|
+
super.info(...sanitizeErrorArgs(args));
|
|
197
237
|
}
|
|
198
238
|
currentLevel = 'info';
|
|
199
239
|
}
|
|
200
240
|
debug(...args) {
|
|
201
241
|
currentLevel = 'debug';
|
|
202
242
|
if (this.level <= LOG_LEVEL_HIERARCHY.debug) {
|
|
203
|
-
super.info(...args);
|
|
243
|
+
super.info(...sanitizeErrorArgs(args));
|
|
204
244
|
}
|
|
205
245
|
currentLevel = 'info';
|
|
206
246
|
}
|
|
207
247
|
info(...args) {
|
|
208
248
|
currentLevel = 'info';
|
|
209
249
|
if (this.level <= LOG_LEVEL_HIERARCHY.info) {
|
|
210
|
-
super.info(...args);
|
|
250
|
+
super.info(...sanitizeErrorArgs(args));
|
|
211
251
|
}
|
|
212
252
|
currentLevel = 'info';
|
|
213
253
|
}
|
|
214
254
|
warn(...args) {
|
|
215
255
|
currentLevel = 'warn';
|
|
216
256
|
if (this.level <= LOG_LEVEL_HIERARCHY.warn) {
|
|
217
|
-
super.warn(...args);
|
|
257
|
+
super.warn(...sanitizeErrorArgs(args));
|
|
218
258
|
}
|
|
219
259
|
currentLevel = 'info';
|
|
220
260
|
}
|
|
221
261
|
error(...args) {
|
|
222
262
|
currentLevel = 'error';
|
|
223
263
|
if (this.level <= LOG_LEVEL_HIERARCHY.error) {
|
|
224
|
-
super.error(...args);
|
|
264
|
+
super.error(...sanitizeErrorArgs(args));
|
|
225
265
|
}
|
|
226
266
|
currentLevel = 'info';
|
|
227
267
|
}
|
|
@@ -230,7 +270,7 @@ class HarperLogger extends Console {
|
|
|
230
270
|
try {
|
|
231
271
|
currentLevel = 'fatal';
|
|
232
272
|
if (this.level <= LOG_LEVEL_HIERARCHY.fatal) {
|
|
233
|
-
super.error(...args);
|
|
273
|
+
super.error(...sanitizeErrorArgs(args));
|
|
234
274
|
}
|
|
235
275
|
currentLevel = 'info';
|
|
236
276
|
} finally {
|
|
@@ -242,13 +282,25 @@ class HarperLogger extends Console {
|
|
|
242
282
|
try {
|
|
243
283
|
currentLevel = 'notify';
|
|
244
284
|
if (this.level <= LOG_LEVEL_HIERARCHY.notify) {
|
|
245
|
-
super.info(...args);
|
|
285
|
+
super.info(...sanitizeErrorArgs(args));
|
|
246
286
|
}
|
|
247
287
|
currentLevel = 'info';
|
|
248
288
|
} finally {
|
|
249
289
|
logImmediately = false;
|
|
250
290
|
}
|
|
251
291
|
}
|
|
292
|
+
// Inherited Console methods that format arbitrary values would bypass sanitizeErrorArgs —
|
|
293
|
+
// guard them too so logger.log(error) / dir / table can't leak either (#1734). Their
|
|
294
|
+
// existing semantics (no level gate) are preserved; only the Error args are wrapped.
|
|
295
|
+
log(...args) {
|
|
296
|
+
super.log(...sanitizeErrorArgs(args));
|
|
297
|
+
}
|
|
298
|
+
dir(item, options?) {
|
|
299
|
+
super.dir(isErrorLike(item) ? errorForLog(item) : item, options);
|
|
300
|
+
}
|
|
301
|
+
table(data, columns?) {
|
|
302
|
+
super.table(Array.isArray(data) ? sanitizeErrorArgs(data) : isErrorLike(data) ? errorForLog(data) : data, columns);
|
|
303
|
+
}
|
|
252
304
|
withTag(tag) {
|
|
253
305
|
return loggerWithTag(tag, true, this);
|
|
254
306
|
}
|
|
@@ -291,6 +343,7 @@ module.exports = {
|
|
|
291
343
|
start: updateLogSettings,
|
|
292
344
|
startOnMainThread: updateLogSettings,
|
|
293
345
|
errorToString,
|
|
346
|
+
errorForLog,
|
|
294
347
|
disableStdio,
|
|
295
348
|
externalLogger,
|
|
296
349
|
};
|
|
@@ -892,7 +945,101 @@ function getDefaultConfig() {
|
|
|
892
945
|
* @return {string|string}
|
|
893
946
|
*/
|
|
894
947
|
export function errorToString(error: any) {
|
|
895
|
-
|
|
948
|
+
if (error == null) return String(error);
|
|
949
|
+
try {
|
|
950
|
+
return typeof error.message === 'string' ? `${error.constructor.name}: ${error.message}` : error.toString();
|
|
951
|
+
} catch {
|
|
952
|
+
// error is hostile (e.g. a revoked Proxy, or a getter that throws) - this must never throw,
|
|
953
|
+
// since it's called directly for response bodies (REST.ts/http.ts/JSONStream) as well as here.
|
|
954
|
+
try {
|
|
955
|
+
return Object.prototype.toString.call(error);
|
|
956
|
+
} catch {
|
|
957
|
+
return '[Unrenderable Object]';
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// Own-enumerable Error properties considered safe to surface in logs — common diagnostic fields
|
|
963
|
+
// (HTTP status, Node error codes) that libraries and app code don't use to carry secrets, unlike
|
|
964
|
+
// arbitrary properties (axios' `config`/`request` with an Authorization header), which stay
|
|
965
|
+
// excluded. `path` is deliberately omitted: it can reveal internal filesystem layout, and the
|
|
966
|
+
// message/stack already names the failing operation.
|
|
967
|
+
const LOGGABLE_ERROR_PROPS = ['code', 'status', 'statusCode', 'errno', 'syscall'];
|
|
968
|
+
|
|
969
|
+
function loggablePropsSuffix(error: any): string {
|
|
970
|
+
if (typeof error !== 'object' || error === null) return '';
|
|
971
|
+
let suffix = '';
|
|
972
|
+
for (const key of LOGGABLE_ERROR_PROPS) {
|
|
973
|
+
try {
|
|
974
|
+
const value = error[key];
|
|
975
|
+
if (value !== undefined) suffix += ` ${key}=${value}`;
|
|
976
|
+
} catch {
|
|
977
|
+
// A hostile property (revoked Proxy, throwing getter) must not crash the logger - skip it.
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
return suffix;
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
function renderErrorLine(error: any): string {
|
|
984
|
+
try {
|
|
985
|
+
const base = typeof error?.stack === 'string' ? error.stack : errorToString(error);
|
|
986
|
+
return base + loggablePropsSuffix(error);
|
|
987
|
+
} catch (err) {
|
|
988
|
+
// error?.stack itself can throw on a hostile object even though errorToString cannot.
|
|
989
|
+
return `[Unrenderable Error: ${err instanceof Error ? err.message : String(err)}]`;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
/**
|
|
994
|
+
* Renders an error to its stack (class name + message + frames) plus a small allowlist of
|
|
995
|
+
* diagnostic properties (see `LOGGABLE_ERROR_PROPS`), then appends the same for each error in its
|
|
996
|
+
* `cause` chain. Deliberately excludes every OTHER own-enumerable property — those are exactly
|
|
997
|
+
* what leaks secrets in #1734 (see `errorForLog`).
|
|
998
|
+
*
|
|
999
|
+
* The `cause` chain is not under this module's control (any code that threw the outer error could
|
|
1000
|
+
* have attached a hostile `cause` - a revoked Proxy, an object with a throwing getter), so every
|
|
1001
|
+
* step of the walk is defensive: this function must never throw regardless of what it's given.
|
|
1002
|
+
*/
|
|
1003
|
+
function errorToLogString(error: any) {
|
|
1004
|
+
if (error == null) return String(error);
|
|
1005
|
+
let output = renderErrorLine(error);
|
|
1006
|
+
const seen = new Set([error]);
|
|
1007
|
+
let cause: any;
|
|
1008
|
+
try {
|
|
1009
|
+
cause = error.cause;
|
|
1010
|
+
} catch {
|
|
1011
|
+
return output;
|
|
1012
|
+
}
|
|
1013
|
+
while (cause != null && !seen.has(cause)) {
|
|
1014
|
+
seen.add(cause);
|
|
1015
|
+
output += `\ncaused by: ${renderErrorLine(cause)}`;
|
|
1016
|
+
try {
|
|
1017
|
+
cause = cause.cause;
|
|
1018
|
+
} catch {
|
|
1019
|
+
break;
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
return output;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
/**
|
|
1026
|
+
* Returns a log-safe representation of an error for passing to the logger. It renders the stack
|
|
1027
|
+
* (class name + message + frames) plus any `cause` chain, but NOT the error's own-enumerable
|
|
1028
|
+
* properties.
|
|
1029
|
+
*
|
|
1030
|
+
* This deliberately avoids logging the raw Error object: Node's Console formats a logged Error with
|
|
1031
|
+
* util.inspect, which dumps every own-enumerable property. Anything an app or an HTTP client library
|
|
1032
|
+
* stashes on a thrown Error — a credential used for an outbound Authorization header, an axios
|
|
1033
|
+
* `config`/`request` with headers — would otherwise land verbatim in hdb.log (see #1734). Those
|
|
1034
|
+
* custom properties are not part of the stack, so this preserves debuggability without leaking them.
|
|
1035
|
+
*
|
|
1036
|
+
* A wrapper carrying the rendering on `util.inspect.custom` is returned rather than a pre-built
|
|
1037
|
+
* string so the (potentially expensive) stack materialization only happens if the logger's level
|
|
1038
|
+
* gate actually writes the entry — passing a raw string would force it eagerly on the discarded path.
|
|
1039
|
+
*/
|
|
1040
|
+
export function errorForLog(error: any) {
|
|
1041
|
+
const render = () => errorToLogString(error);
|
|
1042
|
+
return { [inspect.custom]: render, toString: render };
|
|
896
1043
|
}
|
|
897
1044
|
|
|
898
1045
|
export function setMainLogger(logger: any) {
|
|
@@ -953,4 +1100,5 @@ export default {
|
|
|
953
1100
|
externalLogger,
|
|
954
1101
|
AuthAuditLog,
|
|
955
1102
|
errorToString,
|
|
1103
|
+
errorForLog,
|
|
956
1104
|
};
|