@harperfast/harper 5.2.0 → 5.2.2

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 (308) hide show
  1. package/bin/copyDb.ts +40 -11
  2. package/bin/harper.ts +20 -51
  3. package/bin/help.ts +216 -0
  4. package/components/Application.ts +236 -46
  5. package/components/ApplicationScope.ts +26 -0
  6. package/components/EntryHandler.ts +410 -105
  7. package/components/RuntimeModuleTracker.ts +189 -0
  8. package/components/Scope.ts +68 -24
  9. package/components/componentLoader.ts +70 -16
  10. package/components/deployLifecycle.ts +119 -33
  11. package/components/deploymentRecorder.ts +102 -11
  12. package/components/mcp/tools/operations.ts +6 -0
  13. package/components/operations.js +8 -6
  14. package/config/configUtils.ts +78 -20
  15. package/config/harperConfigEnvVars.ts +114 -17
  16. package/config-root.schema.json +10 -0
  17. package/dataLayer/harperBridge/ResourceBridge.ts +26 -5
  18. package/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +4 -4
  19. package/dataLayer/hdbInfoController.ts +8 -0
  20. package/dataLayer/schemaDescribe.ts +2 -1
  21. package/dist/bin/copyDb.js +33 -9
  22. package/dist/bin/copyDb.js.map +1 -1
  23. package/dist/bin/harper.d.ts +6 -0
  24. package/dist/bin/harper.js +18 -50
  25. package/dist/bin/harper.js.map +1 -1
  26. package/dist/bin/help.d.ts +8 -0
  27. package/dist/bin/help.js +192 -0
  28. package/dist/bin/help.js.map +1 -0
  29. package/dist/components/Application.d.ts +16 -1
  30. package/dist/components/Application.js +210 -38
  31. package/dist/components/Application.js.map +1 -1
  32. package/dist/components/ApplicationScope.d.ts +7 -0
  33. package/dist/components/ApplicationScope.js +22 -0
  34. package/dist/components/ApplicationScope.js.map +1 -1
  35. package/dist/components/EntryHandler.d.ts +4 -4
  36. package/dist/components/EntryHandler.js +386 -95
  37. package/dist/components/EntryHandler.js.map +1 -1
  38. package/dist/components/RuntimeModuleTracker.d.ts +11 -0
  39. package/dist/components/RuntimeModuleTracker.js +189 -0
  40. package/dist/components/RuntimeModuleTracker.js.map +1 -0
  41. package/dist/components/Scope.d.ts +1 -0
  42. package/dist/components/Scope.js +69 -22
  43. package/dist/components/Scope.js.map +1 -1
  44. package/dist/components/componentLoader.js +69 -10
  45. package/dist/components/componentLoader.js.map +1 -1
  46. package/dist/components/deployLifecycle.d.ts +6 -2
  47. package/dist/components/deployLifecycle.js +109 -31
  48. package/dist/components/deployLifecycle.js.map +1 -1
  49. package/dist/components/deploymentRecorder.d.ts +6 -0
  50. package/dist/components/deploymentRecorder.js +94 -12
  51. package/dist/components/deploymentRecorder.js.map +1 -1
  52. package/dist/components/mcp/tools/operations.js +6 -0
  53. package/dist/components/mcp/tools/operations.js.map +1 -1
  54. package/dist/components/operations.js +8 -6
  55. package/dist/components/operations.js.map +1 -1
  56. package/dist/config/configUtils.d.ts +6 -0
  57. package/dist/config/configUtils.js +78 -18
  58. package/dist/config/configUtils.js.map +1 -1
  59. package/dist/config/harperConfigEnvVars.js +108 -16
  60. package/dist/config/harperConfigEnvVars.js.map +1 -1
  61. package/dist/dataLayer/harperBridge/ResourceBridge.js +12 -5
  62. package/dist/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
  63. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +4 -4
  64. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js.map +1 -1
  65. package/dist/dataLayer/hdbInfoController.js +4 -0
  66. package/dist/dataLayer/hdbInfoController.js.map +1 -1
  67. package/dist/dataLayer/schemaDescribe.js +2 -1
  68. package/dist/dataLayer/schemaDescribe.js.map +1 -1
  69. package/dist/resources/DatabaseTransaction.d.ts +57 -0
  70. package/dist/resources/DatabaseTransaction.js +284 -104
  71. package/dist/resources/DatabaseTransaction.js.map +1 -1
  72. package/dist/resources/ResourceInterface.d.ts +8 -2
  73. package/dist/resources/ResourceInterface.js.map +1 -1
  74. package/dist/resources/Resources.js +22 -4
  75. package/dist/resources/Resources.js.map +1 -1
  76. package/dist/resources/Table.d.ts +8 -6
  77. package/dist/resources/Table.js +158 -31
  78. package/dist/resources/Table.js.map +1 -1
  79. package/dist/resources/analytics/write.js +6 -6
  80. package/dist/resources/analytics/write.js.map +1 -1
  81. package/dist/resources/auditStore.js +27 -1
  82. package/dist/resources/auditStore.js.map +1 -1
  83. package/dist/resources/blob.d.ts +0 -1
  84. package/dist/resources/blob.js +15 -7
  85. package/dist/resources/blob.js.map +1 -1
  86. package/dist/resources/databases.d.ts +22 -5
  87. package/dist/resources/databases.js +118 -9
  88. package/dist/resources/databases.js.map +1 -1
  89. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +15 -2
  90. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +128 -39
  91. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
  92. package/dist/resources/jsResource.d.ts +4 -26
  93. package/dist/resources/jsResource.js +5 -59
  94. package/dist/resources/jsResource.js.map +1 -1
  95. package/dist/resources/models/Models.d.ts +11 -1
  96. package/dist/resources/models/Models.js +10 -1
  97. package/dist/resources/models/Models.js.map +1 -1
  98. package/dist/resources/models/backendRegistry.d.ts +9 -0
  99. package/dist/resources/models/backendRegistry.js +10 -0
  100. package/dist/resources/models/backendRegistry.js.map +1 -1
  101. package/dist/resources/models/openaiStream.d.ts +16 -1
  102. package/dist/resources/models/openaiStream.js +113 -21
  103. package/dist/resources/models/openaiStream.js.map +1 -1
  104. package/dist/resources/models/v1/chatCompletions.d.ts +54 -0
  105. package/dist/resources/models/v1/chatCompletions.js +115 -0
  106. package/dist/resources/models/v1/chatCompletions.js.map +1 -0
  107. package/dist/resources/models/v1/embeddings.d.ts +11 -0
  108. package/dist/resources/models/v1/embeddings.js +71 -0
  109. package/dist/resources/models/v1/embeddings.js.map +1 -0
  110. package/dist/resources/models/v1/errors.d.ts +54 -0
  111. package/dist/resources/models/v1/errors.js +130 -0
  112. package/dist/resources/models/v1/errors.js.map +1 -0
  113. package/dist/resources/models/v1/index.d.ts +36 -0
  114. package/dist/resources/models/v1/index.js +75 -0
  115. package/dist/resources/models/v1/index.js.map +1 -0
  116. package/dist/resources/models/v1/models.d.ts +26 -0
  117. package/dist/resources/models/v1/models.js +44 -0
  118. package/dist/resources/models/v1/models.js.map +1 -0
  119. package/dist/resources/models/v1/translation.d.ts +133 -0
  120. package/dist/resources/models/v1/translation.js +298 -0
  121. package/dist/resources/models/v1/translation.js.map +1 -0
  122. package/dist/resources/roles.d.ts +1 -1
  123. package/dist/resources/roles.js +54 -7
  124. package/dist/resources/roles.js.map +1 -1
  125. package/dist/resources/search.d.ts +1 -1
  126. package/dist/resources/search.js +7 -3
  127. package/dist/resources/search.js.map +1 -1
  128. package/dist/security/jsLoader.js +84 -33
  129. package/dist/security/jsLoader.js.map +1 -1
  130. package/dist/security/role.js +4 -0
  131. package/dist/security/role.js.map +1 -1
  132. package/dist/security/superUserGuard.d.ts +7 -0
  133. package/dist/security/superUserGuard.js +23 -0
  134. package/dist/security/superUserGuard.js.map +1 -0
  135. package/dist/security/tokenAuthentication.d.ts +0 -1
  136. package/dist/security/tokenAuthentication.js +6 -2
  137. package/dist/security/tokenAuthentication.js.map +1 -1
  138. package/dist/security/user.d.ts +6 -1
  139. package/dist/security/user.js +23 -1
  140. package/dist/security/user.js.map +1 -1
  141. package/dist/server/http.d.ts +23 -2
  142. package/dist/server/http.js +99 -12
  143. package/dist/server/http.js.map +1 -1
  144. package/dist/server/operationsServer.d.ts +0 -2
  145. package/dist/server/operationsServer.js.map +1 -1
  146. package/dist/server/serverHelpers/multipartParser.js +9 -0
  147. package/dist/server/serverHelpers/multipartParser.js.map +1 -1
  148. package/dist/server/serverHelpers/operationAuthorizationState.d.ts +2 -0
  149. package/dist/server/serverHelpers/operationAuthorizationState.js +13 -0
  150. package/dist/server/serverHelpers/operationAuthorizationState.js.map +1 -0
  151. package/dist/server/serverHelpers/registeredOperations.d.ts +3 -2
  152. package/dist/server/serverHelpers/registeredOperations.js +14 -13
  153. package/dist/server/serverHelpers/registeredOperations.js.map +1 -1
  154. package/dist/server/serverHelpers/serverHandlers.js +19 -3
  155. package/dist/server/serverHelpers/serverHandlers.js.map +1 -1
  156. package/dist/server/serverHelpers/serverUtilities.d.ts +1 -1
  157. package/dist/server/serverHelpers/serverUtilities.js +10 -7
  158. package/dist/server/serverHelpers/serverUtilities.js.map +1 -1
  159. package/dist/server/static.js +57 -18
  160. package/dist/server/static.js.map +1 -1
  161. package/dist/server/storageReclamation.d.ts +17 -0
  162. package/dist/server/storageReclamation.js +90 -6
  163. package/dist/server/storageReclamation.js.map +1 -1
  164. package/dist/server/threads/manageThreads.js +8 -0
  165. package/dist/server/threads/manageThreads.js.map +1 -1
  166. package/dist/server/threads/socketRouter.js +20 -0
  167. package/dist/server/threads/socketRouter.js.map +1 -1
  168. package/dist/server/threads/threadServer.js +12 -0
  169. package/dist/server/threads/threadServer.js.map +1 -1
  170. package/dist/sqlEngine/diff/differential.js +7 -3
  171. package/dist/sqlEngine/diff/differential.js.map +1 -1
  172. package/dist/sqlEngine/parser/normalizer.js +37 -9
  173. package/dist/sqlEngine/parser/normalizer.js.map +1 -1
  174. package/dist/sqlTranslator/index.js +6 -1
  175. package/dist/sqlTranslator/index.js.map +1 -1
  176. package/dist/upgrade/upgradePrompt.d.ts +2 -2
  177. package/dist/upgrade/upgradePrompt.js +22 -3
  178. package/dist/upgrade/upgradePrompt.js.map +1 -1
  179. package/dist/utility/environment/environmentManager.d.ts +11 -0
  180. package/dist/utility/environment/environmentManager.js +154 -0
  181. package/dist/utility/environment/environmentManager.js.map +1 -1
  182. package/dist/utility/errors/commonErrors.d.ts +1 -0
  183. package/dist/utility/errors/commonErrors.js +1 -0
  184. package/dist/utility/errors/commonErrors.js.map +1 -1
  185. package/dist/utility/hdbTerms.d.ts +2 -0
  186. package/dist/utility/hdbTerms.js +2 -0
  187. package/dist/utility/hdbTerms.js.map +1 -1
  188. package/dist/utility/install/installer.js +32 -0
  189. package/dist/utility/install/installer.js.map +1 -1
  190. package/dist/utility/lmdb/environmentUtility.js +2 -1
  191. package/dist/utility/lmdb/environmentUtility.js.map +1 -1
  192. package/dist/utility/logging/harper_logger.d.ts +7 -0
  193. package/dist/utility/logging/harper_logger.js +57 -24
  194. package/dist/utility/logging/harper_logger.js.map +1 -1
  195. package/npm-shrinkwrap.json +208 -198
  196. package/package.json +10 -8
  197. package/resources/DESIGN.md +1 -0
  198. package/resources/DatabaseTransaction.ts +313 -102
  199. package/resources/ResourceInterface.ts +8 -2
  200. package/resources/Resources.ts +22 -4
  201. package/resources/Table.ts +398 -259
  202. package/resources/analytics/write.ts +22 -20
  203. package/resources/auditStore.ts +32 -1
  204. package/resources/blob.ts +15 -8
  205. package/resources/databases.ts +123 -12
  206. package/resources/indexes/HierarchicalNavigableSmallWorld.ts +119 -45
  207. package/resources/jsResource.ts +5 -62
  208. package/resources/models/Models.ts +14 -1
  209. package/resources/models/backendRegistry.ts +10 -0
  210. package/resources/models/openaiStream.ts +131 -19
  211. package/resources/models/v1/chatCompletions.ts +128 -0
  212. package/resources/models/v1/embeddings.ts +70 -0
  213. package/resources/models/v1/errors.ts +141 -0
  214. package/resources/models/v1/index.ts +72 -0
  215. package/resources/models/v1/models.ts +53 -0
  216. package/resources/models/v1/translation.ts +362 -0
  217. package/resources/roles.ts +67 -7
  218. package/resources/search.ts +8 -3
  219. package/security/jsLoader.ts +84 -30
  220. package/security/role.ts +7 -0
  221. package/security/superUserGuard.ts +20 -0
  222. package/security/tokenAuthentication.ts +6 -3
  223. package/security/user.ts +26 -1
  224. package/server/DESIGN.md +45 -34
  225. package/server/http.ts +100 -13
  226. package/server/operationsServer.ts +0 -2
  227. package/server/serverHelpers/multipartParser.ts +9 -0
  228. package/server/serverHelpers/operationAuthorizationState.ts +11 -0
  229. package/server/serverHelpers/registeredOperations.ts +19 -15
  230. package/server/serverHelpers/serverHandlers.js +20 -3
  231. package/server/serverHelpers/serverUtilities.ts +10 -7
  232. package/server/static.ts +75 -20
  233. package/server/storageReclamation.ts +104 -8
  234. package/server/threads/manageThreads.js +8 -0
  235. package/server/threads/socketRouter.ts +20 -0
  236. package/server/threads/threadServer.js +11 -0
  237. package/sqlTranslator/index.ts +6 -1
  238. package/static/defaultConfig.yaml +2 -0
  239. package/studio/web/assets/{Chat-DoVWScmq.js → Chat-BMUjPklt.js} +5 -5
  240. package/studio/web/assets/{Chat-DoVWScmq.js.map → Chat-BMUjPklt.js.map} +1 -1
  241. package/studio/web/assets/{FloatingChat-UZ2NsUOZ.js → FloatingChat-Cbghuxe5.js} +5 -5
  242. package/studio/web/assets/{FloatingChat-UZ2NsUOZ.js.map → FloatingChat-Cbghuxe5.js.map} +1 -1
  243. package/studio/web/assets/{apiToken-BUI_04o7.js → apiToken-CkgTQAqc.js} +2 -2
  244. package/studio/web/assets/{apiToken-BUI_04o7.js.map → apiToken-CkgTQAqc.js.map} +1 -1
  245. package/studio/web/assets/{applications-D03NA7wW.js → applications-WvF4NOTN.js} +13 -13
  246. package/studio/web/assets/applications-WvF4NOTN.js.map +1 -0
  247. package/studio/web/assets/chevron-right-anDJCGlp.js +2 -0
  248. package/studio/web/assets/chevron-right-anDJCGlp.js.map +1 -0
  249. package/studio/web/assets/index-CDeqVx71.css +1 -0
  250. package/studio/web/assets/index-Sevt3MVx.js +819 -0
  251. package/studio/web/assets/index-Sevt3MVx.js.map +1 -0
  252. package/studio/web/assets/index.lazy-Dg5YYPGg.js +14 -0
  253. package/studio/web/assets/{index.lazy-Dx3MpyDC.js.map → index.lazy-Dg5YYPGg.js.map} +1 -1
  254. package/studio/web/assets/{jsonMode-Cjj7dtlr.js → jsonMode-CBSEleE7.js} +2 -2
  255. package/studio/web/assets/{jsonMode-Cjj7dtlr.js.map → jsonMode-CBSEleE7.js.map} +1 -1
  256. package/studio/web/assets/languageServices-gxedeeTo.js +2 -0
  257. package/studio/web/assets/languageServices-gxedeeTo.js.map +1 -0
  258. package/studio/web/assets/{notifications-0edoFTsb.js → notifications-Ap84YyQp.js} +2 -2
  259. package/studio/web/assets/{notifications-0edoFTsb.js.map → notifications-Ap84YyQp.js.map} +1 -1
  260. package/studio/web/assets/notifications-vehqyOLM.js +2 -0
  261. package/studio/web/assets/{notifications-CwKhipK7.js.map → notifications-vehqyOLM.js.map} +1 -1
  262. package/studio/web/assets/{pollUnlessForbidden-HHdQZW1N.js → pollUnlessForbidden-BhzefVRn.js} +2 -2
  263. package/studio/web/assets/pollUnlessForbidden-BhzefVRn.js.map +1 -0
  264. package/studio/web/assets/{profile-DUfEPQtx.js → profile-DzDpRLHY.js} +2 -2
  265. package/studio/web/assets/{profile-DUfEPQtx.js.map → profile-DzDpRLHY.js.map} +1 -1
  266. package/studio/web/assets/queryClient-CgtkoiCY.js +2 -0
  267. package/studio/web/assets/queryClient-CgtkoiCY.js.map +1 -0
  268. package/studio/web/assets/regions-DpJ8s7iN.js +2 -0
  269. package/studio/web/assets/regions-DpJ8s7iN.js.map +1 -0
  270. package/studio/web/assets/register-BDy01-q3.js +3 -0
  271. package/studio/web/assets/register-BDy01-q3.js.map +1 -0
  272. package/studio/web/assets/register-CQHRwNP4.js +3 -0
  273. package/studio/web/assets/register-CQHRwNP4.js.map +1 -0
  274. package/studio/web/assets/{setComponentFile-DMPo4UjC.js → setComponentFile-D0aPcI3W.js} +2 -2
  275. package/studio/web/assets/{setComponentFile-DMPo4UjC.js.map → setComponentFile-D0aPcI3W.js.map} +1 -1
  276. package/studio/web/assets/setup-I15FAF9H.js +3 -0
  277. package/studio/web/assets/{setup-B56Oz1_u.js.map → setup-I15FAF9H.js.map} +1 -1
  278. package/studio/web/assets/status-BF1eJshJ.js +62 -0
  279. package/studio/web/assets/status-BF1eJshJ.js.map +1 -0
  280. package/studio/web/assets/{swagger-ui-react-lQrBxfwM.js → swagger-ui-react-DwSPXXhl.js} +2 -2
  281. package/studio/web/assets/{swagger-ui-react-lQrBxfwM.js.map → swagger-ui-react-DwSPXXhl.js.map} +1 -1
  282. package/studio/web/assets/{textarea-DhLMZ3PA.js → table-DpOjNQHt.js} +2 -10
  283. package/studio/web/assets/table-DpOjNQHt.js.map +1 -0
  284. package/studio/web/assets/{tsMode-CrHCRjTK.js → tsMode-Bu51A-VG.js} +2 -2
  285. package/studio/web/assets/{tsMode-CrHCRjTK.js.map → tsMode-Bu51A-VG.js.map} +1 -1
  286. package/studio/web/assets/{useEntityRestURL-DoaBMEvU.js → useEntityRestURL-DrV1zMrN.js} +2 -2
  287. package/studio/web/assets/{useEntityRestURL-DoaBMEvU.js.map → useEntityRestURL-DrV1zMrN.js.map} +1 -1
  288. package/studio/web/index.html +6 -7
  289. package/upgrade/upgradePrompt.ts +22 -3
  290. package/utility/environment/environmentManager.ts +152 -0
  291. package/utility/errors/commonErrors.ts +2 -0
  292. package/utility/hdbTerms.ts +2 -0
  293. package/utility/install/installer.ts +37 -0
  294. package/utility/lmdb/environmentUtility.ts +2 -1
  295. package/utility/logging/harper_logger.ts +57 -26
  296. package/studio/web/assets/applications-D03NA7wW.js.map +0 -1
  297. package/studio/web/assets/index-Bh_CNAHr.js +0 -809
  298. package/studio/web/assets/index-Bh_CNAHr.js.map +0 -1
  299. package/studio/web/assets/index-C8Mv9kHV.css +0 -1
  300. package/studio/web/assets/index.lazy-Dx3MpyDC.js +0 -14
  301. package/studio/web/assets/notifications-CwKhipK7.js +0 -2
  302. package/studio/web/assets/pollUnlessForbidden-HHdQZW1N.js.map +0 -1
  303. package/studio/web/assets/queryClient-CbA8wM7J.js +0 -2
  304. package/studio/web/assets/queryClient-CbA8wM7J.js.map +0 -1
  305. package/studio/web/assets/setup-B56Oz1_u.js +0 -3
  306. package/studio/web/assets/status-BAod7p3o.js +0 -62
  307. package/studio/web/assets/status-BAod7p3o.js.map +0 -1
  308. package/studio/web/assets/textarea-DhLMZ3PA.js.map +0 -1
