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.
Files changed (395) hide show
  1. package/agent/operations.ts +13 -16
  2. package/components/Application.ts +188 -7
  3. package/components/ApplicationScope.ts +3 -0
  4. package/components/Scope.ts +12 -0
  5. package/components/anthropic/index.ts +9 -2
  6. package/components/bedrock/index.ts +25 -2
  7. package/components/componentLoader.ts +56 -7
  8. package/components/componentSecrets.ts +451 -0
  9. package/components/deploymentRecorder.ts +7 -1
  10. package/components/mcp/adapters/fastify.ts +3 -0
  11. package/components/mcp/adapters/harperHttp.ts +3 -0
  12. package/components/mcp/audit.ts +21 -6
  13. package/components/mcp/customResourceRegistry.ts +17 -38
  14. package/components/mcp/listChanged.ts +37 -20
  15. package/components/mcp/quota.ts +145 -0
  16. package/components/mcp/rateLimit.ts +152 -14
  17. package/components/mcp/resources.ts +2 -4
  18. package/components/mcp/toolRegistry.ts +73 -4
  19. package/components/mcp/tools/application.ts +77 -8
  20. package/components/mcp/tools/operations.ts +106 -36
  21. package/components/mcp/tools/schemas/operationDescriptions.ts +4 -4
  22. package/components/mcp/tools/schemas/operations.ts +5 -0
  23. package/components/mcp/transport.ts +55 -4
  24. package/components/ollama/index.ts +6 -2
  25. package/components/openai/index.ts +9 -2
  26. package/components/operations.js +204 -10
  27. package/components/operationsValidation.js +173 -0
  28. package/components/secretOperations.ts +498 -0
  29. package/config/configUtils.ts +25 -1
  30. package/config-app.schema.json +81 -0
  31. package/config-root.schema.json +8 -1
  32. package/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +4 -3
  33. package/dataLayer/readAuditLog.ts +14 -0
  34. package/dataLayer/schemaDescribe.ts +1 -0
  35. package/dist/agent/operations.d.ts +7 -4
  36. package/dist/agent/operations.js +13 -15
  37. package/dist/agent/operations.js.map +1 -1
  38. package/dist/components/Application.d.ts +25 -2
  39. package/dist/components/Application.js +166 -7
  40. package/dist/components/Application.js.map +1 -1
  41. package/dist/components/ApplicationScope.d.ts +2 -0
  42. package/dist/components/ApplicationScope.js +3 -0
  43. package/dist/components/ApplicationScope.js.map +1 -1
  44. package/dist/components/Scope.d.ts +8 -0
  45. package/dist/components/Scope.js +11 -0
  46. package/dist/components/Scope.js.map +1 -1
  47. package/dist/components/anthropic/index.d.ts +4 -1
  48. package/dist/components/anthropic/index.js +4 -2
  49. package/dist/components/anthropic/index.js.map +1 -1
  50. package/dist/components/bedrock/index.d.ts +4 -1
  51. package/dist/components/bedrock/index.js +22 -2
  52. package/dist/components/bedrock/index.js.map +1 -1
  53. package/dist/components/componentLoader.d.ts +2 -0
  54. package/dist/components/componentLoader.js +52 -10
  55. package/dist/components/componentLoader.js.map +1 -1
  56. package/dist/components/componentSecrets.d.ts +63 -0
  57. package/dist/components/componentSecrets.js +405 -0
  58. package/dist/components/componentSecrets.js.map +1 -0
  59. package/dist/components/deploymentRecorder.d.ts +3 -0
  60. package/dist/components/deploymentRecorder.js +4 -3
  61. package/dist/components/deploymentRecorder.js.map +1 -1
  62. package/dist/components/mcp/adapters/fastify.d.ts +2 -0
  63. package/dist/components/mcp/adapters/fastify.js +1 -0
  64. package/dist/components/mcp/adapters/fastify.js.map +1 -1
  65. package/dist/components/mcp/adapters/harperHttp.d.ts +2 -0
  66. package/dist/components/mcp/adapters/harperHttp.js +1 -0
  67. package/dist/components/mcp/adapters/harperHttp.js.map +1 -1
  68. package/dist/components/mcp/audit.d.ts +2 -2
  69. package/dist/components/mcp/audit.js +20 -5
  70. package/dist/components/mcp/audit.js.map +1 -1
  71. package/dist/components/mcp/customResourceRegistry.d.ts +5 -12
  72. package/dist/components/mcp/customResourceRegistry.js +15 -30
  73. package/dist/components/mcp/customResourceRegistry.js.map +1 -1
  74. package/dist/components/mcp/listChanged.d.ts +7 -2
  75. package/dist/components/mcp/listChanged.js +28 -0
  76. package/dist/components/mcp/listChanged.js.map +1 -1
  77. package/dist/components/mcp/quota.d.ts +31 -0
  78. package/dist/components/mcp/quota.js +155 -0
  79. package/dist/components/mcp/quota.js.map +1 -0
  80. package/dist/components/mcp/rateLimit.d.ts +19 -3
  81. package/dist/components/mcp/rateLimit.js +129 -13
  82. package/dist/components/mcp/rateLimit.js.map +1 -1
  83. package/dist/components/mcp/resources.js.map +1 -1
  84. package/dist/components/mcp/toolRegistry.d.ts +39 -1
  85. package/dist/components/mcp/toolRegistry.js +60 -5
  86. package/dist/components/mcp/toolRegistry.js.map +1 -1
  87. package/dist/components/mcp/tools/application.d.ts +8 -1
  88. package/dist/components/mcp/tools/application.js +65 -5
  89. package/dist/components/mcp/tools/application.js.map +1 -1
  90. package/dist/components/mcp/tools/operations.d.ts +15 -3
  91. package/dist/components/mcp/tools/operations.js +99 -38
  92. package/dist/components/mcp/tools/operations.js.map +1 -1
  93. package/dist/components/mcp/tools/schemas/operationDescriptions.js +4 -4
  94. package/dist/components/mcp/tools/schemas/operationDescriptions.js.map +1 -1
  95. package/dist/components/mcp/tools/schemas/operations.js +4 -0
  96. package/dist/components/mcp/tools/schemas/operations.js.map +1 -1
  97. package/dist/components/mcp/transport.d.ts +6 -0
  98. package/dist/components/mcp/transport.js +47 -3
  99. package/dist/components/mcp/transport.js.map +1 -1
  100. package/dist/components/ollama/index.d.ts +4 -1
  101. package/dist/components/ollama/index.js +4 -2
  102. package/dist/components/ollama/index.js.map +1 -1
  103. package/dist/components/openai/index.d.ts +4 -1
  104. package/dist/components/openai/index.js +4 -2
  105. package/dist/components/openai/index.js.map +1 -1
  106. package/dist/components/operations.d.ts +39 -0
  107. package/dist/components/operations.js +194 -2
  108. package/dist/components/operations.js.map +1 -1
  109. package/dist/components/operationsValidation.d.ts +38 -0
  110. package/dist/components/operationsValidation.js +155 -0
  111. package/dist/components/operationsValidation.js.map +1 -1
  112. package/dist/components/secretOperations.d.ts +113 -0
  113. package/dist/components/secretOperations.js +475 -0
  114. package/dist/components/secretOperations.js.map +1 -0
  115. package/dist/config/configUtils.d.ts +4 -1
  116. package/dist/config/configUtils.js +18 -1
  117. package/dist/config/configUtils.js.map +1 -1
  118. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +4 -3
  119. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js.map +1 -1
  120. package/dist/dataLayer/readAuditLog.js +11 -0
  121. package/dist/dataLayer/readAuditLog.js.map +1 -1
  122. package/dist/dataLayer/schemaDescribe.js +2 -0
  123. package/dist/dataLayer/schemaDescribe.js.map +1 -1
  124. package/dist/globals.d.ts +1 -1
  125. package/dist/globals.js +1 -0
  126. package/dist/globals.js.map +1 -1
  127. package/dist/index.d.ts +3 -0
  128. package/dist/index.js +1 -0
  129. package/dist/index.js.map +1 -1
  130. package/dist/json/systemSchema.json +38 -0
  131. package/dist/resources/DatabaseTransaction.d.ts +31 -3
  132. package/dist/resources/DatabaseTransaction.js +190 -29
  133. package/dist/resources/DatabaseTransaction.js.map +1 -1
  134. package/dist/resources/LMDBTransaction.d.ts +1 -1
  135. package/dist/resources/LMDBTransaction.js +43 -5
  136. package/dist/resources/LMDBTransaction.js.map +1 -1
  137. package/dist/resources/PrimaryRocksDatabase.d.ts +51 -0
  138. package/dist/resources/PrimaryRocksDatabase.js +194 -0
  139. package/dist/resources/PrimaryRocksDatabase.js.map +1 -0
  140. package/dist/resources/RecordEncoder.d.ts +1 -0
  141. package/dist/resources/RecordEncoder.js +81 -75
  142. package/dist/resources/RecordEncoder.js.map +1 -1
  143. package/dist/resources/RequestTarget.d.ts +19 -0
  144. package/dist/resources/RequestTarget.js.map +1 -1
  145. package/dist/resources/Resource.d.ts +0 -1
  146. package/dist/resources/Resource.js +103 -16
  147. package/dist/resources/Resource.js.map +1 -1
  148. package/dist/resources/ResourceInterface.d.ts +6 -2
  149. package/dist/resources/ResourceInterface.js.map +1 -1
  150. package/dist/resources/Resources.js.map +1 -1
  151. package/dist/resources/Table.d.ts +6 -4
  152. package/dist/resources/Table.js +247 -24
  153. package/dist/resources/Table.js.map +1 -1
  154. package/dist/resources/analytics/read.js +25 -28
  155. package/dist/resources/analytics/read.js.map +1 -1
  156. package/dist/resources/blob.js +165 -22
  157. package/dist/resources/blob.js.map +1 -1
  158. package/dist/resources/crdt.d.ts +2 -0
  159. package/dist/resources/crdt.js +45 -36
  160. package/dist/resources/crdt.js.map +1 -1
  161. package/dist/resources/databases.d.ts +1 -0
  162. package/dist/resources/databases.js +134 -7
  163. package/dist/resources/databases.js.map +1 -1
  164. package/dist/resources/graphql.js +8 -0
  165. package/dist/resources/graphql.js.map +1 -1
  166. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +35 -5
  167. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +239 -14
  168. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
  169. package/dist/resources/jsResource.js +6 -0
  170. package/dist/resources/jsResource.js.map +1 -1
  171. package/dist/resources/loadEnv.js +30 -3
  172. package/dist/resources/loadEnv.js.map +1 -1
  173. package/dist/resources/login.js +2 -2
  174. package/dist/resources/login.js.map +1 -1
  175. package/dist/resources/models/Models.js +14 -3
  176. package/dist/resources/models/Models.js.map +1 -1
  177. package/dist/resources/models/embedHook.js +10 -2
  178. package/dist/resources/models/embedHook.js.map +1 -1
  179. package/dist/resources/models/openaiStream.d.ts +56 -0
  180. package/dist/resources/models/openaiStream.js +94 -0
  181. package/dist/resources/models/openaiStream.js.map +1 -0
  182. package/dist/resources/replayLogs.js +4 -3
  183. package/dist/resources/replayLogs.js.map +1 -1
  184. package/dist/resources/search.d.ts +1 -1
  185. package/dist/resources/search.js +104 -11
  186. package/dist/resources/search.js.map +1 -1
  187. package/dist/resources/secretDecryptor.d.ts +54 -0
  188. package/dist/resources/secretDecryptor.js +131 -0
  189. package/dist/resources/secretDecryptor.js.map +1 -0
  190. package/dist/resources/tracked.js +15 -5
  191. package/dist/resources/tracked.js.map +1 -1
  192. package/dist/security/auth.js +71 -8
  193. package/dist/security/auth.js.map +1 -1
  194. package/dist/security/jsLoader.js +11 -0
  195. package/dist/security/jsLoader.js.map +1 -1
  196. package/dist/security/tokenAuthentication.d.ts +9 -1
  197. package/dist/security/tokenAuthentication.js +31 -1
  198. package/dist/security/tokenAuthentication.js.map +1 -1
  199. package/dist/server/DurableSubscriptionsSession.d.ts +1 -1
  200. package/dist/server/DurableSubscriptionsSession.js +8 -4
  201. package/dist/server/DurableSubscriptionsSession.js.map +1 -1
  202. package/dist/server/REST.js +28 -9
  203. package/dist/server/REST.js.map +1 -1
  204. package/dist/server/fastifyRoutes.js +11 -1
  205. package/dist/server/fastifyRoutes.js.map +1 -1
  206. package/dist/server/graphqlQuerying.js +4 -3
  207. package/dist/server/graphqlQuerying.js.map +1 -1
  208. package/dist/server/http.d.ts +40 -2
  209. package/dist/server/http.js +560 -77
  210. package/dist/server/http.js.map +1 -1
  211. package/dist/server/itc/serverHandlers.js +64 -0
  212. package/dist/server/itc/serverHandlers.js.map +1 -1
  213. package/dist/server/liveSubscriptionAuth.d.ts +12 -0
  214. package/dist/server/liveSubscriptionAuth.js +140 -0
  215. package/dist/server/liveSubscriptionAuth.js.map +1 -0
  216. package/dist/server/loadRootComponents.js +2 -1
  217. package/dist/server/loadRootComponents.js.map +1 -1
  218. package/dist/server/middlewareChain.d.ts +44 -1
  219. package/dist/server/middlewareChain.js +86 -10
  220. package/dist/server/middlewareChain.js.map +1 -1
  221. package/dist/server/mqtt.js +2 -1
  222. package/dist/server/mqtt.js.map +1 -1
  223. package/dist/server/operationsServer.js +1 -1
  224. package/dist/server/operationsServer.js.map +1 -1
  225. package/dist/server/serverHelpers/Headers.d.ts +9 -0
  226. package/dist/server/serverHelpers/Headers.js +22 -0
  227. package/dist/server/serverHelpers/Headers.js.map +1 -1
  228. package/dist/server/serverHelpers/JSONStream.js +3 -3
  229. package/dist/server/serverHelpers/JSONStream.js.map +1 -1
  230. package/dist/server/serverHelpers/Request.d.ts +52 -1
  231. package/dist/server/serverHelpers/Request.js +122 -1
  232. package/dist/server/serverHelpers/Request.js.map +1 -1
  233. package/dist/server/serverHelpers/contentTypes.js +3 -8
  234. package/dist/server/serverHelpers/contentTypes.js.map +1 -1
  235. package/dist/server/serverHelpers/progressEmitter.d.ts +19 -0
  236. package/dist/server/serverHelpers/progressEmitter.js +52 -4
  237. package/dist/server/serverHelpers/progressEmitter.js.map +1 -1
  238. package/dist/server/serverHelpers/registeredOperations.d.ts +40 -0
  239. package/dist/server/serverHelpers/registeredOperations.js +286 -0
  240. package/dist/server/serverHelpers/registeredOperations.js.map +1 -0
  241. package/dist/server/serverHelpers/serverHandlers.js +11 -5
  242. package/dist/server/serverHelpers/serverHandlers.js.map +1 -1
  243. package/dist/server/serverHelpers/serverUtilities.d.ts +1 -0
  244. package/dist/server/serverHelpers/serverUtilities.js +67 -5
  245. package/dist/server/serverHelpers/serverUtilities.js.map +1 -1
  246. package/dist/server/serverHelpers/uwsServer.d.ts +80 -0
  247. package/dist/server/serverHelpers/uwsServer.js +433 -0
  248. package/dist/server/serverHelpers/uwsServer.js.map +1 -0
  249. package/dist/server/static.d.ts +0 -15
  250. package/dist/server/static.js +161 -2
  251. package/dist/server/static.js.map +1 -1
  252. package/dist/server/status/index.d.ts +4 -1
  253. package/dist/server/status/index.js +64 -4
  254. package/dist/server/status/index.js.map +1 -1
  255. package/dist/server/threads/manageThreads.d.ts +10 -0
  256. package/dist/server/threads/manageThreads.js +107 -4
  257. package/dist/server/threads/manageThreads.js.map +1 -1
  258. package/dist/server/threads/socketRouter.js +5 -1
  259. package/dist/server/threads/socketRouter.js.map +1 -1
  260. package/dist/server/threads/threadServer.js +92 -14
  261. package/dist/server/threads/threadServer.js.map +1 -1
  262. package/dist/upgrade/directives/5-2-0.d.ts +7 -0
  263. package/dist/upgrade/directives/5-2-0.js +108 -0
  264. package/dist/upgrade/directives/5-2-0.js.map +1 -0
  265. package/dist/upgrade/directives/directivesController.js +2 -1
  266. package/dist/upgrade/directives/directivesController.js.map +1 -1
  267. package/dist/utility/common_utils.js +5 -0
  268. package/dist/utility/common_utils.js.map +1 -1
  269. package/dist/utility/envFile.d.ts +41 -0
  270. package/dist/utility/envFile.js +221 -0
  271. package/dist/utility/envFile.js.map +1 -0
  272. package/dist/utility/globalSchema.d.ts +9 -0
  273. package/dist/utility/hdbTerms.d.ts +25 -0
  274. package/dist/utility/hdbTerms.js +31 -0
  275. package/dist/utility/hdbTerms.js.map +1 -1
  276. package/dist/utility/logging/harper_logger.d.ts +21 -0
  277. package/dist/utility/logging/harper_logger.js +160 -8
  278. package/dist/utility/logging/harper_logger.js.map +1 -1
  279. package/dist/utility/logging/logRotator.js +29 -17
  280. package/dist/utility/logging/logRotator.js.map +1 -1
  281. package/dist/utility/logging/readLog.d.ts +1 -1
  282. package/dist/utility/logging/readLog.js +305 -2
  283. package/dist/utility/logging/readLog.js.map +1 -1
  284. package/dist/utility/operationPermissions.d.ts +18 -0
  285. package/dist/utility/operationPermissions.js +35 -1
  286. package/dist/utility/operationPermissions.js.map +1 -1
  287. package/dist/utility/operation_authorization.d.ts +17 -0
  288. package/dist/utility/operation_authorization.js +38 -0
  289. package/dist/utility/operation_authorization.js.map +1 -1
  290. package/dist/utility/secretEnvelope.d.ts +30 -0
  291. package/dist/utility/secretEnvelope.js +94 -0
  292. package/dist/utility/secretEnvelope.js.map +1 -0
  293. package/dist/utility/signalling.d.ts +7 -0
  294. package/dist/utility/signalling.js +16 -0
  295. package/dist/utility/signalling.js.map +1 -1
  296. package/dist/validation/configValidator.js +70 -24
  297. package/dist/validation/configValidator.js.map +1 -1
  298. package/index.ts +4 -0
  299. package/json/systemSchema.json +38 -0
  300. package/npm-shrinkwrap.json +15066 -9347
  301. package/package.json +13 -8
  302. package/resources/DESIGN.md +15 -12
  303. package/resources/DatabaseTransaction.ts +206 -37
  304. package/resources/LMDBTransaction.ts +43 -9
  305. package/resources/PrimaryRocksDatabase.ts +201 -0
  306. package/resources/RecordEncoder.ts +81 -69
  307. package/resources/RequestTarget.ts +21 -0
  308. package/resources/Resource.ts +109 -25
  309. package/resources/ResourceInterface.ts +6 -2
  310. package/resources/Resources.ts +1 -3
  311. package/resources/Table.ts +238 -29
  312. package/resources/analytics/read.ts +25 -30
  313. package/resources/blob.ts +159 -23
  314. package/resources/crdt.ts +37 -31
  315. package/resources/databases.ts +143 -6
  316. package/resources/graphql.ts +8 -0
  317. package/resources/indexes/HierarchicalNavigableSmallWorld.ts +251 -17
  318. package/resources/jsResource.ts +6 -0
  319. package/resources/loadEnv.ts +34 -3
  320. package/resources/login.ts +2 -2
  321. package/resources/models/Models.ts +19 -3
  322. package/resources/models/embedHook.ts +13 -2
  323. package/resources/models/openaiStream.ts +133 -0
  324. package/resources/replayLogs.ts +4 -3
  325. package/resources/search.ts +113 -11
  326. package/resources/secretDecryptor.ts +159 -0
  327. package/resources/tracked.ts +24 -17
  328. package/security/auth.ts +70 -10
  329. package/security/jsLoader.ts +12 -0
  330. package/security/tokenAuthentication.ts +42 -1
  331. package/server/DESIGN.md +10 -0
  332. package/server/DurableSubscriptionsSession.ts +8 -4
  333. package/server/REST.ts +26 -9
  334. package/server/fastifyRoutes.ts +10 -1
  335. package/server/graphqlQuerying.ts +4 -3
  336. package/server/http.ts +563 -77
  337. package/server/itc/serverHandlers.js +70 -0
  338. package/server/liveSubscriptionAuth.ts +152 -0
  339. package/server/loadRootComponents.js +2 -1
  340. package/server/middlewareChain.ts +107 -17
  341. package/server/mqtt.ts +2 -1
  342. package/server/operationsServer.ts +2 -2
  343. package/server/serverHelpers/Headers.ts +24 -0
  344. package/server/serverHelpers/JSONStream.ts +3 -3
  345. package/server/serverHelpers/Request.ts +127 -0
  346. package/server/serverHelpers/contentTypes.ts +3 -8
  347. package/server/serverHelpers/progressEmitter.ts +55 -5
  348. package/server/serverHelpers/registeredOperations.ts +259 -0
  349. package/server/serverHelpers/serverHandlers.js +11 -5
  350. package/server/serverHelpers/serverUtilities.ts +88 -5
  351. package/server/serverHelpers/uwsServer.ts +488 -0
  352. package/server/static.ts +180 -2
  353. package/server/status/index.ts +72 -4
  354. package/server/threads/manageThreads.js +102 -4
  355. package/server/threads/socketRouter.ts +5 -1
  356. package/server/threads/threadServer.js +93 -10
  357. package/studio/web/assets/{Chat-SdD1EZca.js → Chat-Cv_2paZE.js} +2 -2
  358. package/studio/web/assets/{Chat-SdD1EZca.js.map → Chat-Cv_2paZE.js.map} +1 -1
  359. package/studio/web/assets/{FloatingChat-GMq6tHK9.js → FloatingChat-CPyPIcVR.js} +4 -4
  360. package/studio/web/assets/{FloatingChat-GMq6tHK9.js.map → FloatingChat-CPyPIcVR.js.map} +1 -1
  361. package/studio/web/assets/{applications-B5hhv7uA.js → applications-C0jT2vdZ.js} +2 -2
  362. package/studio/web/assets/{applications-B5hhv7uA.js.map → applications-C0jT2vdZ.js.map} +1 -1
  363. package/studio/web/assets/{index-VFcpNWgq.js → index-D_GKOkhn.js} +6 -6
  364. package/studio/web/assets/index-D_GKOkhn.js.map +1 -0
  365. package/studio/web/assets/{index.lazy-DgcJojIA.js → index.lazy-CAmCIA65.js} +4 -4
  366. package/studio/web/assets/{index.lazy-DgcJojIA.js.map → index.lazy-CAmCIA65.js.map} +1 -1
  367. package/studio/web/assets/{profile-BqN-8vzm.js → profile-CaF-4aZe.js} +2 -2
  368. package/studio/web/assets/{profile-BqN-8vzm.js.map → profile-CaF-4aZe.js.map} +1 -1
  369. package/studio/web/assets/{setComponentFile-CHFvFq9u.js → setComponentFile-DLW1DHCt.js} +2 -2
  370. package/studio/web/assets/{setComponentFile-CHFvFq9u.js.map → setComponentFile-DLW1DHCt.js.map} +1 -1
  371. package/studio/web/assets/{setup-CQ3vWJSr.js → setup-TGCErIhq.js} +2 -2
  372. package/studio/web/assets/{setup-CQ3vWJSr.js.map → setup-TGCErIhq.js.map} +1 -1
  373. package/studio/web/assets/{status-BsW5fjuh.js → status-DG0Maoao.js} +2 -2
  374. package/studio/web/assets/{status-BsW5fjuh.js.map → status-DG0Maoao.js.map} +1 -1
  375. package/studio/web/assets/{swagger-ui-react-DITdeB9-.js → swagger-ui-react-8T4SgQ1m.js} +2 -2
  376. package/studio/web/assets/{swagger-ui-react-DITdeB9-.js.map → swagger-ui-react-8T4SgQ1m.js.map} +1 -1
  377. package/studio/web/assets/{tsMode-Dyph-OUs.js → tsMode-DJ6Sl24Q.js} +2 -2
  378. package/studio/web/assets/{tsMode-Dyph-OUs.js.map → tsMode-DJ6Sl24Q.js.map} +1 -1
  379. package/studio/web/assets/{useEntityRestURL-D0QTUqex.js → useEntityRestURL-JO2mfWTQ.js} +2 -2
  380. package/studio/web/assets/{useEntityRestURL-D0QTUqex.js.map → useEntityRestURL-JO2mfWTQ.js.map} +1 -1
  381. package/studio/web/index.html +1 -1
  382. package/upgrade/directives/5-2-0.ts +82 -0
  383. package/upgrade/directives/directivesController.ts +2 -1
  384. package/utility/common_utils.ts +5 -0
  385. package/utility/envFile.ts +218 -0
  386. package/utility/hdbTerms.ts +31 -0
  387. package/utility/logging/harper_logger.ts +156 -8
  388. package/utility/logging/logRotator.ts +24 -14
  389. package/utility/logging/readLog.ts +323 -2
  390. package/utility/operationPermissions.ts +35 -1
  391. package/utility/operation_authorization.ts +74 -1
  392. package/utility/secretEnvelope.ts +113 -0
  393. package/utility/signalling.ts +15 -0
  394. package/validation/configValidator.ts +78 -25
  395. package/studio/web/assets/index-VFcpNWgq.js.map +0 -1
