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
@@ -1,10 +1,13 @@
1
1
  /**
2
2
  * Operations API surface for the built-in agent (#626).
3
3
  *
4
- * Six handlers, all super_user-only the auth check is inline because none
5
- * of these ops are registered in `utility/operation_authorization.ts`'s
6
- * `requiredPermissions` map. Without the inline gate, a non-SU request would
7
- * fall through the standard flow and be allowed.
4
+ * Six handlers, super_user by default. Authorization is declared via
5
+ * `requiresSuperUser: true` on each OperationDefinition, so `registerOperation`
6
+ * registers them with the central `verifyPerms` system and they participate in
7
+ * the role `operations` allowlist an operator can grant a scoped, non-super_user
8
+ * "delegation" role access to these ops (e.g. `operations: ['agent']`) without
9
+ * granting full super_user. verifyPerms runs before the handler for every
10
+ * ops-API request, so no inline gate is needed here.
8
11
  */
9
12
 
10
13
  import type { OperationDefinition } from '../server/serverHelpers/serverUtilities.ts';
@@ -25,38 +28,37 @@ export function buildOperations(deps: OperationDeps): OperationDefinition[] {
25
28
  {
26
29
  name: OPERATIONS_ENUM.AGENT_PROMPT,
27
30
  execute: async (op) => agentPrompt(op, deps),
31
+ requiresSuperUser: true,
28
32
  },
29
33
  {
30
34
  name: OPERATIONS_ENUM.GET_AGENT_SESSION,
31
35
  execute: async (op) => getAgentSession(op),
36
+ requiresSuperUser: true,
32
37
  },
33
38
  {
34
39
  name: OPERATIONS_ENUM.LIST_AGENT_SESSIONS,
35
40
  execute: async (op) => listAgentSessions(op),
41
+ requiresSuperUser: true,
36
42
  },
37
43
  {
38
44
  name: OPERATIONS_ENUM.CANCEL_AGENT_RUN,
39
45
  execute: async (op) => cancelAgentRun(op, deps),
46
+ requiresSuperUser: true,
40
47
  },
41
48
  {
42
49
  name: OPERATIONS_ENUM.APPROVE_AGENT_ACTION,
43
50
  execute: async (op) => approveAgentAction(op, deps),
51
+ requiresSuperUser: true,
44
52
  },
45
53
  {
46
54
  name: OPERATIONS_ENUM.SET_AGENT_CONFIG,
47
55
  execute: async (op) => setAgentConfig(op, deps),
56
+ requiresSuperUser: true,
48
57
  },
49
58
  ];
50
59
  }
51
60
 