@@ -9,6 +9,7 @@ import * as commonUtils from '../common_utils.ts';
9
9
  import * as hdbTerms from '../hdbTerms.ts';
10
10
  import * as configUtils from '../../config/configUtils.ts';
11
11
  import { mkdirSync } from 'node:fs';
12
+ import { workerData } from 'node:worker_threads';
12
13
 
13
14
  const INIT_ERR = 'Error initializing environment manager';
14
15
  const BOOT_PROPS_FILE_PATH = 'BOOT_PROPS_FILE_PATH';
@@ -24,6 +25,19 @@ const installPropsToSave = {
24
25
  let installProps: any = {};
25
26
  export { BOOT_PROPS_FILE_PATH };
26
27
 
28
+ // Every param passed to setProperty() on this thread, keyed by its canonical config param so
29
+ // aliases collapse to one last-write-wins entry. Workers inherit this map (getConfigOverrides() and
30
+ // the workerDataProvider registration in server/threads/manageThreads.js) and it is replayed after
31
+ // a forced config reload; `base` is the configured value the override displaced, which is how
32
+ // reapplyAllOverrides() tells a reload that left the param alone from one that is itself the change.
33
+ //
34
+ // setProperty() therefore means operator/harness *intent* only. Code caching a value it derived
35
+ // from config it just read (initializePaths.js, utility/lmdb/environmentUtility.ts) must call
36
+ // configUtils.updateConfigObject() directly, or the derived value outlives what it was derived from
37
+ // and ships to every worker as an override.
38
+ const appliedOverrides = new Map<string, { value: any; base: any }>();
39
+ let inheritedOverridesApplied = false;
40
+
27
41
  /**
28
42
  * The base path of the HDB install is often referenced, but is referenced as a const variable at the top of many
29
43
  * modules. This is a problem during install, as the path may not yet be defined. We offer a function to get the
@@ -62,11 +76,42 @@ export function get(propName: string): any {
62
76
  * Note - this function will NOT update the config file. If you want to update the file
63
77
  * use the updateConfigValue method in configUtils.
64
78
  *
79
+ * The override is also recorded so it propagates to every worker thread spawned from here and
80
+ * survives a forced config reload — except a reload that changes this param on disk, which wins
81
+ * and retires the override (see reapplyAllOverrides()). `value` must be structured-cloneable.
82
+ *
65
83
  * This function should only be used by the installer and unit tests.
66
84
  * @param propName
67
85
  * @param value
68
86
  */
69
87
  export function setProperty(propName: string, value: any) {
88
+ // Snapshot rather than keep the caller's reference: initializePaths.js and environmentUtility.ts
89
+ // cache derived per-database paths by mutating the live `databases` value in place, which is
90
+ // often the very object handed to setProperty. Cloning here also settles cloneability at the
91
+ // caller that can still fix it — manageThreads.js's provider only logs a clone failure and
92
+ // spawns the worker on the on-disk config anyway.
93
+ let snapshot;
94
+ try {
95
+ snapshot = structuredClone(value);
96
+ } catch (err) {
97
+ throw new Error(`Config property '${propName}' cannot be set to a value that is not structured-cloneable`, {
98
+ cause: err,
99
+ });
100
+ }
101
+
102
+ // Key by the canonical param name, not the raw alias passed in: CONFIG_PARAM_MAP is many-to-one
103
+ // (e.g. SERVER_PORT_KEY and OPERATIONSAPI_NETWORK_PORT both canonicalize to
104
+ // 'operationsApi_network_port'), and replay of a stale alias after the canonical one would
105
+ // reintroduce the parent/worker skew this map exists to prevent.
106
+ const canonicalName = (hdbTerms.CONFIG_PARAM_MAP as any)[propName.toLowerCase()] ?? propName;
107
+ const existing = appliedOverrides.get(canonicalName);
108
+ appliedOverrides.set(canonicalName, {
109
+ value: snapshot,
110
+ // Keep the first override's base: everything after it displaced an override, not a
111
+ // configured value, so it says nothing about what the config file held.
112
+ base: existing ? existing.base : snapshotConfigValue(configUtils.getConfigValue(propName)),
113
+ });
114
+
70
115
  if (installPropsToSave[propName]) {
71
116
  installProps[propName] = value;
72
117
  }
@@ -74,6 +119,98 @@ export function setProperty(propName: string, value: any) {
74
119
  configUtils.updateConfigObject(propName, value);
75
120
  }
76
121
 
122
+ /**
123
+ * Every config override applied on this thread via setProperty(), for propagation to worker
124
+ * threads spawned from here (see server/threads/manageThreads.js's 'configOverrides'
125
+ * workerData provider). Returns undefined when nothing has been overridden, so a normal
126
+ * production boot contributes no extra workerData.
127
+ */
128
+ export function getConfigOverrides(): Record<string, any> | undefined {
129
+ if (!appliedOverrides.size) return undefined;
130
+ const overrides = {};
131
+ for (const [propName, { value }] of appliedOverrides) {
132
+ overrides[propName] = value;
133
+ }
134
+ return overrides;
135
+ }
136
+
137
+ /**
138
+ * Replays the parent thread's config overrides (received via workerData) on THIS thread, so a
139
+ * worker's effective config matches its parent's rather than silently falling back to whatever
140
+ * is installed on disk. Applied once per thread, right after the on-disk config is first read —
141
+ * see initSync(). A later forced re-init is handled separately by reapplyAllOverrides(), since by
142
+ * then these inherited values are already folded into appliedOverrides.
143
+ */
144
+ function applyInheritedConfigOverrides() {
145
+ const inherited = (workerData as any)?.configOverrides;
146
+ if (!inherited) return;
147
+ for (const propName of Object.keys(inherited)) {
148
+ setProperty(propName, inherited[propName]);
149
+ }
150
+ }
151
+
152
+ /** Detached copy of a config value; falls back to the value itself for the (unreached) non-cloneable case. */
153
+ function snapshotConfigValue(value: any) {
154
+ try {
155
+ return structuredClone(value);
156
+ } catch {
157
+ return value;
158
+ }
159
+ }
160
+
161
+ /** Key-ordering is a YAML formatting choice, not a config change, so it must not affect equality. */
162
+ function stableStringify(value: any) {
163
+ if (value === null || typeof value !== 'object') return JSON.stringify(value);
164
+ if (Array.isArray(value)) return `[${value.map(stableStringify).join(',')}]`;
165
+ return `{${Object.keys(value)
166
+ .sort()
167
+ .map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`)
168
+ .join(',')}}`;
169
+ }
170
+
171
+ /**
172
+ * A reload rebuilds config values into fresh objects, so identity says nothing about whether the
173
+ * file changed. Anything this cannot serialize (a cyclic value, which structuredClone accepts)
174
+ * counts as changed rather than throwing out of the reload path — initSync()'s catch exits the
175
+ * process.
176
+ */
177
+ function sameConfigValue(a: any, b: any) {
178
+ if (Object.is(a, b)) return true;
179
+ if (a === null || b === null || typeof a !== 'object' || typeof b !== 'object') return false;
180
+ try {
181
+ return stableStringify(a) === stableStringify(b);
182
+ } catch {
183
+ return false;
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Replays this thread's overrides on top of a config that was just re-read from disk. A forced
189
+ * initSync() rebuilds the whole config, which would otherwise revert the thread to the on-disk
190
+ * config regardless of any overrides — the same defect applyInheritedConfigOverrides() closes for a
191
+ * worker's first boot, but for a reload on any thread, main included.
192
+ *
193
+ * An override does NOT survive a reload that changed its own param on disk. A forced reload is how
194
+ * an operator's config edit takes effect (the RESTART handlers in security/keys.ts and
195
+ * processManagement.js), so replaying unconditionally would make editing an overridden param a
196
+ * silent no-op for the life of the process. The edit is the newer intent, so the override retires
197
+ * entirely — dropped from the map, so it stops reaching newly spawned workers as well.
198
+ *
199
+ * Call only after configUtils.initConfig(force) has repopulated the config from disk.
200
+ */
201
+ function reapplyAllOverrides() {
202
+ if (appliedOverrides.size === 0) return;
203
+ // Iterate a snapshot: setProperty() re-inserts into appliedOverrides as we go.
204
+ for (const [propName, { value, base }] of [...appliedOverrides]) {
205
+ if (!sameConfigValue(configUtils.getConfigValue(propName), base)) {
206
+ appliedOverrides.delete(propName);
207
+ log.notify(`Config reload changed '${propName}'; dropping this thread's in-process override of it`);
208
+ continue;
209
+ }
210
+ setProperty(propName, value);
211
+ }
212
+ }
213
+
77
214
  /**
78
215
  * Checks to see if the Harper boot props file exists.
79
216
  * If it does, it grabs the install user and settings path for future reference.
@@ -108,6 +245,21 @@ export function initSync(force: boolean = false) {
108
245
  try {
109
246
  if (propFileExists || doesPropFileExist() || commonUtils.noBootFile() || force) {
110
247
  configUtils.initConfig(force);
248
+ if (!inheritedOverridesApplied) {
249
+ inheritedOverridesApplied = true;
250
+ applyInheritedConfigOverrides();
251
+ }
252
+ // force implies the on-disk config was just re-read from scratch, which would otherwise
253
+ // silently drop every override applied on this thread so far (inherited or local) —
254
+ // except the ones the re-read itself changed, which the config file wins.
255
+ if (force) reapplyAllOverrides();
256
+
257
+ // Sync installProps' HDB_ROOT from the config's rootPath *after* any override replay
258
+ // above (not before): setProperty() records a rootPath override under its canonical
259
+ // CONFIG_PARAM_MAP name, not under HDB_ROOT_KEY, so replaying it doesn't retrigger this
260
+ // same side effect below in setProperty() — this sync is what makes getHdbBasePath()
261
+ // track an isolated rootPath override instead of staying pinned to whatever the pre-replay
262
+ // disk read produced.
111
263
  const configHdbRoot = configUtils.getConfigValue(hdbTerms.HDB_SETTINGS_NAMES.HDB_ROOT_KEY);
112
264
  // Only overwrite if we actually got a value from config
113
265
  if (configHdbRoot !== undefined) {
@@ -200,6 +200,8 @@ const USER_ERROR_MSGS = {
200
200
  ALTER_USER_ROLE_NOT_FOUND: (role) => `Update failed. Requested '${role}' role not found.`,
201
201
  DUP_ROLES_FOUND: (role) =>
202
202
  `Multiple ${role} roles found. Roles must have unique 'role' value. Please update and try again.`,
203
+ LAST_SUPER_USER:
204
+ 'Rejected: this change would leave the instance with no active super_user. Grant super_user to another active user first.',
203
205
  ROLE_NAME_NOT_FOUND: (role) => `${role} role not found`,
204
206
  USER_ALREADY_EXISTS: (user) => `User ${user} already exists`,
205
207
  USER_NOT_EXIST: (user) => `User ${user} does not exist`,
@@ -553,6 +553,7 @@ export const CONFIG_PARAMS = {
553
553
  LICENSE_MODE: 'license_mode',
554
554
  LICENSE_REGION: 'license_region',
555
555
  LOCALSTUDIO_ENABLED: 'localStudio_enabled',
556
+ MODELSGATEWAY_ENABLED: 'modelsGateway_enabled',
556
557
  LOGGING_COLORS: 'logging_colors',
557
558
  LOGGING_CONSOLE: 'logging_console',
558
559
  LOGGING_FILE: 'logging_file',
@@ -693,6 +694,7 @@ export const CONFIG_PARAMS = {
693
694
  STORAGE_TRANSACTIONLOG_COOLINGINTERVAL: 'storage_transactionLog_coolingInterval',
694
695
  STORAGE_ENGINE: 'storage_engine',
695
696
  STORAGE_READONLY: 'storage_readOnly',
697
+ STORAGE_ROCKS_COMPRESSION: 'storage_rocks_compression',
696
698
  STORAGE_ROCKS_BLOCKCACHESIZE: 'storage_rocks_blockCacheSize',
697
699
  STORAGE_ROCKS_WRITEBUFFERMANAGERSIZE: 'storage_rocks_writeBufferManagerSize',
698
700
  STORAGE_ROCKS_WRITEBUFFERMANAGERCOSTTOCACHE: 'storage_rocks_writeBufferManagerCostToCache',
@@ -19,6 +19,7 @@ import * as hdbTerms from '../hdbTerms.ts';
19
19
  const { CONFIG_PARAMS } = hdbTerms;
20
20
  import installValidator from '../../validation/installValidator.ts';
21
21
  import mountHdb from '../mount_hdb.ts';
22
+ import { composeConfigFromEnv } from '../../config/harperConfigEnvVars.ts';
22
23
  import * as configUtils from '../../config/configUtils.ts';
23
24
  import * as userOps from '../../security/user.ts';
24
25
  import * as roleOps from '../../security/role.ts';
@@ -197,6 +198,7 @@ async function install() {
197
198
  }
198
199
  envManager.setHdbBasePath(hdbRoot);
199
200
  envManager.setProperty(hdbTerms.CONFIG_PARAMS.STORAGE_ENGINE, installParams.STORAGE_ENGINE);
201
+ stageRocksCompression();
200
202
 
201
203
  // Creates the Harper project folder structure and the LMDB environments/dbis.
202
204
  await mountHdb(hdbRoot);
@@ -575,6 +577,41 @@ export function applyInstallModeDefaults(args, defaultsMode) {
575
577
  * @param installParams
576
578
  * @returns {Promise<void>}
577
579
  */