@@ -16,6 +16,37 @@ export type ProgressListener = (event: ProgressEvent) => void;
16
16
  export class ProgressEmitter {
17
17
  private listeners: ProgressListener[] = [];
18
18
 
19
+ /**
20
+ * Set by {@link createSSEResponseStream} to a signal that aborts when the client
21
+ * disconnects. Open-ended operations (e.g. a live log tail that never returns on its own)
22
+ * read this to stop producing events and resolve, instead of running until process exit.
23
+ */
24
+ signal?: AbortSignal;
25
+
26
+ /**
27
+ * Backpressure signal for open-ended producers. {@link createSSEResponseStream} sets this
28
+ * `true` when the underlying SSE stream's write buffer is full and clears it on `drain`. A
29
+ * producer that can outrun a slow client (e.g. a log tail on a busy file) should await
30
+ * {@link whenWritable} before emitting more, so buffered frames can't grow without bound.
31
+ * Bounded producers (deploy) simply never check it.
32
+ */
33
+ paused = false;
34
+ private drainWaiters: Array<() => void> = [];
35
+
36
+ /** Resolves once the SSE stream can accept more writes (immediately when not paused). */
37
+ whenWritable(): Promise<void> {
38
+ if (!this.paused) return Promise.resolve();
39
+ return new Promise((resolve) => this.drainWaiters.push(resolve));
40
+ }
41
+
42
+ /** Called by the SSE wrapper on `drain` (and on teardown) to release awaiting producers. */
43
+ resume(): void {
44
+ this.paused = false;
45
+ const waiters = this.drainWaiters;
46
+ this.drainWaiters = [];
47
+ for (const waiter of waiters) waiter();
48
+ }
49
+
19
50
  emit(event: string, data: unknown): void {
20
51
  // Snapshot before iteration so a listener that unsubscribes itself during dispatch
21
52
  // doesn't shift indexes underneath us.
@@ -54,19 +85,32 @@ export function createSSEResponseStream(emitter: ProgressEmitter, operation: ()
54
85
  // that consumers ignore, so it's safe filler.
55
86
  stream.write(`: stream open\n\n`);
56
87
 
88
+ // Give the operation a way to observe client disconnect. `cleanup` runs when the stream
89
+ // closes/ends (and after the operation settles); aborting there lets an open-ended
90
+ // operation like a log tail stop and resolve rather than leak until the process exits.
91
+ const abortController = new AbortController();
92
+ emitter.signal = abortController.signal;
93
+
57
94
  let active = true;
58
95
  let errorEmitted = false;
59
96
  const unsubscribe = emitter.subscribe((event) => {
60
97
  if (active) {
61
- writeSSE(stream, event);
98
+ // A `false` return means the stream's buffer is over its high-water mark; flag it so
99
+ // producers that check `whenWritable()` back off until the 'drain' below clears it.
100
+ const canWriteMore = writeSSE(stream, event);
101
+ if (!canWriteMore) emitter.paused = true;
62
102
  if (event.event === 'error') errorEmitted = true;
63
103
  }
64
104
  });
105
+ stream.on('drain', () => emitter.resume());
65
106
 
66
107
  const cleanup = () => {
67
108
  if (active) {
68
109
  active = false;
69
110
  unsubscribe();
111
+ abortController.abort();
112
+ // Release any producer awaiting drain so the operation can settle instead of hanging.
113
+ emitter.resume();
70
114
  }
71
115
  };
72
116
 
@@ -100,11 +144,17 @@ export function createSSEResponseStream(emitter: ProgressEmitter, operation: ()
100
144
  return stream;
101
145
  }
102
146
 
103
- function writeSSE(stream: PassThrough, event: ProgressEvent): void {
147
+ /**
148
+ * Write one SSE record; returns `false` if any of its writes pushed the buffer past its
149
+ * high-water mark (so a dense multi-line payload that tips the buffer on an early `data:`
150
+ * line is still detected, not just the final `\n`). `stream.write(...)` is always the left
151
+ * operand so every line is written regardless of the accumulated backpressure flag.
152
+ */
153
+ function writeSSE(stream: PassThrough, event: ProgressEvent): boolean {
104
154
  const data = typeof event.data === 'string' ? event.data : JSON.stringify(event.data);
105
- stream.write(`event: ${event.event}\n`);
155
+ let canWrite = stream.write(`event: ${event.event}\n`);
106
156
  for (const line of data.split(/\r?\n/)) {
107
- stream.write(`data: ${line}\n`);
157
+ canWrite = stream.write(`data: ${line}\n`) && canWrite;
108
158
  }
109
- stream.write('\n');
159
+ return stream.write('\n') && canWrite;
110
160
  }
@@ -0,0 +1,259 @@
1
+ /**
2
+ * Cross-thread bridge for `server.registerOperation()` (#1736).
3
+ *
4
+ * Components (jsResource `resources.js`, Plugin API `handleApplication`) load per-worker, so a
5
+ * `registerOperation()` call made there lands in that worker's module-local OPERATION_FUNCTION_MAP.
6
+ * The ops-API HTTP dispatcher runs only on the main thread and reads the main thread's copy of
7
+ * that map, so such operations were unreachable ("Operation '<name>' not found") for every caller.
8
+ *
9
+ * The bridge mirrors the RESOURCE_OPENAPI request/response pattern (operationsServer.ts /
10
+ * itc/serverHandlers.js), with one deliberate difference: executing an operation is side-effecting,
11
+ * so a request is sent to exactly ONE registering worker (never broadcast-first-wins).
12
+ *
13
+ * - Worker: `registerOperation()` announces the name (OPERATION_REGISTERED) to all threads;
14
+ * only the main thread records it, as name -> Set<threadId>.
15
+ * - Main: on an OPERATION_FUNCTION_MAP miss, `getRemoteOperationFunction()` supplies a forwarding
16
+ * function that sends the request body (OPERATION_EXECUTE_REQUEST) to one live registering
17
+ * worker and awaits the correlated OPERATION_EXECUTE_RESPONSE.
18
+ * - Worker: executes through the normal `chooseOperation` + `processLocalTransaction` path, so
19
+ * permission checks run where the operation function (and its metadata) actually exists. The
20
+ * main thread performs authentication only and forwards the resolved `hdb_user`.
21
+ */
22
+ import { isMainThread, threadId } from 'node:worker_threads';
23
+ import { Readable } from 'node:stream';
24
+ import * as terms from '../../utility/hdbTerms.ts';
25
+ import * as env from '../../utility/environment/environmentManager.ts';
26
+ import harperLogger from '../../utility/logging/harper_logger.ts';
27
+ import { ServerError } from '../../utility/errors/hdbError.ts';
28
+ import { sendItcEvent } from '../threads/itc.js';
29
+ import { onMessageByType, onThreadExit } from '../threads/manageThreads.js';
30
+
31
+ const operationLog = harperLogger.loggerWithTag('operation');
32
+
33
+ // The connected-ports array with `sendToThread` — assigned to the `threads` global by
34
+ // manageThreads via _assignPackageExport (the same access pattern as itc/serverHandlers.js;
35
+ // the globals.js `exports.threads` binding is reassigned after load, so it can't be imported).
36
+ declare const threads: { sendToThread(threadId: number, message: any): boolean };
37
+
38
+ // Fields attached to a request body by the HTTP/auth layer that must not (and often cannot)
39
+ // cross the thread boundary via structured clone.
40
+ const NON_FORWARDABLE_FIELDS = ['baseRequest', 'baseResponse', 'fastifyResponse', 'progress', 'parsed_sql_object'];
41
+
42
+ // Bound how long the main thread waits for a worker to finish a forwarded operation. Past the
43
+ // operations-API connection timeout the client socket is gone anyway; this just prevents a
44
+ // wedged-but-alive worker from leaking pending forwards forever.
45
+ const EXECUTE_TIMEOUT_MS = env.get(terms.CONFIG_PARAMS.OPERATIONSAPI_NETWORK_TIMEOUT) || 120_000;
46
+
47
+ // Dispatch functions injected by serverUtilities at its module load (it statically imports this
48
+ // module, so a plain import here would be a cycle; a runtime require of a .ts path doesn't
49
+ // survive the dist build). A worker can only receive an execute request after announcing a
50
+ // registration — which goes through serverUtilities — so these are always set on that path.
51
+ let localDispatch: {
52
+ chooseOperation: (body: any) => Function;
53
+ processLocalTransaction: (req: any, operationFunction: Function) => Promise<any>;
54
+ };
55
+ export function setLocalOperationDispatch(dispatch: typeof localDispatch) {
56
+ localDispatch = dispatch;
57
+ }
58
+
59
+ /** name -> threadIds of workers that registered it (main thread only) */
60
+ const registeredByWorker = new Map<string, Set<number>>();
61
+ const pendingExecutions = new Map<
62
+ number,
63
+ { targetThreadId: number; resolve: (result: any) => void; reject: (error: Error) => void }
64
+ >();
65
+ let nextRequestId = 1;
66
+ let mainListenersAttached = false;
67
+
68
+ /**
69
+ * Worker side: announce a registration so the main thread can forward calls here. Fire-and-forget —
70
+ * a lost announcement just means the op stays unreachable (the pre-#1736 status quo), and the
71
+ * broadcast has its own ack timeout.
72
+ */
73
+ export function announceRegisteredOperation(name: string) {
74
+ if (isMainThread) return;
75
+ sendItcEvent({
76
+ type: terms.ITC_EVENT_TYPES.OPERATION_REGISTERED,
77
+ message: { name },
78
+ }).catch((error) => operationLog.error(`Failed to announce registered operation '${name}'`, error));
79
+ }
80
+
81
+ /**
82
+ * ITC handler (all threads receive the broadcast; only main records it).
83
+ */
84
+ export function operationRegisteredHandler(event: { message: { name: string; originator: number } }) {
85
+ if (!isMainThread) return;
86
+ const { name, originator } = event.message;
87
+ if (typeof name !== 'string' || typeof originator !== 'number') return;
88
+ let workerIds = registeredByWorker.get(name);
89
+ if (!workerIds) registeredByWorker.set(name, (workerIds = new Set()));
90
+ workerIds.add(originator);
91
+ operationLog.debug(`Registered operation '${name}' announced by worker thread ${originator}`);
92
+ }
93
+
94
+ let rotation = 0;
95
+ /**
96
+ * Main-thread dispatch fallback: if a worker registered `name`, return a forwarding operation
97
+ * function for it; otherwise undefined (caller falls through to the usual "not found" error).
98
+ * Permission checks are intentionally NOT run on the main thread for forwarded operations — the
99
+ * function and metadata that `verifyPerms` needs only exist on the worker, which runs the full
100
+ * `chooseOperation` check with the forwarded `hdb_user`.
101
+ */
102
+ export function getRemoteOperationFunction(name: string): ((body: any) => Promise<any>) | undefined {
103
+ if (!isMainThread) return undefined;
104
+ if (!registeredByWorker.has(name)) return undefined;
105
+ return (body: any) => executeRemoteOperation(name, body);
106
+ }
107
+
108
+ function attachMainListeners() {
109
+ if (mainListenersAttached) return;
110
+ mainListenersAttached = true;
111
+ onMessageByType(terms.ITC_EVENT_TYPES.OPERATION_EXECUTE_RESPONSE, (event: any) => {
112
+ const { requestId, originator, result, error } = event.message ?? {};
113
+ const pending = pendingExecutions.get(requestId);
114
+ if (!pending) return;
115
+ // Defense-in-depth: only the worker the request was sent to may settle it.
116
+ if (originator !== pending.targetThreadId) return;
117
+ pendingExecutions.delete(requestId);
118
+ if (error) pending.reject(new ServerError(error.message, error.statusCode || 500));
119
+ else pending.resolve(result);
120
+ });
121
+ // A worker that dies mid-execution can never respond; fail its in-flight forwards rather
122
+ // than waiting out the timeout, and forget its registrations (a replacement worker
123
+ // re-registers on component load).
124
+ onThreadExit((deadThreadId: number) => {
125
+ for (const [name, workerIds] of registeredByWorker) {
126
+ workerIds.delete(deadThreadId);
127
+ if (workerIds.size === 0) registeredByWorker.delete(name);
128
+ }
129
+ for (const [requestId, pending] of pendingExecutions) {
130
+ if (pending.targetThreadId === deadThreadId) {
131
+ pendingExecutions.delete(requestId);
132
+ pending.reject(new ServerError('The worker thread executing this operation exited', 503));
133
+ }
134
+ }
135
+ });
136
+ }
137
+
138
+ async function executeRemoteOperation(name: string, body: any): Promise<any> {
139
+ attachMainListeners();
140
+ const workerIds = registeredByWorker.get(name);
141
+ const forwardBody = { ...body };
142
+ for (const field of NON_FORWARDABLE_FIELDS) delete forwardBody[field];
143
+
144
+ // Pick one live registering worker (rotating for spread), pruning ids whose port is gone.
145
+ // sendToThread returning false means the worker exited; try the next one.
146
+ while (workerIds?.size) {
147
+ const candidates = [...workerIds];
148
+ const targetThreadId = candidates[rotation++ % candidates.length];
149
+ const requestId = nextRequestId++;
150
+ let sent;
151
+ try {
152
+ sent = threads.sendToThread(targetThreadId, {
153
+ type: terms.ITC_EVENT_TYPES.OPERATION_EXECUTE_REQUEST,
154
+ message: { requestId, body: forwardBody, originator: threadId },
155
+ });
156
+ } catch (error) {
157
+ // Structured-clone failure (e.g. a function or native handle on the request body) is a
158
+ // server-side limitation, not a malformed client request — 500, not 400. No other worker
159
+ // will fare better with the same body.
160
+ operationLog.error(`Failed to forward operation '${name}' to worker thread ${targetThreadId}`, error);
161
+ throw new ServerError(
162
+ `Operation '${name}' request could not be forwarded to a worker thread: ${error.message}`,
163
+ 500
164
+ );
165
+ }
166
+ if (!sent) {
167
+ workerIds.delete(targetThreadId);
168
+ continue;
169
+ }
170
+ return new Promise((promiseResolve, promiseReject) => {
171
+ const timer = setTimeout(() => {
172
+ pendingExecutions.delete(requestId);
173
+ promiseReject(new ServerError(`Timed out waiting for worker thread to execute operation '${name}'`, 503));
174
+ }, EXECUTE_TIMEOUT_MS);
175
+ timer.unref();
176
+ pendingExecutions.set(requestId, {
177
+ targetThreadId,
178
+ resolve(result) {
179
+ clearTimeout(timer);
180
+ promiseResolve(result);
181
+ },
182
+ reject(error) {
183
+ clearTimeout(timer);
184
+ promiseReject(error);
185
+ },
186
+ });
187
+ });
188
+ }
189
+ if (registeredByWorker.get(name)?.size === 0) registeredByWorker.delete(name);
190
+ throw new ServerError(
191
+ `Operation '${name}' is registered by a component but no worker thread is available to run it`,
192
+ 503
193
+ );
194
+ }
195
+
196
+ /**
197
+ * ITC handler, worker side: execute a forwarded operation through the normal dispatch path
198
+ * (permission check + processLocalTransaction) and send the result back to the main thread.
199
+ */
200
+ export async function operationExecuteRequestHandler(event: {
201
+ message: { requestId: number; body: any; originator: number };
202
+ }) {
203
+ const { requestId, body, originator } = event.message;
204
+ let response;
205
+ try {
206
+ if (!localDispatch) throw new ServerError('This worker thread cannot execute operations', 503);
207
+ // bypass_auth is trusted as forwarded: the main thread already strips it from external
208
+ // HTTP requests (handlePostRequest, before any dispatch decision), and an internal caller
209
+ // legitimately setting it (server.operation(op, context, false)) must see the same
210
+ // authorize:false behavior whether the operation happens to run locally or via this bridge.
211
+ // ITC is an internal, same-process trust boundary — a worker able to forge this message
212
+ // already has direct DB access and gains nothing by spoofing bypass_auth.
213
+ const operationFunction = localDispatch.chooseOperation(body);
214
+ const result = await localDispatch.processLocalTransaction({ body }, operationFunction);
215
+ if (result instanceof Readable || typeof result?.pipe === 'function') {
216
+ // Streaming results would need MessagePort transfer plumbing — explicitly unsupported
217
+ // for worker-registered operations for now (#1736), rather than failing opaquely.
218
+ // The handler already ran and the stream may hold an open fd/cursor/socket; destroy it
219
+ // rather than abandoning it to GC finalization.
220
+ result.destroy?.();
221
+ throw new ServerError(
222
+ `Operation '${body.operation}' returned a stream; streaming results are not supported for operations registered from a component`,
223
+ 501
224
+ );
225
+ }
226
+ response = { requestId, result };
227
+ } catch (error) {
228
+ response = {
229
+ requestId,
230
+ error: {
231
+ message: error.http_resp_msg?.error ?? error.http_resp_msg ?? error.message,
232
+ statusCode: error.statusCode,
233
+ },
234
+ };
235
+ }
236
+ try {
237
+ sendExecuteResponse(originator, response);
238
+ } catch (error) {
239
+ // The result itself failed to structured-clone; report that instead of leaving the main
240
+ // thread to time out.
241
+ operationLog.error(`Failed to send result of operation '${body?.operation}' back to the main thread`, error);
242
+ sendExecuteResponse(originator, {
243
+ requestId,
244
+ error: {
245
+ message: `Operation '${body?.operation}' result could not be returned across threads: ${error.message}`,
246
+ statusCode: 500,
247
+ },
248
+ });
249
+ }
250
+ }
251
+
252
+ function sendExecuteResponse(requestOriginator: number, message: any) {
253
+ // Stamp this worker's threadId so the main thread can verify the response came from the
254
+ // worker the request was actually sent to.
255
+ message.originator = threadId;
256
+ if (!threads.sendToThread(requestOriginator, { type: terms.ITC_EVENT_TYPES.OPERATION_EXECUTE_RESPONSE, message })) {
257
+ operationLog.trace(`Dropping operation execute response for request ${message.requestId}: main thread unreachable`);
258
+ }
259
+ }
@@ -25,11 +25,17 @@ const { applyImpersonation } = require('../../security/impersonation.ts');
25
25
  const { createGzip, constants } = require('zlib');
26
26
  const { ProgressEmitter, createSSEResponseStream } = require('./progressEmitter.ts');
27
27
 
28
- // Operations that support `Accept: text/event-stream` for live progress streaming. The
29
- // handler attaches a ProgressEmitter as req.body.progress so the operation can emit phase
30
- // events; the response body is the SSE-encoded emitter output. Non-SSE clients see the
31
- // historical single-response shape because progress is undefined on that path.
32
- const SSE_PROGRESS_OPERATIONS = new Set([terms.OPERATIONS_ENUM.DEPLOY_COMPONENT, terms.OPERATIONS_ENUM.GET_DEPLOYMENT]);
28
+ // Operations that support `Accept: text/event-stream` for live streaming. The handler
29
+ // attaches a ProgressEmitter as req.body.progress so the operation can emit events; the
30
+ // response body is the SSE-encoded emitter output. Non-SSE clients see the historical
31
+ // single-response shape because progress is undefined on that path. deploy_component /
32
+ // get_deployment stream a bounded run and end; read_log tails the log and stays open until
33
+ // the client disconnects (the emitter's abort signal), so subscribers see new lines live.
34
+ const SSE_PROGRESS_OPERATIONS = new Set([
35
+ terms.OPERATIONS_ENUM.DEPLOY_COMPONENT,
36
+ terms.OPERATIONS_ENUM.GET_DEPLOYMENT,
37
+ terms.OPERATIONS_ENUM.READ_LOG,
38
+ ]);
33
39
 
34
40
  const NO_AUTH_OPERATIONS = [
35
41
  terms.OPERATIONS_ENUM.CREATE_AUTHENTICATION_TOKENS,
@@ -37,6 +37,14 @@ import type { Context } from '../../resources/ResourceInterface.ts';
37
37
  import * as status from '../status/index.ts';
38
38
  import * as regDeprecated from '../../resources/registrationDeprecated.ts';
39
39
  import * as deploymentOperations from '../../components/deploymentOperations.ts';
40
+ import * as secretOperations from '../../components/secretOperations.ts';
41
+ import { contextStorage } from '../../resources/transaction.ts';
42
+ import { isMainThread } from 'node:worker_threads';
43
+ import {
44
+ announceRegisteredOperation,
45
+ getRemoteOperationFunction,
46
+ setLocalOperationDispatch,
47
+ } from './registeredOperations.ts';
40
48
 
41
49
  const pSearchSearch = util.promisify(search.search);
42
50
  let pEvaluateSql: (sql: string) => Promise<any>;
@@ -75,17 +83,37 @@ export async function processLocalTransaction(req: OperationRequest, operationFu
75
83
  harperLogger.logLevel === terms.LOG_LEVELS.DEBUG ||
76
84
  harperLogger.logLevel === terms.LOG_LEVELS.TRACE)
77
85
  ) {
78
- // Need to remove auth variables, but we don't want to create an object unless
79
- // the logging is actually going to happen.
86
+ // Need to remove auth variables and secret-bearing fields, but we don't want to create
87
+ // an object unless the logging is actually going to happen. registryAuth carries a
88
+ // transient private registry token on deploy_component; value/values carry .env secrets
89
+ // from set_env_value; value/envelope carry secrets from set_secret — none may reach the
90
+ // operations log.
80
91
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
81
- const { hdb_user, hdbAuthHeader, password, payload, ...cleanBody } = req.body;
92
+ const { hdb_user, hdbAuthHeader, password, payload, registryAuth, value, values, envelope, ...cleanBody } =
93
+ req.body;
82
94
  operationLog.info(cleanBody);
83
95
  }
84
96
  } catch (e) {
85
97
  operationLog.error(e);
86
98
  }
87
99
 
88
- let data = await operationFunctionCaller.callOperationFunctionAsAwait(operationFunction, req.body, null);
100
+ // Bridge the authenticated user into the ambient async context so static Resource API calls
101
+ // (e.g. table.put) inside operation handlers inherit user attribution for audit records
102
+ // (issue #1591). An explicit context passed by a handler still takes precedence (the
103
+ // transactional wrappers only fall back to contextStorage when no context is provided), and
104
+ // an existing ambient context already carrying this user (e.g. server.operation() called
105
+ // from within a request handler) is preserved rather than shadowed. When the ambient user
106
+ // differs (or is absent), the ambient context is merged rather than replaced: other ambient
107
+ // properties (open transaction, signal, caches) are preserved so atomicity is unaffected and
108
+ // only the user is swapped for attribution; the outer context object itself is never mutated.
109
+ const hdbUser = req.body.hdb_user;
110
+ const currentStore = contextStorage.getStore();
111
+ const callOperationFunction = () =>
112
+ operationFunctionCaller.callOperationFunctionAsAwait(operationFunction, req.body, null);
113
+ let data =
114
+ hdbUser && currentStore?.user !== hdbUser
115
+ ? await contextStorage.run({ ...currentStore, user: hdbUser }, callOperationFunction)
116
+ : await callOperationFunction();
89
117
 
90
118
  if (typeof data !== 'object') {
91
119
  data = { message: data };
@@ -113,6 +141,15 @@ export type OperationDefinition = {
113
141
  httpMethod?: 'DELETE' | 'GET' | 'HEAD' | 'OPTIONS' | 'PATCH' | 'POST' | 'PUT' | 'TRACE'; // method to use for REST
114
142
  isJob?: boolean;
115
143
  parametersSchema?: any[];
144
+ // When set, the operation declares its authorization requirement to the central verifyPerms
145
+ // system so it participates in the role `operations` allowlist (grantable to a scoped role)
146
+ // instead of a hand-rolled inline check. Tri-state:
147
+ // `true` = super_user by default (grantable to a scoped role via the `operations` allowlist);
148
+ // `false` = grantable AND open to ANY authenticated user with a valid role — it registers an
149
+ // empty-perms entry, which verifyPerms treats as "no specific permission required"
150
+ // and allows; use deliberately, it is NOT a "still locked down" default;
151
+ // omit = pre-existing behavior (no central entry; the caller enforces its own auth, if any).
152
+ requiresSuperUser?: boolean;
116
153
  };
117
154
 
118
155
  /**
@@ -120,7 +157,24 @@ export type OperationDefinition = {
120
157
  * @param operationDefinition
121
158
  */
122
159
  server.registerOperation = (operationDefinition: OperationDefinition) => {
123
- OPERATION_FUNCTION_MAP.set(operationDefinition.name as any, new OperationFunctionObject(operationDefinition.execute));
160
+ const { name, execute, requiresSuperUser } = operationDefinition;
161
+ let handler = execute;
162
+ if (requiresSuperUser !== undefined) {
163
+ // verifyPerms keys requiredPermissions by the handler's function `.name`, but registered ops
164
+ // are typically anonymous arrows (all named "execute") which collide and can't be keyed. Wrap
165
+ // in a FRESH function named after the op so the lookup resolves the right entry. Wrap rather
166
+ // than rename `execute` in place: renaming would mutate a handler shared across two op names,
167
+ // causing the first op to be checked against the second op's permission entry. Forward all
168
+ // args (transparent pass-through) so the wrapper never changes the handler's call contract.
169
+ handler = (...args: any[]) => (execute as any)(...args);
170
+ Object.defineProperty(handler, 'name', { value: name, configurable: true });
171
+ opAuth.registerOperationPermission(name, { requiresSu: requiresSuperUser });
172
+ }
173
+ OPERATION_FUNCTION_MAP.set(name as any, new OperationFunctionObject(handler));
174
+ // Components load per-worker, so a registration made there is invisible to the main-thread
175
+ // ops-API dispatcher (each thread has its own OPERATION_FUNCTION_MAP instance). Announce it
176
+ // so the main thread can forward calls to this worker (#1736).
177
+ if (!isMainThread) announceRegisteredOperation(name);
124
178
  };
125
179
 
126
180
  export function chooseOperation(json: OperationRequestBody) {
@@ -128,6 +182,15 @@ export function chooseOperation(json: OperationRequestBody) {
128
182
  try {
129
183
  getOpResult = getOperationFunction(json);
130
184
  } catch (err) {
185
+ // Not in this thread's map — a component may have registered it on a worker thread
186
+ // (components load per-worker). Forward there for execution; the worker runs the
187
+ // full permission check (with the forwarded hdb_user) where the operation function
188
+ // and its metadata actually exist, so no perm check is skipped by returning early
189
+ // here (#1736). Workers never re-forward, so an unknown op can't loop.
190
+ if (isMainThread) {
191
+ const remoteOperationFunction = getRemoteOperationFunction(json.operation);
192
+ if (remoteOperationFunction) return remoteOperationFunction;
193
+ }
131
194
  operationLog.error(`Error when selecting operation function - ${err}`);
132
195
  throw err;
133
196
  }
@@ -210,6 +273,11 @@ export function getOperationFunction(json: OperationRequestBody): OperationFunct
210
273
  );
211
274
  }
212
275
 
276
+ // Give the cross-thread bridge (#1736) the worker-side dispatch path for forwarded operations.
277
+ // Injected (rather than imported there) because registeredOperations is imported above — a
278
+ // static import back into this module would be a cycle.
279
+ setLocalOperationDispatch({ chooseOperation, processLocalTransaction });
280
+
213
281
  _assignPackageExport('operation', operation);
214
282
  /**
215
283
  * Standalone function to execute an operation
@@ -403,6 +471,12 @@ function initializeOperationFunctionMap(): Map<OperationFunctionName, OperationF
403
471
  terms.OPERATIONS_ENUM.SET_COMPONENT_FILE,
404
472
  new OperationFunctionObject(customFunctionOperations.setComponentFile)
405
473
  );
474
+ opFuncMap.set(terms.OPERATIONS_ENUM.GET_ENV_KEYS, new OperationFunctionObject(customFunctionOperations.getEnvKeys));
475
+ opFuncMap.set(terms.OPERATIONS_ENUM.SET_ENV_VALUE, new OperationFunctionObject(customFunctionOperations.setEnvValue));
476
+ opFuncMap.set(
477
+ terms.OPERATIONS_ENUM.DELETE_ENV_VALUE,
478
+ new OperationFunctionObject(customFunctionOperations.deleteEnvValue)
479
+ );
406
480
  opFuncMap.set(
407
481
  terms.OPERATIONS_ENUM.DROP_COMPONENT,
408
482
  new OperationFunctionObject(customFunctionOperations.dropComponent)
@@ -455,6 +529,15 @@ function initializeOperationFunctionMap(): Map<OperationFunctionName, OperationF
455
529
  terms.OPERATIONS_ENUM.GET_DEPLOYMENT,
456
530
  new OperationFunctionObject(deploymentOperations.handleGetDeployment)
457
531
  );
532
+ opFuncMap.set(terms.OPERATIONS_ENUM.SET_SECRET, new OperationFunctionObject(secretOperations.setSecret));
533
+ opFuncMap.set(terms.OPERATIONS_ENUM.GRANT_SECRET, new OperationFunctionObject(secretOperations.grantSecret));
534
+ opFuncMap.set(terms.OPERATIONS_ENUM.REVOKE_SECRET, new OperationFunctionObject(secretOperations.revokeSecret));
535
+ opFuncMap.set(terms.OPERATIONS_ENUM.LIST_SECRETS, new OperationFunctionObject(secretOperations.listSecrets));
536
+ opFuncMap.set(terms.OPERATIONS_ENUM.DELETE_SECRET, new OperationFunctionObject(secretOperations.deleteSecret));
537
+ opFuncMap.set(
538
+ terms.OPERATIONS_ENUM.GET_SECRETS_PUBLIC_KEY,
539
+ new OperationFunctionObject(secretOperations.getSecretsPublicKey)
540
+ );
458
541
  opFuncMap.set(
459
542
  terms.OPERATIONS_ENUM.READ_TRANSACTION_LOG,
460
543
  new OperationFunctionObject(transactionLog.readTransactionLog)