52
- function requireSuperUser(op: any): void {
53
- if (!op?.hdb_user?.role?.permission?.super_user) {
54
- throw new ClientError('Agent operations require super_user', 403);
55
- }
56
- }
57
-
58
61
  async function agentPrompt(op: any, deps: OperationDeps) {
59
- requireSuperUser(op);
60
62
  const config = deps.getConfig();
61
63
  if (!config.enabled) {
62
64
  throw new ClientError('Agent component is disabled (agent.enabled=false)', 409);
@@ -90,7 +92,6 @@ async function agentPrompt(op: any, deps: OperationDeps) {
90
92
  }
91
93
 
92
94
  async function getAgentSession(op: any) {
93
- requireSuperUser(op);
94
95
  const sessionId = String(op?.session_id ?? '');
95
96
  if (!sessionId) throw new ClientError('session_id is required', 400);
96
97
  const session = await getSession(sessionId);
@@ -99,13 +100,11 @@ async function getAgentSession(op: any) {
99
100
  }
100
101
 
101
102
  async function listAgentSessions(op: any) {
102
- requireSuperUser(op);
103
103
  const limit = Number.isFinite(op?.limit) ? Number(op.limit) : undefined;
104
104
  return { sessions: await listSessions({ limit }) };
105
105
  }
106
106
 
107
107
  async function cancelAgentRun(op: any, deps: OperationDeps) {
108
- requireSuperUser(op);
109
108
  const sessionId = String(op?.session_id ?? '');
110
109
  if (!sessionId) throw new ClientError('session_id is required', 400);
111
110
  const session = await getSession(sessionId);
@@ -120,7 +119,6 @@ async function cancelAgentRun(op: any, deps: OperationDeps) {
120
119
  }
121
120
 
122
121
  async function approveAgentAction(op: any, deps: OperationDeps) {
123
- requireSuperUser(op);
124
122
  const sessionId = String(op?.session_id ?? '');
125
123
  const approvalId = String(op?.approval_id ?? '');
126
124
  if (!sessionId || !approvalId) {
@@ -135,7 +133,6 @@ async function approveAgentAction(op: any, deps: OperationDeps) {
135
133
  }
136
134
 
137
135
  async function setAgentConfig(op: any, deps: OperationDeps) {
138
- requireSuperUser(op);
139
136
  const patch: Partial<AgentConfig> = {};
140
137
  for (const key of ['enabled', 'provider', 'model', 'maxTurns', 'maxCostUsd', 'autoApprove', 'allowDestructive']) {
141
138
  if (op?.[key] !== undefined) (patch as any)[key] = op[key];
@@ -20,6 +20,7 @@ import {
20
20
  writeFile,
21
21
  } from 'node:fs/promises';
22
22
  import { spawn } from 'node:child_process';
23
+ import { tmpdir } from 'node:os';
23
24
  import { randomUUID } from 'node:crypto';
24
25
  import { createReadStream, existsSync, readdirSync } from 'node:fs';
25
26
  import { Readable } from 'node:stream';
@@ -37,6 +38,10 @@ interface ApplicationConfig {
37
38
  timeout?: number;
38
39
  allowInstallScripts?: boolean;
39
40
  };
41
+ // Private-registry auth in reference form only — each entry names an hdb_secret row, never a
42
+ // token. Recorded by deploy_component so every (cold) install — reboot, new peer, rollback —
43
+ // re-resolves the credential from the store rather than needing it re-supplied.
44
+ registryAuth?: { registry: string; secret: string; scope?: string }[];
40
45
  // an application config can have other arbitrary properties
41
46
  [key: string]: unknown;
42
47
  }
@@ -73,6 +78,22 @@ export class InvalidInstallTimeoutError extends TypeError {
73
78
  }
74
79
  }
75
80
 
81
+ export class InvalidRegistryAuthPropertyError extends TypeError {
82
+ constructor(applicationName: string, registryAuth: unknown) {
83
+ super(
84
+ `Invalid 'registryAuth' property for application ${applicationName}: expected array, got ${typeof registryAuth}`
85
+ );
86
+ }
87
+ }
88
+
89
+ export class InvalidRegistryAuthEntryError extends TypeError {
90
+ constructor(applicationName: string) {
91
+ super(
92
+ `Invalid 'registryAuth' entry for application ${applicationName}: expected { registry, secret, scope? } reference`
93
+ );
94
+ }
95
+ }
96
+
76
97
  export function assertApplicationConfig(
77
98
  applicationName: string,
78
99
  applicationConfig: Record<'package', unknown> & Record<string, unknown>
@@ -110,6 +131,24 @@ export function assertApplicationConfig(
110
131
  );
111
132
  }
112
133
  }
134
+ if ('registryAuth' in applicationConfig && applicationConfig.registryAuth !== undefined) {
135
+ const entries = applicationConfig.registryAuth;
136
+ if (!Array.isArray(entries)) {
137
+ throw new InvalidRegistryAuthPropertyError(applicationName, entries);
138
+ }
139
+ for (const entry of entries) {
140
+ // Config carries references only — a literal `token` here would mean a plaintext credential
141
+ // was persisted to disk, which the deploy path is designed to prevent.
142
+ if (
143
+ typeof entry !== 'object' ||
144
+ entry === null ||
145
+ typeof (entry as any).registry !== 'string' ||
146
+ typeof (entry as any).secret !== 'string'
147
+ ) {
148
+ throw new InvalidRegistryAuthEntryError(applicationName);
149
+ }
150
+ }
151
+ }
113
152
  }
114
153
 
115
154
  /**
@@ -210,7 +249,10 @@ export async function extractApplication(application: Application) {
210
249
  application.name,
211
250
  'npm',
212
251
  ['pack', '--json', application.packageIdentifier],
213
- parentDirPath
252
+ parentDirPath,
253
+ undefined,
254
+ undefined,
255
+ application.npmUserconfigPath
214
256
  );
215
257
  if (code !== 0) {
216
258
  if (isSSHAuthFailure(stderr)) {
@@ -354,7 +396,8 @@ export async function installApplication(application: Application) {
354
396
  args,
355
397
  application.dirPath,
356
398
  application.install?.timeout,
357
- customOnLine
399
+ customOnLine,
400
+ application.npmUserconfigPath
358
401
  );
359
402
  // if it succeeds, return
360
403
  if (code === 0) {
@@ -414,7 +457,8 @@ export async function installApplication(application: Application) {
414
457
  application.install?.allowInstallScripts ? ['install'] : ['install', '--ignore-scripts'], // All of `npm`, `yarn`, and `pnpm` support the `install` command. If we need to configure options here we may have to use some other defaults though
415
458
  application.dirPath,
416
459
  application.install?.timeout,
417
- pmOnLine
460
+ pmOnLine,
461
+ application.npmUserconfigPath
418
462
  );
419
463
 
420
464
  // if it succeeds, return
@@ -470,7 +514,8 @@ export async function installApplication(application: Application) {
470
514
  npmInstallArgs,
471
515
  application.dirPath,
472
516
  application.install?.timeout,
473
- npmOnLine
517
+ npmOnLine,
518
+ application.npmUserconfigPath
474
519
  );
475
520
 
476
521
  // if it succeeds, return
@@ -506,6 +551,7 @@ interface ApplicationOptions {
506
551
  packageIdentifier?: string;
507
552
  install?: { command?: string; timeout?: number; allowInstallScripts?: boolean };
508
553
  onInstallLine?: OnInstallLine;
554
+ registryAuth?: RegistryAuthEntry[];
509
555
  }
510
556
 
511
557
  export class Application {
@@ -517,19 +563,75 @@ export class Application {
517
563
  dirPath: string;
518
564
  logger: Logger;
519
565
  packageManagerPrefix: string; // can be used to configure a package manager prefix, specifically "sfw".
520
-
521
- constructor({ name, payload, packageIdentifier, install, onInstallLine }: ApplicationOptions) {
566
+ // Transient registry auth provided by a deploy. The token is held only in memory and a
567
+ // per-deploy `.npmrc`; it is never persisted to config, hdb_deployment, or replicated.
568
+ registryAuth?: RegistryAuthEntry[];
569
+ // Path to the per-deploy `.npmrc`, set by writeTransientNpmrc() during prepareApplication and
570
+ // passed to the spawn calls; undefined when no registry auth was provided.
571
+ npmUserconfigPath?: string;
572
+ #npmrcTempDir?: string;
573
+
574
+ constructor({ name, payload, packageIdentifier, install, onInstallLine, registryAuth }: ApplicationOptions) {
522
575
  this.name = name;
523
576
  this.payload = payload;
524
577
  this.packageIdentifier = packageIdentifier && derivePackageIdentifier(packageIdentifier);
525
578
  this.install = install;
526
579
  this.onInstallLine = onInstallLine;
580
+ this.registryAuth = registryAuth;
527
581
  const componentsRoot = getConfigPath(CONFIG_PARAMS.COMPONENTSROOT);
528
582
  if (!componentsRoot) throw new Error('componentsRoot is not configured');
529
583
  this.dirPath = join(componentsRoot, name);
530
584
  this.logger = logger.loggerWithTag(name);
531
585
  this.packageManagerPrefix = getConfigValue(CONFIG_PARAMS.APPLICATIONS_PACKAGEMANAGERPREFIX);
532
586
  }
587
+
588
+ // Write the transient `.npmrc` into a fresh 0700 temp dir (file mode 0600) and record its path
589
+ // so the deploy's npm spawns authenticate against the private registry. No-op without registry auth.
590
+ //
591
+ // Because `nonInteractiveSpawn` points npm at this single file (replacing any inherited
592
+ // npm_config_userconfig), prepend the contents of an already-configured userconfig — e.g. a
593
+ // fabric-injected file carrying cluster registries, a proxy, or a cafile — so those settings
594
+ // survive. The transient auth is appended last so it wins on conflict (npm honors the last
595
+ // value for a given key).
596
+ async writeTransientNpmrc(): Promise<void> {
597
+ if (!this.registryAuth?.length) return;
598
+ // Defensive: if called more than once, remove the prior temp dir first so it isn't leaked.
599
+ if (this.#npmrcTempDir) await this.cleanupTransientNpmrc();
600
+ this.#npmrcTempDir = await mkdtemp(join(tmpdir(), 'harper-npmrc-'));
601
+ const npmrcPath = join(this.#npmrcTempDir, '.npmrc');
602
+ let content = '';
603
+ const inheritedUserconfig = process.env.npm_config_userconfig ?? process.env.NPM_CONFIG_USERCONFIG;
604
+ if (inheritedUserconfig) {
605
+ try {
606
+ const inherited = await readFile(inheritedUserconfig, 'utf8');
607
+ content = inherited.endsWith('\n') ? inherited : inherited + '\n';
608
+ } catch (error: any) {
609
+ // Missing inherited file is fine (npm would have created/ignored it); surface anything else.
610
+ if (error?.code !== 'ENOENT') throw error;
611
+ }
612
+ }
613
+ content += buildNpmrcContent(this.registryAuth);
614
+ await writeFile(npmrcPath, content, { mode: 0o600 });
615
+ this.npmUserconfigPath = npmrcPath;
616
+ }
617
+
618
+ // Remove the transient `.npmrc` (and its temp dir) once the deploy's npm work is done.
619
+ async cleanupTransientNpmrc(): Promise<void> {
620
+ if (!this.#npmrcTempDir) return;
621
+ try {
622
+ await rm(this.#npmrcTempDir, { recursive: true, force: true });
623
+ } catch (error) {
624
+ // Called from prepareApplication's finally; a throw here (e.g. a Windows file lock) would
625
+ // mask the original deploy error and skip broadcastDeployEnd. Log and always clear state.
626
+ this.logger.warn(`Failed to remove transient .npmrc dir ${this.#npmrcTempDir}:`, error);
627
+ } finally {
628
+ this.#npmrcTempDir = undefined;
629
+ this.npmUserconfigPath = undefined;
630
+ // Drop the in-memory token array too, so it can't surface in a later heap dump or error
631
+ // serialization of this Application instance.
632
+ this.registryAuth = undefined;
633
+ }
634
+ }
533
635
  }
534
636
 
535
637
  /**
@@ -569,9 +671,13 @@ export function derivePackageIdentifier(packageIdentifier: string) {
569
671
  export async function prepareApplication(application: Application) {
570
672
  await broadcastDeployStart(application.name);
571
673
  try {
674
+ // Materialize the per-deploy `.npmrc` before extraction so both `npm pack` (extract) and
675
+ // `npm install` authenticate against the private registry; always remove it afterward.
676
+ await application.writeTransientNpmrc();
572
677
  await extractApplication(application);
573
678
  await installApplication(application);
574
679
  } finally {
680
+ await application.cleanupTransientNpmrc();
575
681
  broadcastDeployEnd(application.name);
576
682
  }
577
683
  }
@@ -631,10 +737,29 @@ export async function installApplications() {
631
737
  // This will throw if the config is invalid
632
738
  assertApplicationConfig(name, applicationConfig);
633
739
 
740
+ // Resolve any private-registry auth references from the store so a cold install (fresh
741
+ // node, wiped components dir, new peer that never installed) can authenticate without the
742
+ // token being re-supplied. Best-effort: if custody isn't available yet or a referenced
743
+ // secret is missing, log and install without it (a truly private package then fails in
744
+ // npm with its own error) rather than blocking boot.
745
+ let resolvedRegistryAuth: RegistryAuthEntry[] | undefined;
746
+ if (applicationConfig.registryAuth?.length) {
747
+ try {
748
+ const { resolveRegistryAuth } = await import('./secretOperations.ts');
749
+ resolvedRegistryAuth = (await resolveRegistryAuth(applicationConfig.registryAuth, name)) as
750
+ RegistryAuthEntry[] | undefined;
751
+ } catch (error) {
752
+ logger.warn?.(
753
+ `Could not resolve registryAuth for application ${name} at install time: ${(error as Error).message}`
754
+ );
755
+ }
756
+ }
757
+
634
758
  const application = new Application({
635
759
  name,
636
760
  packageIdentifier: applicationConfig.package,
637
761
  install: applicationConfig.install,
762
+ registryAuth: resolvedRegistryAuth,
638
763
  });
639
764
 
640
765
  // Lock check: only install if not already installed with matching configuration
@@ -675,6 +800,49 @@ function getGitSSHCommand() {
675
800
  }
676
801
  }
677
802
 
803
+ export interface RegistryAuthEntry {
804
+ registry: string;
805
+ token: string;
806
+ scope?: string;
807
+ }
808
+
809
+ // Normalize a registry to a full URL with a scheme and trailing slash, e.g.
810
+ // `npm.pkg.github.com` or `//npm.pkg.github.com` → `https://npm.pkg.github.com/`.
811
+ function normalizeRegistryUrl(registry: string): string {
812
+ let url = registry.trim();
813
+ if (!/^https?:\/\//i.test(url)) {
814
+ url = url.startsWith('//') ? `https:${url}` : `https://${url}`;
815
+ }
816
+ if (!url.endsWith('/')) url += '/';
817
+ return url;
818
+ }
819
+
820
+ // Build the contents of a transient `.npmrc` from registry auth entries: an auth-token line keyed
821
+ // by npm's registry auth key (scheme stripped, leading `//`, trailing `/`) plus a registry-routing
822
+ // line. A scope routes only that `@scope` to the registry (`@scope:registry=…`); without a scope
823
+ // the entry sets npm's default `registry=…` so an unscoped package spec (e.g. `npm:my-private-app`)
824
+ // or its transitive deps actually resolve against this registry rather than the public default.
825
+ // A scope-less entry therefore requires its registry to serve/proxy whatever npm needs to install;
826
+ // with multiple scope-less entries npm's last-value-wins applies to the default `registry`.
827
+ export function buildNpmrcContent(registryAuth: RegistryAuthEntry[]): string {
828
+ const lines: string[] = [];
829
+ for (const { registry, token, scope } of registryAuth) {
830
+ // Enforce the no-newline invariant at the injection point so it holds for every source. The
831
+ // ops validator already rejects CR/LF in a literal `token`, but a token resolved from an
832
+ // hdb_secret row bypasses that guard; without this a `\n` in a secret value would inject
833
+ // arbitrary .npmrc lines (admin-only per the threat model, but the literal path already
834
+ // defends this class).
835
+ if (/[\r\n]/.test(token)) {
836
+ throw new Error(`registry auth token for '${registry}' contains an illegal newline character`);
837
+ }
838
+ const registryUrl = normalizeRegistryUrl(registry);
839
+ const authKey = registryUrl.replace(/^https?:/i, '');
840
+ lines.push(`${authKey}:_authToken=${token}`);
841
+ lines.push(scope ? `${scope}:registry=${registryUrl}` : `registry=${registryUrl}`);
842
+ }
843
+ return lines.join('\n') + '\n';
844
+ }
845
+
678
846
  /**
679
847
  * Execute a command (using `spawn`) with stdin ignored.
680
848
  *
@@ -734,7 +902,8 @@ export function nonInteractiveSpawn(
734
902
  args: string[],
735
903
  cwd: string,
736
904
  timeoutMs: number = 60 * 60 * 1000,
737
- onLine?: (stream: 'stdout' | 'stderr', line: string) => void
905
+ onLine?: (stream: 'stdout' | 'stderr', line: string) => void,
906
+ npmUserconfigPath?: string
738
907
  ): Promise<{ stdout: string; stderr: string; code: number }> {
739
908
  return new Promise((resolve, reject) => {
740
909
  logger
@@ -748,6 +917,18 @@ export function nonInteractiveSpawn(
748
917
  env.GIT_SSH_COMMAND = gitSSHCommand;
749
918
  }
750
919
 
920
+ // A deploy carrying transient registry auth points npm at a per-deploy `.npmrc` so
921
+ // `npm pack`/`install` can authenticate against a private registry without the token
922
+ // ever touching disk durably, the package reference, config, or hdb_deployment.
923
+ if (npmUserconfigPath) {
924
+ // On case-insensitive platforms (Windows) an inherited NPM_CONFIG_USERCONFIG would
925
+ // shadow the lowercase key we set, so drop any existing case variant first.
926
+ for (const key of Object.keys(env)) {
927
+ if (key.toLowerCase() === 'npm_config_userconfig') delete env[key];
928
+ }
929
+ env.npm_config_userconfig = npmUserconfigPath;
930
+ }
931
+
751
932
  if (process.platform === 'win32' && command === 'npm') {
752
933
  command = 'npm.cmd';
753
934
  }
@@ -16,6 +16,8 @@ export class MissingDefaultFilesOptionError extends Error {
16
16
  * This class is used to represent the application scope for the VM context used for loading modules within an application
17
17
  */
18
18
  export class ApplicationScope {
19
+ /** Component identity (application directory name) — what secret grants are matched against. */
20
+ name: string;
19
21
  logger: any;
20
22
  resources: Resources;
21
23
  server: Server;
@@ -25,6 +27,7 @@ export class ApplicationScope {
25
27
  config: any;
26
28
  moduleCache: any; // used by the loader to retain a cache of modules, type is an internal detail of the loader
27
29
  constructor(name: string, resources: Resources, server: Server, isInternal = false) {
30
+ this.name = name;
28
31
  this.logger = forComponent(name, !isInternal);
29
32
 
30
33
  this.resources = resources;
@@ -12,6 +12,7 @@ import { FilesOption } from './deriveGlobOptions.ts';
12
12
  import { requestRestart } from './requestRestart.ts';
13
13
  import { resolveBaseURLPath } from './resolveBaseURLPath.ts';
14
14
  import { ApplicationScope } from './ApplicationScope.ts';
15
+ import { getSecretsForComponent } from './componentSecrets.ts';
15
16
  import { deployLifecycle } from './deployLifecycle.ts';
16
17
 
17
18
  export class MissingDefaultFilesOptionError extends Error {
@@ -146,6 +147,17 @@ export class Scope extends EventEmitter<ScopeEventsMap> {
146
147
  return this.#logger;
147
148
  }
148
149
 
150
+ /**
151
+ * The application's secrets view (#1550): hdb_secret rows granted to this application plus its
152
+ * declared global-tier env names. Frozen, enumerable, values decrypted at component load.
153
+ * Keyed by the ApplicationScope's name (the application directory name — the identity grants
154
+ * and env declarations use, and the same binding `import { secrets } from 'harper'` resolves);
155
+ * `#appName` can differ on paths like RUN_HDB_APP, where it is the full directory path.
156
+ */
157
+ get secrets(): Readonly<Record<string, string>> {
158
+ return getSecretsForComponent(this.applicationScope?.name ?? this.#appName);
159
+ }
160
+
149
161
  get appName(): string {
150
162
  return this.#appName;
151
163
  }
@@ -257,7 +257,10 @@ export class AnthropicBackend implements ModelBackend {
257
257
  signal,
258
258
  });
259
259
  if (!res.ok) {
260
- throw new AnthropicBackendError(`Anthropic ${path} returned HTTP ${res.status}${await readErrorSuffix(res)}`);
260
+ throw new AnthropicBackendError(
261
+ `Anthropic ${path} returned HTTP ${res.status}${await readErrorSuffix(res)}`,
262
+ res.status
263
+ );
261
264
  }
262
265
  return res;
263
266
  }
@@ -283,9 +286,13 @@ export function registerAnthropicBackend(args: {
283
286
  }
284
287
 
285
288
  export class AnthropicBackendError extends ServerError {
286
- constructor(message: string) {
289
+ /** HTTP status returned by the upstream provider, when the failure came from an HTTP response.
290
+ * Distinct from ServerError's statusCode, which is Harper's own response status (#1593). */
291
+ declare upstreamStatus?: number;
292
+ constructor(message: string, upstreamStatus?: number) {
287
293
  super(message);
288
294
  this.name = 'AnthropicBackendError';
295
+ if (upstreamStatus !== undefined) this.upstreamStatus = upstreamStatus;
289
296
  }
290
297
  }
291
298
 
@@ -256,6 +256,11 @@ export class BedrockBackend implements ModelBackend {
256
256
  * Send a command with caller-supplied AbortSignal + optional per-call
257
257
  * timeout composed via `AbortSignal.any`. The SDK accepts `abortSignal`
258
258
  * in the request options bag.
259
+ *
260
+ * SDK `ServiceException`s are re-thrown as `BedrockBackendError` carrying the
261
+ * upstream HTTP status (`$metadata.httpStatusCode`) so the sanitized `@embed`
262
+ * error is diagnosable, matching the openai/anthropic/ollama backends (#1593).
263
+ * Aborts/timeouts are caller-driven, not upstream failures — propagated untouched.
259
264
  */
260
265
  async #sendWithAbort(
261
266
  client: { send: (cmd: object, options?: { abortSignal?: AbortSignal }) => Promise<unknown> },
@@ -263,7 +268,21 @@ export class BedrockBackend implements ModelBackend {
263
268
  callerSignal?: AbortSignal
264
269
  ): Promise<unknown> {
265
270
  const abortSignal = composeSignal(callerSignal, this.#requestTimeoutMs);
266
- return client.send(command, abortSignal ? { abortSignal } : undefined);
271
+ try {
272
+ return await client.send(command, abortSignal ? { abortSignal } : undefined);
273
+ } catch (err) {
274
+ if ((err as any)?.name === 'AbortError' || abortSignal?.aborted) throw err;
275
+ const status = (err as any)?.$metadata?.httpStatusCode;
276
+ const sdkName = (err as any)?.name;
277
+ const wrapped = new BedrockBackendError(
278
+ `Bedrock request failed${sdkName ? ` (${sdkName})` : ''}`,
279
+ typeof status === 'number' ? status : undefined
280
+ );
281
+ // Keep the raw SDK error for the unsanitized server log; only class name +
282
+ // upstreamStatus surface to callers.
283
+ (wrapped as any).cause = err;
284
+ throw wrapped;
285
+ }
267
286
  }
268
287
  }
269
288
 
@@ -284,9 +303,13 @@ export function registerBedrockBackend(args: {
284
303
  }
285
304
 
286
305
  export class BedrockBackendError extends ServerError {
287
- constructor(message: string) {
306
+ /** HTTP status returned by the upstream provider, when the failure came from an HTTP response.
307
+ * Distinct from ServerError's statusCode, which is Harper's own response status (#1593). */
308
+ declare upstreamStatus?: number;
309
+ constructor(message: string, upstreamStatus?: number) {
288
310
  super(message);
289
311
  this.name = 'BedrockBackendError';
312
+ if (upstreamStatus !== undefined) this.upstreamStatus = upstreamStatus;
290
313
  }
291
314
  }
292
315
 
@@ -23,7 +23,7 @@ import * as login from '../resources/login.ts';
23
23
  import * as REST from '../server/REST.ts';
24
24
  import * as staticFiles from '../server/static.ts';
25
25
  import * as loadEnv from '../resources/loadEnv.ts';
26
- import harperLogger from '../utility/logging/harper_logger.ts';
26
+ import harperLogger, { errorForLog } from '../utility/logging/harper_logger.ts';
27
27
  import * as dataLoader from '../resources/dataLoader.ts';
28
28
  import { restartWorkers, getWorkerIndex } from '../server/threads/manageThreads.js';
29
29
  import { resetRestartNeeded, subscribeToRestartRequests } from './requestRestart.ts';
@@ -46,6 +46,7 @@ import * as mcpComponent from './mcp/index.ts';
46
46
  import { Status } from '../server/status/index.ts';
47
47
  import { lifecycle as componentLifecycle } from './status/index.ts';
48
48
  import { DEFAULT_CONFIG } from './DEFAULT_CONFIG.ts';
49
+ import { materializeGlobalSecrets, processComponentEnv } from './componentSecrets.ts';
49
50
  import { PluginModule } from './PluginModule.ts';
50
51
  import { getEnvBuiltInComponents } from './Application.ts';
51
52
  import { pathToFileURL } from 'node:url';
@@ -61,9 +62,14 @@ let resources;
61
62
  * @param loadedPluginModules
62
63
  * @param loadedResources
63
64
  */
64
- export function loadComponentDirectories(loadedPluginModules?: Map<any, any>, loadedResources?: Resources) {
65
+ export async function loadComponentDirectories(loadedPluginModules?: Map<any, any>, loadedResources?: Resources) {
65
66
  if (loadedResources) resources = loadedResources;
66
67
  if (loadedPluginModules) loadedComponents = loadedPluginModules;
68
+ // Materialize hdb_secret global-tier rows into process.env and snapshot the scoped tier before
69
+ // any application loads (root components — including the Pro custody registration — have
70
+ // already loaded by this point). Re-runs on each reload cycle, which is how changed/late-custody
71
+ // secrets heal. Never throws.
72
+ await materializeGlobalSecrets();
67
73
  const cfsLoaded: Promise<any>[] = [];
68
74
  if (existsSync(CF_ROUTES_DIR)) {
69
75
  const cfFolders = readdirSync(CF_ROUTES_DIR, { withFileTypes: true });
@@ -303,6 +309,11 @@ export interface LoadComponentOptions {
303
309
  autoReload?: boolean;
304
310
  providedLoadedComponents?: Map<any, any>;
305
311
  appName?: string;
312
+ // When provided, every Scope created during this load is added to this set instead of being
313
+ // auto-closed on worker shutdown. The caller then owns closing them. Used by transient loads
314
+ // (e.g. the deploy pre-flight validation) so their deploy-lifecycle listeners don't accumulate
315
+ // across deploys (#1462).
316
+ collectScopes?: Set<Scope>;
306
317
  }
307
318
 
308
319
  /**
@@ -366,6 +377,34 @@ export async function loadComponent(
366
377
  // per-component iteration below; built-in entries register synchronously.
367
378
  if (isRoot) await bootstrapModels(config);
368
379
 
380
+ // The `env:` block declares the component's environment expectations (string literal →
381
+ // process.env; object → declaration satisfied from the hdb_secret store / process.env).
382
+ // Processed before any of the component's plugins load, so literals and the load-gate apply
383
+ // to everything below. A failed gate contains to this component — nothing of this
384
+ // component's is registered (its URL space is simply absent) and the instance keeps running.
385
+ if (config.env !== undefined) {
386
+ if (isRoot) {
387
+ harperLogger.warn(
388
+ `The 'env' config block is not supported in the root config; declare env expectations in each component's config`
389
+ );
390
+ } else {
391
+ const componentStatusName = basename(componentDirectory);
392
+ try {
393
+ // Refresh the store snapshot so out-of-cycle loads (e.g. deploy validation in a
394
+ // long-lived worker, after a set_secret/grant_secret since boot) gate against
395
+ // current data. Cheap (one small system-table scan per env-declaring component).
396
+ await materializeGlobalSecrets();
397
+ processComponentEnv(componentStatusName, config.env);
398
+ } catch (error) {
399
+ error.message = `Could not load component '${componentStatusName}' due to: ${error.message}`;
400
+ errorReporter?.(error);
401
+ (getWorkerIndex() === 0 ? console : harperLogger).error(error);
402
+ componentLifecycle.failed(componentStatusName, error, `Could not load component '${componentStatusName}'`);
403
+ return undefined;
404
+ }
405
+ }
406
+ }
407
+
369
408
  if (!isRoot) {
370
409
  try {
371
410
  await symlinkHarperModule(componentDirectory);
@@ -383,6 +422,7 @@ export async function loadComponent(
383
422
  const componentFunctionality = {};
384
423
  // iterate through the app handlers so they can each do their own loading process
385
424
  for (const componentName in config) {
425
+ if (componentName === 'env') continue; // handled above — not a plugin
386
426
  // For root components, use just the component name
387
427
  // For application components, use applicationName.componentName format (directoryName.componentName)
388
428
  const componentStatusName = isRoot ? componentName : `${basename(componentDirectory)}.${componentName}`;
@@ -442,6 +482,7 @@ export async function loadComponent(
442
482
  applicationScope: subApplicationScope,
443
483
  autoReload: false,
444
484
  appName: appName || componentName,
485
+ collectScopes: options.collectScopes,
445
486
  });
446
487
  componentFunctionality[componentName] = true;
447
488
  }
@@ -510,9 +551,17 @@ export async function loadComponent(
510
551
  origin
511
552
  );
512
553
 
513
- // Track the close so the worker's shutdown path waits for it (and thus for any async
514
- // native-runtime disposal, e.g. @harperfast/vite's dev server) before calling realExit.
515
- onMessageByType(ITC_EVENT_TYPES.SHUTDOWN, () => trackScopeClose(scope.close()));
554
+ if (options.collectScopes) {
555
+ // A transient/validation load owns these scopes and closes them itself once the
556
+ // load is validated (see operations.js deploy pre-flight). Skip the worker-shutdown
557
+ // auto-close so their deploy-lifecycle listeners — and this SHUTDOWN handler — don't
558
+ // accumulate across deploys (#1462).
559
+ options.collectScopes.add(scope);
560
+ } else {
561
+ // Track the close so the worker's shutdown path waits for it (and thus for any async
562
+ // native-runtime disposal, e.g. @harperfast/vite's dev server) before calling realExit.
563
+ onMessageByType(ITC_EVENT_TYPES.SHUTDOWN, () => trackScopeClose(scope.close()));
564
+ }
516
565
 
517
566
  await sequentiallyHandleApplication(scope, extensionModule);
518
567
 
@@ -607,7 +656,7 @@ export async function loadComponent(
607
656
  error.message
608
657
  }`;
609
658
  errorReporter?.(error);
610
- (getWorkerIndex() === 0 ? console : harperLogger).error(error);
659
+ (getWorkerIndex() === 0 ? console : harperLogger).error(errorForLog(error));
611
660
  resources.set(componentConfig.path || '/', new ErrorResource(error), null, true);
612
661
  componentLifecycle.failed(componentStatusName, error, `Could not load component '${componentStatusName}'`);
613
662
  }
@@ -683,7 +732,7 @@ export async function loadComponent(
683
732
  );
684
733
  }
685
734
  } catch (error) {
686
- console.error(`Could not load application directory ${componentDirectory}`, error);
735
+ console.error(`Could not load application directory ${componentDirectory}`, errorForLog(error));
687
736
  error.message = `Could not load application due to ${error.message}`;
688
737
  errorReporter?.(error);
689
738
  resources.set('', new ErrorResource(error));