580
+ /**
581
+ * Make the deployment codec readable before `mountHdb()` creates the system column families.
582
+ *
583
+ * The config file is not written until `createConfigFile()`, several steps after `mountHdb()`, but
584
+ * RocksDB fixes a column family's codec when the family is created and refuses a later reopen that
585
+ * asks for a different one — and worker threads share one process-wide family registry. Left
586
+ * unstaged, the main thread creates `__dbis__` at the build default while every worker resolves the
587
+ * configured codec, and Harper dies with "The system database failed to load". Staging it in the
588
+ * in-memory config here is the same trick the STORAGE_ENGINE line above uses.
589
+ *
590
+ * Checked in the same precedence `createConfigFile()` itself later resolves the value with, so
591
+ * `storage.compression` also participates when no explicit RocksDB codec is set: its default is
592
+ * true, which resolves to lz4. Stage both values from every source `createConfigFile()` honors so
593
+ * the first open cannot create `__dbis__` with `none` and later workers reopen it with lz4.
594
+ */
595
+ function stageRocksCompression() {
596
+ const rocksCompression = hdbTerms.CONFIG_PARAMS.STORAGE_ROCKS_COMPRESSION;
597
+ const compression = hdbTerms.CONFIG_PARAMS.STORAGE_COMPRESSION;
598
+ const envConfig = composeConfigFromEnv();
599
+ const commandLineConfig = assignCMDENVVariables([rocksCompression, compression], true);
600
+ const fileConfig = cfgEnv[hdbTerms.INSTALL_PROMPTS.HDB_CONFIG] ? getConfigFromFile() : {};
601
+ const configuredRocksCompression =
602
+ envConfig?.storage?.rocks?.compression ?? commandLineConfig[rocksCompression] ?? fileConfig[rocksCompression];
603
+ const configuredCompression =
604
+ envConfig?.storage?.compression ?? commandLineConfig[compression] ?? fileConfig[compression] ?? true;
605
+ if (
606
+ configuredRocksCompression !== undefined &&
607
+ configuredRocksCompression !== null &&
608
+ configuredRocksCompression !== ''
609
+ ) {
610
+ envManager.setProperty(rocksCompression, configuredRocksCompression);
611
+ }
612
+ envManager.setProperty(compression, configuredCompression);
613
+ }
614
+
578
615
  async function createConfigFile(installParams) {
579
616
  hdbLogger.trace('Creating Harper config file');
580
617
  const args = assignCMDENVVariables(Object.keys(hdbTerms.CONFIG_PARAM_MAP), true);
@@ -13,6 +13,7 @@ import * as lmdbTerms from './terms.ts';
13
13
  import * as hdbTerms from '../hdbTerms.ts';
14
14
  import { resetDatabases } from '../../resources/databases.ts';
15
15
  import * as envMngr from '../environment/environmentManager.ts';
16
+ import { updateConfigObject } from '../../config/configUtils.ts';
16
17
 
17
18
  const INTERNAL_DBIS_NAME = lmdbTerms.INTERNAL_DBIS_NAME;
18
19
  const DBI_DEFINITION_NAME = lmdbTerms.DBI_DEFINITION_NAME;
@@ -109,7 +110,7 @@ export async function createEnvironment(
109
110
 
110
111
  envName = envName.toString();
111
112
  let schemasConfig = envMngr.get(hdbTerms.CONFIG_PARAMS.DATABASES);
112
- if (!schemasConfig) envMngr.setProperty(hdbTerms.CONFIG_PARAMS.DATABASES, (schemasConfig = {}));
113
+ if (!schemasConfig) updateConfigObject(hdbTerms.CONFIG_PARAMS.DATABASES, (schemasConfig = {}));
113
114
  if (!schemasConfig[dbName]) schemasConfig[dbName] = {};
114
115
  schemasConfig[dbName].path = basePath;
115
116
  try {
@@ -381,14 +381,34 @@ module.exports = {
381
381
  inspectForLog,
382
382
  isErrorLike,
383
383
  disableStdio,
384
+ isStdioBrokenError,
384
385
  externalLogger,
385
386
  };
386
387
 
388
+ // A bare no-op would read as permanent backpressure to a pipe()'d source; this drains and
389
+ // discards instead.
390
+ function noopWrite(_chunk?: any, encoding?: any, callback?: any) {
391
+ if (typeof encoding === 'function') callback = encoding;
392
+ if (typeof callback === 'function') callback();
393
+ return true;
394
+ }
395
+
387
396
  /**
388
397
  * We call this if stdio is not functional
389
398
  */
390
399
  export function disableStdio(_unused?: any) {
391
- nativeStdWrite = function () {}; // make this a noop
400
+ nativeStdWrite = noopWrite;
401
+ }
402
+
403
+ const STDIO_BROKEN_ERROR_CODES = new Set(['EPIPE', 'EIO', 'ERR_STREAM_DESTROYED']);
404
+
405
+ /**
406
+ * True when this error means a stdio stream (stdout/stderr) is permanently unwritable - the
407
+ * pipe's reader died (EPIPE), the terminal closed (EIO), or the stream was already destroyed.
408
+ * Callers should disableStdio() on this rather than logging through the same sink that threw it.
409
+ */
410
+ export function isStdioBrokenError(error: any): boolean {
411
+ return STDIO_BROKEN_ERROR_CODES.has(error?.code);
392
412
  }
393
413
 
394
414
  /**
@@ -509,32 +529,42 @@ export function initLogSettings(forceInit = false) {
509
529
  stdioLogging();
510
530
  }
511
531
  let loggingEnabled = true;
532
+
533
+ // A POSIX pipe reports a broken pipe asynchronously - write() returns normally and the EPIPE
534
+ // arrives later as an 'error' event, past the try/catch below. An unhandled 'error' event on any
535
+ // EventEmitter becomes an uncaughtException, so the listener is what actually catches that case.
536
+ function installStdioGuard(stream) {
537
+ if (stream.harperStdioErrorHandler) stream.removeListener('error', stream.harperStdioErrorHandler);
538
+ stream.harperStdioErrorHandler = (error) => {
539
+ if (!isStdioBrokenError(error)) throw error;
540
+ disableStdio();
541
+ };
542
+ stream.on('error', stream.harperStdioErrorHandler);
543
+
544
+ stream.write = function (data) {
545
+ if (
546
+ log_to_file &&
547
+ typeof data === 'string' && // this is how we identify console output vs redirected output from a worker
548
+ loggingEnabled &&
549
+ logConsole
550
+ ) {
551
+ data = data.toString();
552
+ if (data[data.length - 1] === '\n') data = data.slice(0, -1);
553
+ writeToLogFile(data);
554
+ }
555
+ try {
556
+ return nativeStdWrite.apply(stream, arguments);
557
+ } catch (error) {
558
+ if (!isStdioBrokenError(error)) throw error;
559
+ disableStdio();
560
+ return noopWrite.apply(stream, arguments);
561
+ }
562
+ };
563
+ }
564
+
512
565
  function stdioLogging() {
513
- if (log_to_file) {
514
- process.stdout.write = function (data) {
515
- if (
516
- typeof data === 'string' && // this is how we identify console output vs redirected output from a worker
517
- loggingEnabled &&
518
- logConsole
519
- ) {
520
- data = data.toString();
521
- if (data[data.length - 1] === '\n') data = data.slice(0, -1);
522
- writeToLogFile(data);
523
- }
524
- return nativeStdWrite.apply(process.stdout, arguments);
525
- };
526
- process.stderr.write = function (data) {
527
- if (
528
- typeof data === 'string' && // this is how we identify console output vs redirected output from a worker
529
- loggingEnabled &&
530
- logConsole
531
- ) {
532
- if (data[data.length - 1] === '\n') data = data.slice(0, -1);
533
- writeToLogFile(data);
534
- }
535
- return nativeStdWrite.apply(process.stderr, arguments);
536
- };
537
- }
566
+ installStdioGuard(process.stdout);
567
+ installStdioGuard(process.stderr);
538
568
  }
539
569
 
540
570
  export function loggerWithTag(tag: string, conditional?: boolean, logger: any = mainLogger) {
@@ -1809,6 +1839,7 @@ export default {
1809
1839
  setLogLevel,
1810
1840
  OUTPUTS,
1811
1841
  disableStdio,
1842
+ isStdioBrokenError,
1812
1843
  externalLogger,
1813
1844
  AuthAuditLog,
1814
1845
  errorToString,